1
0
mirror of https://github.com/keeweb/keeweb.git synced 2024-06-26 07:39:04 +02:00
This commit is contained in:
antelle 2021-06-17 22:36:51 +02:00
parent b859daf39b
commit c8ad6c8416
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C
12 changed files with 191 additions and 107 deletions

View File

@ -68,4 +68,4 @@ export const IconMap = [
'dollar-sign',
'signature',
'mobile'
] as const;
];

View File

@ -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 {

View File

@ -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();
}
}

View File

@ -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');

View File

@ -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';

View 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
});
};

View File

@ -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
});
};

View File

@ -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];

View 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>
);
};

View File

@ -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 : (

View File

@ -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>