mirror of
https://github.com/keeweb/keeweb.git
synced 2024-06-26 07:39:04 +02:00
grp
This commit is contained in:
parent
b859daf39b
commit
c8ad6c8416
|
@ -68,4 +68,4 @@ export const IconMap = [
|
|||
'dollar-sign',
|
||||
'signature',
|
||||
'mobile'
|
||||
] as const;
|
||||
];
|
||||
|
|
|
@ -2,7 +2,7 @@ import { View } from 'framework/views/view';
|
|||
import { AutoType } from 'auto-type';
|
||||
import { Shortcuts } from 'comp/app/shortcuts';
|
||||
import { Locale } from 'util/locale';
|
||||
import { AutoTypeHintView } from 'views/auto-type/auto-type-hint-view';
|
||||
import { AutoTypeHintView } from 'views/auxiliary/auto-type-hint-view';
|
||||
import template from 'templates/details/details-auto-type.hbs';
|
||||
|
||||
class DetailsAutoTypeView extends View {
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
import { View } from 'framework/views/view';
|
||||
import { AutoTypeHintView } from 'views/auto-type/auto-type-hint-view';
|
||||
import { IconSelectView } from 'views/icon-select-view';
|
||||
|
||||
class GrpView extends View {
|
||||
focusAutoTypeSeq(e) {
|
||||
if (!this.views.hint) {
|
||||
this.views.hint = new AutoTypeHintView({ input: e.target });
|
||||
this.views.hint.render();
|
||||
this.views.hint.on('remove', () => {
|
||||
delete this.views.hint;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
showIconsSelect() {
|
||||
if (this.views.sub) {
|
||||
this.removeSubView();
|
||||
} else {
|
||||
const subView = new IconSelectView(
|
||||
{
|
||||
iconId: this.model.customIconId || this.model.iconId,
|
||||
file: this.model.file
|
||||
},
|
||||
{
|
||||
parent: this.$el.find('.grp__icons')[0]
|
||||
}
|
||||
);
|
||||
this.listenTo(subView, 'select', this.iconSelected);
|
||||
subView.render();
|
||||
this.views.sub = subView;
|
||||
}
|
||||
this.pageResized();
|
||||
}
|
||||
|
||||
iconSelected(sel) {
|
||||
if (sel.custom) {
|
||||
if (sel.id !== this.model.customIconId) {
|
||||
this.model.setCustomIcon(sel.id);
|
||||
}
|
||||
} else if (sel.id !== this.model.iconId) {
|
||||
this.model.setIcon(+sel.id);
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import { View } from 'framework/views/view';
|
||||
import { IconMap } from 'const/icon-map';
|
||||
import { Logger } from 'util/logger';
|
||||
import template from 'templates/icon-select.hbs';
|
||||
|
||||
|
@ -15,23 +14,6 @@ class IconSelectView extends View {
|
|||
'change .icon-select__file-input': 'iconSelected'
|
||||
};
|
||||
|
||||
special = {
|
||||
select: null,
|
||||
download: null
|
||||
};
|
||||
|
||||
render() {
|
||||
const customIcons = this.model.file.getCustomIcons();
|
||||
const hasCustomIcons = Object.keys(customIcons).length > 0;
|
||||
super.render({
|
||||
sel: this.model.iconId,
|
||||
icons: IconMap,
|
||||
canDownloadFavicon: !!this.model.url,
|
||||
customIcons,
|
||||
hasCustomIcons
|
||||
});
|
||||
}
|
||||
|
||||
iconClick(e) {
|
||||
const target = $(e.target).closest('.icon-select__icon');
|
||||
const iconId = target[0].getAttribute('data-val');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { AutoTypeHintView } from 'views/auto-type/auto-type-hint-view';
|
||||
import { AutoTypeHintView } from 'views/auxiliary/auto-type-hint-view';
|
||||
import { Links } from 'const/links';
|
||||
import { Features } from 'util/features';
|
||||
import { useLayoutEffect, useRef, useState } from 'preact/hooks';
|
32
app/scripts/ui/auxiliary/icon-select.ts
Normal file
32
app/scripts/ui/auxiliary/icon-select.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { IconSelectView } from 'views/auxiliary/icon-select-view';
|
||||
import { IconMap } from 'const/icon-map';
|
||||
import { File } from 'models/file';
|
||||
import { FileOpener } from 'util/browser/file-opener';
|
||||
|
||||
export const IconSelect: FunctionComponent<{
|
||||
file: File;
|
||||
icon: number | undefined;
|
||||
customIcon: string | undefined;
|
||||
websiteUrl?: string;
|
||||
|
||||
iconSelected: (iconId: number) => void;
|
||||
customIconSelected: (id: string) => void;
|
||||
}> = ({ file, icon, customIcon, websiteUrl, iconSelected }) => {
|
||||
const selectOtherClicked = () => {
|
||||
FileOpener.open((file) => {
|
||||
console.log('f', file);
|
||||
}, 'image/*');
|
||||
};
|
||||
|
||||
return h(IconSelectView, {
|
||||
icons: IconMap,
|
||||
customIcons: file.getCustomIcons(),
|
||||
selectedIcon: icon,
|
||||
selectedCustomIcon: customIcon,
|
||||
canDownloadFavicon: !!websiteUrl,
|
||||
|
||||
iconSelected,
|
||||
selectOtherClicked
|
||||
});
|
||||
};
|
|
@ -14,6 +14,7 @@ export const GroupPanel: FunctionComponent = () => {
|
|||
const [title, setTitle] = useState(group.title ?? '');
|
||||
const [autoTypeSeq, setAutoTypeSeq] = useState(group.autoTypeSeq ?? '');
|
||||
const [autoTypeSeqInvalid, setAutoTypeSeqInvalid] = useState(false);
|
||||
const [iconSelectVisible, setIconSelectVisible] = useState(false);
|
||||
|
||||
const backClicked = () => Workspace.showList();
|
||||
|
||||
|
@ -48,23 +49,44 @@ export const GroupPanel: FunctionComponent = () => {
|
|||
Workspace.selectAllAndShowList();
|
||||
};
|
||||
|
||||
const iconClicked = () => {
|
||||
setIconSelectVisible(!iconSelectVisible);
|
||||
};
|
||||
|
||||
const iconSelected = (iconId: number) => {
|
||||
group.setIcon(iconId);
|
||||
setIconSelectVisible(false);
|
||||
};
|
||||
|
||||
const customIconSelected = (id: string) => {
|
||||
group.setCustomIcon(id);
|
||||
setIconSelectVisible(false);
|
||||
};
|
||||
|
||||
return h(GroupPanelView, {
|
||||
file: group.file,
|
||||
title,
|
||||
readOnly: group.top,
|
||||
enableSearching: group.getEffectiveEnableSearching(),
|
||||
icon: group.icon ?? 'folder',
|
||||
iconId: group.iconId,
|
||||
customIcon: group.customIcon,
|
||||
customIconId: group.customIconId,
|
||||
canAutoType: !!Launcher,
|
||||
autoTypeEnabled: group.getEffectiveEnableAutoType(),
|
||||
autoTypeSeq,
|
||||
autoTypeSeqInvalid,
|
||||
defaultAutoTypeSeq: group.getParentEffectiveAutoTypeSeq(),
|
||||
iconSelectVisible,
|
||||
|
||||
backClicked,
|
||||
titleChanged,
|
||||
enableSearchingChanged,
|
||||
autoTypeEnabledChanged,
|
||||
autoTypeSeqChanged,
|
||||
trashClicked
|
||||
trashClicked,
|
||||
iconClicked,
|
||||
iconSelected,
|
||||
customIconSelected
|
||||
});
|
||||
};
|
||||
|
|
|
@ -5,12 +5,15 @@ let el: HTMLInputElement | undefined;
|
|||
const logger = new Logger('file-reader');
|
||||
|
||||
export const FileOpener = {
|
||||
open(selected: (file: File) => void): void {
|
||||
open(selected: (file: File) => void, accept?: string): void {
|
||||
el?.remove();
|
||||
|
||||
el = document.createElement('input');
|
||||
el.type = 'file';
|
||||
el.classList.add('hide-by-pos');
|
||||
if (accept) {
|
||||
el.accept = accept;
|
||||
}
|
||||
el.click();
|
||||
el.addEventListener('change', () => {
|
||||
const file = el?.files?.[0];
|
||||
|
|
93
app/scripts/views/auxiliary/icon-select-view.tsx
Normal file
93
app/scripts/views/auxiliary/icon-select-view.tsx
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { classes } from 'util/ui/classes';
|
||||
import { Locale } from 'util/locale';
|
||||
|
||||
export const IconSelectView: FunctionComponent<{
|
||||
icons: string[];
|
||||
customIcons: Map<string, string>;
|
||||
selectedIcon: number | undefined;
|
||||
selectedCustomIcon: string | undefined;
|
||||
canDownloadFavicon: boolean;
|
||||
|
||||
iconSelected: (iconId: number) => void;
|
||||
selectOtherClicked: () => void;
|
||||
}> = ({
|
||||
icons,
|
||||
customIcons,
|
||||
selectedIcon,
|
||||
selectedCustomIcon,
|
||||
canDownloadFavicon,
|
||||
|
||||
iconSelected,
|
||||
selectOtherClicked
|
||||
}) => {
|
||||
const iconClicked = (e: MouseEvent) => {
|
||||
if (!(e.target instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
const iconId = e.target.dataset.iconId as string;
|
||||
if (!iconId) {
|
||||
return;
|
||||
}
|
||||
iconSelected(+iconId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="icon-select">
|
||||
<div class="icon-select__items" onClick={iconClicked}>
|
||||
{icons.map((icon, ix) => (
|
||||
<i
|
||||
key={icon}
|
||||
class={classes({
|
||||
'fa': true,
|
||||
[`fa-${icon}`]: true,
|
||||
'icon-select__icon': true,
|
||||
'icon-select__icon--active': ix === selectedIcon
|
||||
})}
|
||||
data-icon-id={ix}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div class="icon-select__items icon-select__items--actions">
|
||||
<input type="file" class="icon-select__file-input hide-by-pos" accept="image/*" />
|
||||
{canDownloadFavicon ? (
|
||||
<span
|
||||
class="icon-select__icon icon-select__icon-btn icon-select__icon-download"
|
||||
data-val="special"
|
||||
data-special="download"
|
||||
>
|
||||
<i class="fa fa-cloud-download-alt" />
|
||||
<kw-tip text={Locale.iconFavTitle} />
|
||||
</span>
|
||||
) : null}
|
||||
<span
|
||||
class="icon-select__icon icon-select__icon-btn icon-select__icon-select"
|
||||
data-val="special"
|
||||
data-special="select"
|
||||
onClick={selectOtherClicked}
|
||||
>
|
||||
<i class="fa fa-ellipsis-h" />
|
||||
<kw-tip text={Locale.iconSelCustom} />
|
||||
</span>
|
||||
</div>
|
||||
{customIcons.size > 0 ? (
|
||||
<div class="icon-select__items icon-select__items--custom">
|
||||
{[...customIcons.entries()].map(([id, icon]) => (
|
||||
<span
|
||||
key={id}
|
||||
class={classes({
|
||||
'icon-select__icon': true,
|
||||
'icon-select__icon-btn': true,
|
||||
'icon-select__icon-custom': true,
|
||||
'icon-select__icon--active': id === selectedCustomIcon
|
||||
})}
|
||||
data-icon-id={id}
|
||||
>
|
||||
<img src={icon} />
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -2,23 +2,27 @@ import { FunctionComponent } from 'preact';
|
|||
import { BackButton } from 'views/components/back-button';
|
||||
import { Scrollable } from 'views/components/scrollable';
|
||||
import { Locale } from 'util/locale';
|
||||
import { Logger } from 'util/logger';
|
||||
import { StringFormat } from 'util/formatting/string-format';
|
||||
import { AutoTypeHint } from 'ui/auto-type/auto-type-hint';
|
||||
import { AutoTypeHint } from 'ui/auxiliary/auto-type-hint';
|
||||
import { IconSelect } from 'ui/auxiliary/icon-select';
|
||||
import { File } from 'models/file';
|
||||
import { classes } from 'util/ui/classes';
|
||||
import { useRef } from 'preact/hooks';
|
||||
|
||||
export const GroupPanelView: FunctionComponent<{
|
||||
file: File;
|
||||
title: string;
|
||||
readOnly: boolean;
|
||||
enableSearching: boolean;
|
||||
icon: string;
|
||||
iconId?: number;
|
||||
customIcon?: string;
|
||||
customIconId?: string;
|
||||
canAutoType: boolean;
|
||||
autoTypeEnabled: boolean;
|
||||
autoTypeSeq: string;
|
||||
autoTypeSeqInvalid: boolean;
|
||||
defaultAutoTypeSeq: string;
|
||||
iconSelectVisible: boolean;
|
||||
|
||||
backClicked: () => void;
|
||||
titleChanged: (value: string) => void;
|
||||
|
@ -26,24 +30,34 @@ export const GroupPanelView: FunctionComponent<{
|
|||
autoTypeEnabledChanged: () => void;
|
||||
autoTypeSeqChanged: (value: string) => void;
|
||||
trashClicked: () => void;
|
||||
iconClicked: () => void;
|
||||
iconSelected: (iconId: number) => void;
|
||||
customIconSelected: (id: string) => void;
|
||||
}> = ({
|
||||
file,
|
||||
title,
|
||||
readOnly,
|
||||
enableSearching,
|
||||
icon,
|
||||
iconId,
|
||||
customIcon,
|
||||
customIconId,
|
||||
canAutoType,
|
||||
autoTypeEnabled,
|
||||
autoTypeSeq,
|
||||
autoTypeSeqInvalid,
|
||||
defaultAutoTypeSeq,
|
||||
iconSelectVisible,
|
||||
|
||||
backClicked,
|
||||
titleChanged,
|
||||
enableSearchingChanged,
|
||||
autoTypeEnabledChanged,
|
||||
autoTypeSeqChanged,
|
||||
trashClicked
|
||||
trashClicked,
|
||||
iconClicked,
|
||||
iconSelected,
|
||||
customIconSelected
|
||||
}) => {
|
||||
return (
|
||||
<div class="grp">
|
||||
|
@ -52,7 +66,7 @@ export const GroupPanelView: FunctionComponent<{
|
|||
<div class="grp__content">
|
||||
<h1>{Locale.grpTitle}</h1>
|
||||
<div class="grp__field">
|
||||
<label for="grp__field-title">{StringFormat.capFirst(Logger.name)}:</label>
|
||||
<label for="grp__field-title">{StringFormat.capFirst(Locale.name)}:</label>
|
||||
<input
|
||||
type="text"
|
||||
class="input-base"
|
||||
|
@ -81,12 +95,26 @@ export const GroupPanelView: FunctionComponent<{
|
|||
<label>{StringFormat.capFirst(Locale.icon)}:</label>
|
||||
<div class="grp__icon-wrap">
|
||||
{customIcon ? (
|
||||
<img src={customIcon} class="grp__icon grp__icon--image" />
|
||||
<img
|
||||
src={customIcon}
|
||||
class="grp__icon grp__icon--image"
|
||||
onClick={iconClicked}
|
||||
/>
|
||||
) : (
|
||||
<i class={`fa fa-${icon} grp__icon`} />
|
||||
<i class={`fa fa-${icon} grp__icon`} onClick={iconClicked} />
|
||||
)}
|
||||
</div>
|
||||
<div class="grp__icons" />
|
||||
<div class="grp__icons">
|
||||
{iconSelectVisible ? (
|
||||
<IconSelect
|
||||
file={file}
|
||||
icon={iconId}
|
||||
customIcon={customIconId}
|
||||
iconSelected={iconSelected}
|
||||
customIconSelected={customIconSelected}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
{canAutoType ? (
|
||||
<>
|
||||
{readOnly ? null : (
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
<div class="icon-select">
|
||||
<div class="icon-select__items">
|
||||
{{#each icons as |icon ix|}}
|
||||
<i class="fa fa-{{icon}} icon-select__icon {{#ifeq ix ../sel}}icon-select__icon--active{{/ifeq}}" data-val="{{ix}}"></i>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="icon-select__items icon-select__items--actions">
|
||||
<input type="file" class="icon-select__file-input hide-by-pos" accept="image/*" />
|
||||
{{#if canDownloadFavicon}}
|
||||
<span class="icon-select__icon icon-select__icon-btn icon-select__icon-download"
|
||||
data-val="special" data-special="download" title="{{res 'iconFavTitle'}}">
|
||||
<i class="fa fa-cloud-download-alt"></i>
|
||||
</span>
|
||||
{{/if}}
|
||||
<span class="icon-select__icon icon-select__icon-btn icon-select__icon-select"
|
||||
data-val="special" data-special="select" title="{{res 'iconSelCustom'}}">
|
||||
<i class="fa fa-ellipsis-h"></i>
|
||||
</span>
|
||||
</div>
|
||||
{{#if hasCustomIcons}}
|
||||
<div class="icon-select__items icon-select__items--custom">
|
||||
{{#each customIcons as |icon ci|}}
|
||||
<span class="icon-select__icon icon-select__icon-btn icon-select__icon-custom {{#ifeq ci ../sel}}icon-select__icon--active{{/ifeq}}"
|
||||
data-val="{{ci}}">
|
||||
<img src="{{icon}}" />
|
||||
</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user