1
0
mirror of https://github.com/keeweb/keeweb.git synced 2024-06-28 07:50:55 +02:00
This commit is contained in:
antelle 2021-06-14 07:25:29 +02:00
parent 9dd8ee05e9
commit 763ee2e1f5
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C
6 changed files with 189 additions and 151 deletions

View File

@ -153,6 +153,14 @@ class Workspace extends Model {
}
}
togglePanel(panel: WorkspacePanel) {
if (this.mode === 'panel' && this.panel === panel) {
this.showList();
} else {
this.showPanel(panel);
}
}
showList() {
if (FileManager.hasOpenFiles) {
this.mode = 'list';

View File

@ -1,83 +1,8 @@
import { View } from 'framework/views/view';
import { Events } from 'framework/events';
import { AutoType } from 'auto-type';
import { Scrollable } from 'framework/views/scrollable';
import { AutoTypeHintView } from 'views/auto-type/auto-type-hint-view';
import { IconSelectView } from 'views/icon-select-view';
import template from 'templates/grp.hbs';
class GrpView extends View {
parent = '.app__panel';
template = template;
events = {
'click .grp__icon': 'showIconsSelect',
'click .grp__buttons-trash': 'moveToTrash',
'click .back-button': 'returnToApp',
'input #grp__field-title': 'changeTitle',
'focus #grp__field-auto-type-seq': 'focusAutoTypeSeq',
'input #grp__field-auto-type-seq': 'changeAutoTypeSeq',
'change #grp__check-search': 'setEnableSearching',
'change #grp__check-auto-type': 'setEnableAutoType'
};
render() {
this.removeSubView();
super.render({
title: this.model.title,
icon: this.model.icon || 'folder',
customIcon: this.model.customIcon,
enableSearching: this.model.getEffectiveEnableSearching(),
readonly: this.model.top,
canAutoType: AutoType.enabled,
autoTypeSeq: this.model.autoTypeSeq,
autoTypeEnabled: this.model.getEffectiveEnableAutoType(),
defaultAutoTypeSeq: this.model.getParentEffectiveAutoTypeSeq()
});
if (!this.model.title) {
this.$el.find('#grp__field-title').focus();
}
this.createScroll({
root: this.$el.find('.grp')[0],
scroller: this.$el.find('.scroller')[0],
bar: this.$el.find('.scroller__bar')[0]
});
this.pageResized();
}
removeSubView() {
if (this.views.sub) {
this.views.sub.remove();
delete this.views.sub;
}
}
changeTitle(e) {
const title = $.trim(e.target.value);
if (title) {
if (!this.model.top && title !== this.model.title) {
this.model.setName(title);
}
} else {
if (this.model.isJustCreated) {
this.model.removeWithoutHistory();
Events.emit('edit-group');
}
}
}
changeAutoTypeSeq(e) {
const el = e.target;
const seq = $.trim(el.value);
AutoType.validate(null, seq, (err) => {
$(e.target).toggleClass('input--error', !!err);
if (!err) {
this.model.setAutoTypeSeq(seq);
}
});
}
focusAutoTypeSeq(e) {
if (!this.views.hint) {
this.views.hint = new AutoTypeHintView({ input: e.target });
@ -118,27 +43,4 @@ class GrpView extends View {
}
this.render();
}
moveToTrash() {
this.model.moveToTrash();
Events.emit('select-all');
}
setEnableSearching(e) {
const enabled = e.target.checked;
this.model.setEnableSearching(enabled);
}
setEnableAutoType(e) {
const enabled = e.target.checked;
this.model.setEnableAutoType(enabled);
}
returnToApp() {
Events.emit('edit-group');
}
}
Object.assign(GrpView.prototype, Scrollable);
export { GrpView };

View File

@ -54,9 +54,9 @@ export const AppMenuItem: FunctionComponent<{ item: MenuItem }> = ({ item }) =>
if (item.filter?.type === 'trash') {
askUserAndEmptyTrash();
} else if (item.filter?.type === 'group') {
Workspace.showPanel('group');
Workspace.togglePanel('group');
} else if (item.filter?.type === 'tag') {
Workspace.showPanel('tag');
Workspace.togglePanel('tag');
}
};

View File

@ -1,11 +1,70 @@
import { FunctionComponent, h } from 'preact';
import { GroupPanelView } from 'views/panel/group-panel-view';
import { Workspace } from 'models/workspace';
import { Group } from 'models/group';
import { useModelWatcher } from 'util/ui/hooks';
import { Launcher } from 'comp/launcher';
import { useState } from 'preact/hooks';
export const GroupPanel: FunctionComponent = () => {
const group = Workspace.menu.selectedItem as Group;
useModelWatcher(group);
const [title, setTitle] = useState(group.title ?? '');
const [autoTypeSeq, setAutoTypeSeq] = useState(group.getEffectiveAutoTypeSeq());
const [autoTypeSeqInvalid, setAutoTypeSeqInvalid] = useState(false);
const backClicked = () => Workspace.showList();
const titleChanged = (value: string) => {
setTitle(value);
value = value.trim();
if (value && !group.top && value !== group.title) {
group.setName(value);
}
};
const enableSearchingChanged = () => {
group.setEnableSearching(!group.getEffectiveEnableSearching());
};
const autoTypeEnabledChanged = () => {
group.setEnableAutoType(!group.getEffectiveEnableAutoType());
};
const autoTypeSeqChanged = (value: string) => {
setAutoTypeSeq(value);
value = value.trim();
// TODO: AutoType.validate
setAutoTypeSeqInvalid(false);
group.setAutoTypeSeq(value);
};
const trashClicked = () => {
group.moveToTrash();
Workspace.selectAllAndShowList();
};
return h(GroupPanelView, {
backClicked
title,
readOnly: group.top,
enableSearching: group.getEffectiveEnableSearching(),
icon: group.icon ?? 'folder',
customIcon: group.customIcon,
canAutoType: !!Launcher,
autoTypeEnabled: group.getEffectiveEnableAutoType(),
autoTypeSeq,
autoTypeSeqInvalid,
defaultAutoTypeSeq: group.getParentEffectiveAutoTypeSeq(),
backClicked,
titleChanged,
enableSearchingChanged,
autoTypeEnabledChanged,
autoTypeSeqChanged,
trashClicked
});
};

View File

@ -2,16 +2,134 @@ 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 { classes } from 'util/ui/classes';
export const GroupPanelView: FunctionComponent<{ backClicked: () => void }> = ({ backClicked }) => {
export const GroupPanelView: FunctionComponent<{
title: string;
readOnly: boolean;
enableSearching: boolean;
icon: string;
customIcon?: string;
canAutoType: boolean;
autoTypeEnabled: boolean;
autoTypeSeq: string;
autoTypeSeqInvalid: boolean;
defaultAutoTypeSeq: string;
backClicked: () => void;
titleChanged: (value: string) => void;
enableSearchingChanged: () => void;
autoTypeEnabledChanged: () => void;
autoTypeSeqChanged: (value: string) => void;
trashClicked: () => void;
}> = ({
title,
readOnly,
enableSearching,
icon,
customIcon,
canAutoType,
autoTypeEnabled,
autoTypeSeq,
autoTypeSeqInvalid,
defaultAutoTypeSeq,
backClicked,
titleChanged,
enableSearchingChanged,
autoTypeEnabledChanged,
autoTypeSeqChanged,
trashClicked
}) => {
return (
<div class="grp">
<BackButton onClick={backClicked} />
<Scrollable>
<div class="grp__content">
<h1>{Locale.grpTitle}</h1>
<div class="grp__field">
<label for="grp__field-title">{StringFormat.capFirst(Logger.name)}:</label>
<input
type="text"
class="input-base"
id="grp__field-title"
value={title}
onInput={(e) => titleChanged((e.target as HTMLInputElement).value)}
size={50}
maxLength={1024}
required
readonly={readOnly}
autofocus={!title}
/>
</div>
{readOnly ? null : (
<div>
<input
type="checkbox"
class="input-base"
id="grp__check-search"
checked={enableSearching}
onClick={enableSearchingChanged}
/>
<label for="grp__check-search">{Locale.grpSearch}</label>
</div>
)}
<label>{StringFormat.capFirst(Locale.icon)}:</label>
<div class="grp__icon-wrap">
{customIcon ? (
<img src={customIcon} class="grp__icon grp__icon--image" />
) : (
<i class={`fa fa-${icon} grp__icon`} />
)}
</div>
<div class="grp__icons" />
{canAutoType ? (
<>
{readOnly ? null : (
<div>
<input
type="checkbox"
class="input-base"
id="grp__check-auto-type"
checked={autoTypeEnabled}
onClick={autoTypeEnabledChanged}
/>
<label for="grp__check-auto-type">{Locale.grpAutoType}</label>
</div>
)}
<div class="grp__field">
<label for="grp__field-auto-type-seq">
{Locale.grpAutoTypeSeq}:
</label>
<input
type="text"
class={classes({
'input-base': true,
'input--error': autoTypeSeqInvalid
})}
id="grp__field-auto-type-seq"
value={autoTypeSeq}
onClick={(e) =>
autoTypeSeqChanged((e.target as HTMLInputElement).value)
}
size={50}
maxLength={1024}
placeholder={`${Locale.grpAutoTypeSeqDefault}: ${defaultAutoTypeSeq}`}
/>
</div>
</>
) : null}
</div>
</Scrollable>
{readOnly ? null : (
<div class="grp__buttons">
<i class="grp__buttons-trash fa fa-trash-alt" onClick={trashClicked}>
<kw-tip text={Locale.grpTrash} placement="right" />
</i>
</div>
)}
</div>
);
};

View File

@ -1,49 +0,0 @@
<div class="grp">
<div class="back-button">
{{res 'retToApp'}} <i class="fa fa-arrow-circle-left back-button__post"></i>
</div>
<div class="scroller">
<div class="grp__content">
<h1>{{res 'grpTitle'}}</h1>
<div class="grp__field">
<label for="grp__field-title">{{Res 'name'}}:</label>
<input type="text" class="input-base" id="grp__field-title" value="{{title}}" size="50" maxlength="1024"
required {{#if readonly}}readonly{{/if}} />
</div>
{{#unless readonly}}
<div>
<input type="checkbox" class="input-base" id="grp__check-search" {{#if enableSearching}}checked{{/if}} />
<label for="grp__check-search">{{res 'grpSearch'}}</label>
</div>
{{/unless}}
<label>{{Res 'icon'}}:</label>
<div class="grp__icon-wrap">
{{#if customIcon}}
<img src="{{customIcon}}" class="grp__icon grp__icon--image" />
{{else}}
<i class="fa fa-{{icon}} grp__icon"></i>
{{/if}}
</div>
<div class="grp__icons"></div>
{{#if canAutoType}}
{{#unless readonly}}
<div>
<input type="checkbox" class="input-base" id="grp__check-auto-type" {{#if autoTypeEnabled}}checked{{/if}} />
<label for="grp__check-auto-type">{{res 'grpAutoType'}}</label>
</div>
{{/unless}}
<div class="grp__field">
<label for="grp__field-auto-type-seq">{{res 'grpAutoTypeSeq'}}:</label>
<input type="text" class="input-base" id="grp__field-auto-type-seq" value="{{autoTypeSeq}}"
size="50" maxlength="1024" placeholder="{{res 'grpAutoTypeSeqDefault'}}: {{defaultAutoTypeSeq}}" />
</div>
{{/if}}
</div>
</div>
<div class="scroller__bar-wrapper"><div class="scroller__bar"></div></div>
{{#unless readonly}}
<div class="grp__buttons">
<i class="grp__buttons-trash fa fa-trash-alt" title="{{res 'grpTrash'}}" tip-placement="right"></i>
</div>
{{/unless}}
</div>