mirror of
https://github.com/keeweb/keeweb.git
synced 2024-06-28 07:50:55 +02:00
group
This commit is contained in:
parent
9dd8ee05e9
commit
763ee2e1f5
|
@ -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';
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue
Block a user