@design.estate/dees-catalog 9.6.0 → 9.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_bundle/bundle.js +2957 -2685
- package/dist_bundle/bundle.js.map +1 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/demos/inputs.d.ts +1 -1
- package/dist_ts_web/demos/inputs.js +5 -4
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.js +5 -4
- package/dist_ts_web/elements/00group-form/dees-form/dees-form.d.ts +5 -1
- package/dist_ts_web/elements/00group-form/dees-form/dees-form.js +27 -3
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.d.ts +1 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.js +11 -2
- package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.d.ts +3 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.demo.js +4 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.js +51 -13
- package/dist_ts_web/elements/00group-input/dees-input-base/dees-input-base.d.ts +1 -8
- package/dist_ts_web/elements/00group-input/dees-input-base/dees-input-base.js +7 -2
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/component.d.ts +18 -3
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/component.js +181 -144
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/date-validation.d.ts +13 -0
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/date-validation.js +65 -0
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.js +44 -56
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/styles.js +6 -1
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/template.js +11 -5
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.d.ts +8 -8
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.js +6 -53
- package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.d.ts +62 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.demo.d.ts +1 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.demo.js +3 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.js +224 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/index.d.ts +1 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/index.js +2 -0
- package/dist_ts_web/elements/00group-input/index.d.ts +1 -0
- package/dist_ts_web/elements/00group-input/index.js +2 -1
- package/dist_ts_web/elements/00group-input/text-input-contextmenu.d.ts +29 -0
- package/dist_ts_web/elements/00group-input/text-input-contextmenu.js +29 -0
- package/dist_ts_web/elements/00group-overlay/dees-contextmenu/dees-contextmenu.d.ts +1 -1
- package/dist_ts_web/elements/00group-overlay/dees-contextmenu/dees-contextmenu.js +5 -4
- package/package.json +2 -2
- package/readme.md +78 -4
- package/scripts/check-bdtheme-ratchet.cjs +2 -2
- package/scripts/check-packed-consumer.mjs +48 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/demos/inputs.ts +5 -4
- package/ts_web/elements/00group-dataview/dees-table/dees-table.ts +3 -2
- package/ts_web/elements/00group-form/dees-form/dees-form.ts +25 -2
- package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.ts +4 -0
- package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.demo.ts +3 -0
- package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.ts +37 -11
- package/ts_web/elements/00group-input/dees-input-base/dees-input-base.ts +6 -1
- package/ts_web/elements/00group-input/dees-input-datepicker/component.ts +149 -148
- package/ts_web/elements/00group-input/dees-input-datepicker/date-validation.ts +64 -0
- package/ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.ts +40 -40
- package/ts_web/elements/00group-input/dees-input-datepicker/styles.ts +5 -0
- package/ts_web/elements/00group-input/dees-input-datepicker/template.ts +10 -4
- package/ts_web/elements/00group-input/dees-input-text/dees-input-text.ts +5 -52
- package/ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.demo.ts +2 -0
- package/ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.ts +128 -0
- package/ts_web/elements/00group-input/dees-input-textarea/index.ts +1 -0
- package/ts_web/elements/00group-input/index.ts +1 -0
- package/ts_web/elements/00group-input/text-input-contextmenu.ts +31 -0
- package/ts_web/elements/00group-overlay/dees-contextmenu/dees-contextmenu.ts +3 -3
- package/readme.hints.md +0 -1233
- package/readme.icons.md +0 -1849
- package/readme.info.md +0 -80
- package/readme.plan.md +0 -671
- package/readme.playbook.md +0 -820
- package/readme.theme-migration.md +0 -168
|
@@ -11,18 +11,21 @@ export const renderDatepicker = (component: DeesInputDatepicker): TemplateResult
|
|
|
11
11
|
aria-label=${component.label}
|
|
12
12
|
aria-haspopup="dialog"
|
|
13
13
|
aria-expanded=${component.isOpened ? 'true' : 'false'}
|
|
14
|
+
aria-invalid=${component.validationText ? 'true' : 'false'}
|
|
15
|
+
aria-describedby="date-description date-validation"
|
|
14
16
|
class="date-input field-control ${component.isOpened ? 'open' : ''}"
|
|
15
|
-
.value=${component.formatDate(component.value)}
|
|
17
|
+
.value=${component.draftText ?? component.formatDate(component.value)}
|
|
16
18
|
.placeholder=${component.placeholder}
|
|
17
19
|
?disabled=${component.disabled}
|
|
20
|
+
?required=${component.required}
|
|
18
21
|
@click=${component.toggleCalendar}
|
|
19
22
|
@keydown=${component.handleKeydown}
|
|
20
23
|
@input=${component.handleManualInput}
|
|
21
24
|
@blur=${component.handleInputBlur}
|
|
22
|
-
style="padding-right: ${component.value ? 'calc(var(--dees-input-action-size) + 40px)' : '40px'}"
|
|
25
|
+
style="padding-right: ${component.value || component.draftText ? 'calc(var(--dees-input-action-size) + 40px)' : '40px'}"
|
|
23
26
|
/>
|
|
24
27
|
<div class="icon-container">
|
|
25
|
-
${component.value && !component.disabled ? html`
|
|
28
|
+
${(component.value || component.draftText) && !component.disabled ? html`
|
|
26
29
|
<button type="button" class="clear-button input-action" @click=${component.clearValue} aria-label="Clear date">
|
|
27
30
|
<dees-icon icon="lucide:x" iconSize="14"></dees-icon>
|
|
28
31
|
</button>
|
|
@@ -30,7 +33,10 @@ export const renderDatepicker = (component: DeesInputDatepicker): TemplateResult
|
|
|
30
33
|
<dees-icon class="calendar-icon" icon="lucide:calendar" iconSize="16"></dees-icon>
|
|
31
34
|
</div>
|
|
32
35
|
</div>
|
|
33
|
-
|
|
36
|
+
<div class="field-messages">
|
|
37
|
+
<div id="date-description">${component.renderDescription()}</div>
|
|
38
|
+
<div id="date-validation" class="validationText" role="status">${component.validationText}</div>
|
|
39
|
+
</div>
|
|
34
40
|
</div>
|
|
35
41
|
`;
|
|
36
42
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as colors from '../../00colors.js';
|
|
2
2
|
import { DeesInputBase } from '../dees-input-base/dees-input-base.js';
|
|
3
|
+
import { textInputContextMenuItems } from '../text-input-contextmenu.js';
|
|
3
4
|
import { demoFunc } from './dees-input-text.demo.js';
|
|
4
5
|
|
|
5
6
|
import {
|
|
@@ -408,58 +409,10 @@ export class DeesInputText extends DeesInputBase {
|
|
|
408
409
|
}
|
|
409
410
|
|
|
410
411
|
public getContextMenuItems() {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
name: 'Cut',
|
|
416
|
-
iconName: 'lucide:Scissors',
|
|
417
|
-
shortcut: 'Cmd+X',
|
|
418
|
-
disabled: !hasSelection,
|
|
419
|
-
action: async () => {
|
|
420
|
-
const selected = this.value.substring(input.selectionStart!, input.selectionEnd!);
|
|
421
|
-
await navigator.clipboard.writeText(selected);
|
|
422
|
-
const start = input.selectionStart!;
|
|
423
|
-
this.value = this.value.substring(0, start) + this.value.substring(input.selectionEnd!);
|
|
424
|
-
input.value = this.value;
|
|
425
|
-
input.setSelectionRange(start, start);
|
|
426
|
-
this.changeSubject.next(this);
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
|
-
{
|
|
430
|
-
name: 'Copy',
|
|
431
|
-
iconName: 'lucide:Copy',
|
|
432
|
-
shortcut: 'Cmd+C',
|
|
433
|
-
disabled: !hasSelection,
|
|
434
|
-
action: async () => {
|
|
435
|
-
const selected = this.value.substring(input.selectionStart!, input.selectionEnd!);
|
|
436
|
-
await navigator.clipboard.writeText(selected);
|
|
437
|
-
},
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
name: 'Paste',
|
|
441
|
-
iconName: 'lucide:ClipboardPaste',
|
|
442
|
-
shortcut: 'Cmd+V',
|
|
443
|
-
action: async () => {
|
|
444
|
-
const text = await navigator.clipboard.readText();
|
|
445
|
-
const start = input.selectionStart!;
|
|
446
|
-
const end = input.selectionEnd!;
|
|
447
|
-
this.value = this.value.substring(0, start) + text + this.value.substring(end);
|
|
448
|
-
input.value = this.value;
|
|
449
|
-
input.setSelectionRange(start + text.length, start + text.length);
|
|
450
|
-
this.changeSubject.next(this);
|
|
451
|
-
},
|
|
452
|
-
},
|
|
453
|
-
{ divider: true },
|
|
454
|
-
{
|
|
455
|
-
name: 'Select All',
|
|
456
|
-
iconName: 'lucide:TextCursorInput',
|
|
457
|
-
shortcut: 'Cmd+A',
|
|
458
|
-
action: async () => {
|
|
459
|
-
input.select();
|
|
460
|
-
},
|
|
461
|
-
},
|
|
462
|
-
];
|
|
412
|
+
return textInputContextMenuItems({
|
|
413
|
+
input: this.shadowRoot!.querySelector('input')!, disabled: this.disabled,
|
|
414
|
+
commit: value => { this.value = value; this.changeSubject.next(this); },
|
|
415
|
+
});
|
|
463
416
|
}
|
|
464
417
|
|
|
465
418
|
public getValue(): string {
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { customElement, property, html, css, type TemplateResult } from '@design.estate/dees-element';
|
|
2
|
+
import { DeesInputBase } from '../dees-input-base/dees-input-base.js';
|
|
3
|
+
import { textInputContextMenuItems } from '../text-input-contextmenu.js';
|
|
4
|
+
import { demoFunc } from './dees-input-textarea.demo.js';
|
|
5
|
+
import '../../00group-overlay/dees-contextmenu/dees-contextmenu.js';
|
|
6
|
+
|
|
7
|
+
declare global { interface HTMLElementTagNameMap { 'dees-input-textarea': DeesInputTextarea; } }
|
|
8
|
+
|
|
9
|
+
@customElement('dees-input-textarea')
|
|
10
|
+
export class DeesInputTextarea extends DeesInputBase<DeesInputTextarea> {
|
|
11
|
+
public static demo = demoFunc;
|
|
12
|
+
public static demoGroups = ['Input'];
|
|
13
|
+
public static inputLayout = 'compound' as const;
|
|
14
|
+
|
|
15
|
+
@property({ type: String }) accessor value = '';
|
|
16
|
+
@property({ type: String }) accessor placeholder = '';
|
|
17
|
+
@property({ type: Number }) accessor rows = 3;
|
|
18
|
+
@property({ type: Number }) accessor maxLength = -1;
|
|
19
|
+
@property({ type: String, reflect: true }) accessor validationState: 'valid' | 'warn' | 'invalid' | undefined;
|
|
20
|
+
@property({ type: String }) accessor validationText = '';
|
|
21
|
+
@property({ attribute: false }) accessor validationFunction: ((value: string) => { valid: boolean; message?: string }) | undefined;
|
|
22
|
+
private touched = false;
|
|
23
|
+
private lastValidity: boolean | undefined;
|
|
24
|
+
|
|
25
|
+
public static styles = [...DeesInputBase.baseStyles, css`
|
|
26
|
+
textarea { display: block; width: 100%; height: auto; padding: 10px 12px; resize: vertical; text-align: left; }
|
|
27
|
+
.field-messages { grid-column: 1 / -1; }
|
|
28
|
+
.validationText { font-size: var(--dees-font-footnote-size); line-height: var(--dees-font-footnote-line); color: var(--dees-color-text-error); }
|
|
29
|
+
.validationText:not(:empty) { margin-top: var(--dees-spacing-xs); }
|
|
30
|
+
:host([validationstate="invalid"]) textarea { border-color: var(--dees-color-accent-error); }
|
|
31
|
+
:host([validationstate="warn"]) .validationText { color: var(--dees-color-text-warning); }
|
|
32
|
+
:host([validationstate="valid"]) .validationText { color: var(--dees-color-text-success); }
|
|
33
|
+
`];
|
|
34
|
+
|
|
35
|
+
public render(): TemplateResult {
|
|
36
|
+
return html`<div class="input-wrapper">
|
|
37
|
+
<dees-label .label=${this.label} .infoText=${this.infoText} .required=${this.required} @click=${() => this.focus()}></dees-label>
|
|
38
|
+
<div class="input-container">
|
|
39
|
+
<textarea class="field-control" .value=${this.value} .rows=${Math.max(1, this.rows)}
|
|
40
|
+
placeholder=${this.placeholder} ?disabled=${this.disabled} ?required=${this.required}
|
|
41
|
+
aria-label=${this.label ?? ''} aria-invalid=${this.validationState === 'invalid' ? 'true' : 'false'}
|
|
42
|
+
aria-describedby="field-description field-validation"
|
|
43
|
+
@input=${this.handleInput} @blur=${() => this.validate()}></textarea>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="field-messages">
|
|
46
|
+
<div id="field-description">${this.renderDescription()}</div>
|
|
47
|
+
<div id="field-validation" class="validationText" role="status">${this.validationText}</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public willUpdate(changed: Map<PropertyKey, unknown>): void {
|
|
53
|
+
super.willUpdate(changed);
|
|
54
|
+
if (changed.has('value') && this.value == null) this.value = '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public updated(changed: Map<string, unknown>): void {
|
|
58
|
+
super.updated(changed);
|
|
59
|
+
const input = this.shadowRoot?.querySelector('textarea');
|
|
60
|
+
if (this.maxLength >= 0) input?.setAttribute('maxlength', String(this.maxLength));
|
|
61
|
+
else input?.removeAttribute('maxlength');
|
|
62
|
+
if (['value', 'required', 'maxLength', 'validationFunction', 'disabled'].some(key => changed.has(key))) {
|
|
63
|
+
this.validate(this.touched || Boolean(this.value));
|
|
64
|
+
} else if (changed.has('validationState') || changed.has('validationText')) {
|
|
65
|
+
this.syncValidity();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private ruleMessage(): string {
|
|
70
|
+
if (this.disabled) return '';
|
|
71
|
+
if (this.required && !this.value.length) return 'Enter a value.';
|
|
72
|
+
if (this.maxLength >= 0 && this.value.length > this.maxLength) return `Use ${this.maxLength} characters or fewer.`;
|
|
73
|
+
const result = this.validationFunction?.(this.value);
|
|
74
|
+
return result && !result.valid ? result.message || 'Check this value.' : '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private syncValidity(): void {
|
|
78
|
+
const message = this.disabled ? '' : this.ruleMessage() || (this.validationState === 'invalid' ? this.validationText || 'Check this value.' : '');
|
|
79
|
+
this.shadowRoot?.querySelector('textarea')?.setCustomValidity(message);
|
|
80
|
+
const valid = !message;
|
|
81
|
+
if (valid !== this.lastValidity) {
|
|
82
|
+
this.lastValidity = valid;
|
|
83
|
+
this.dispatchEvent(new CustomEvent('validation-change', { detail: { valid, message }, bubbles: true, composed: true }));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public validate(showMessage = true): boolean {
|
|
88
|
+
if (showMessage) this.touched = true;
|
|
89
|
+
const message = this.ruleMessage();
|
|
90
|
+
this.validationText = showMessage ? message : '';
|
|
91
|
+
this.validationState = showMessage ? message ? 'invalid' : 'valid' : undefined;
|
|
92
|
+
this.syncValidity();
|
|
93
|
+
return !message;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public checkValidity(): boolean {
|
|
97
|
+
return this.disabled || (!this.ruleMessage() && this.validationState !== 'invalid');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public reportValidity(): boolean {
|
|
101
|
+
// Preserve a host-supplied validation message until the value changes.
|
|
102
|
+
const valid = this.validationState === 'invalid' ? this.checkValidity() : this.validate();
|
|
103
|
+
if (!valid) this.focus();
|
|
104
|
+
return valid;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private handleInput = (): void => {
|
|
108
|
+
if (this.disabled) return;
|
|
109
|
+
this.value = this.shadowRoot!.querySelector('textarea')!.value;
|
|
110
|
+
this.validate();
|
|
111
|
+
this.changeSubject.next(this);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
public getContextMenuItems() {
|
|
115
|
+
return textInputContextMenuItems({ input: this.shadowRoot!.querySelector('textarea')!, disabled: this.disabled,
|
|
116
|
+
commit: value => { this.value = value; this.validate(); this.changeSubject.next(this); } });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public getValue(): string { return this.value; }
|
|
120
|
+
public setValue(value: string | null): void {
|
|
121
|
+
this.value = value ?? '';
|
|
122
|
+
this.touched = false;
|
|
123
|
+
this.validate(false);
|
|
124
|
+
}
|
|
125
|
+
public reset(): void { this.setValue(''); }
|
|
126
|
+
public focus(options?: FocusOptions): void { this.shadowRoot?.querySelector('textarea')?.focus(options); }
|
|
127
|
+
public blur(): void { this.shadowRoot?.querySelector('textarea')?.blur(); }
|
|
128
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dees-input-textarea.js';
|
|
@@ -15,6 +15,7 @@ export * from './dees-input-richtext/index.js';
|
|
|
15
15
|
export * from './dees-input-searchselect/index.js';
|
|
16
16
|
export * from './dees-input-tags/index.js';
|
|
17
17
|
export * from './dees-input-text/index.js';
|
|
18
|
+
export * from './dees-input-textarea/index.js';
|
|
18
19
|
export * from './dees-input-toggle/index.js';
|
|
19
20
|
export * from './dees-input-typelist/index.js';
|
|
20
21
|
export * from './dees-input-wysiwyg.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
interface ITextInputContext {
|
|
2
|
+
input: HTMLInputElement | HTMLTextAreaElement;
|
|
3
|
+
disabled: boolean;
|
|
4
|
+
commit: (value: string) => void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** Shared plain-text editing commands for single- and multiline fields. */
|
|
8
|
+
export const textInputContextMenuItems = ({ input, disabled, commit }: ITextInputContext) => {
|
|
9
|
+
const selectedText = () => input.value.substring(input.selectionStart ?? 0, input.selectionEnd ?? 0);
|
|
10
|
+
const replaceSelection = (text: string) => {
|
|
11
|
+
if (disabled || input.disabled) return;
|
|
12
|
+
input.setRangeText(text, input.selectionStart ?? 0, input.selectionEnd ?? 0, 'end');
|
|
13
|
+
commit(input.value);
|
|
14
|
+
};
|
|
15
|
+
return [
|
|
16
|
+
{ name: 'Cut', iconName: 'lucide:Scissors', shortcut: 'Cmd+X', disabled: disabled || !selectedText(), action: async () => {
|
|
17
|
+
if (disabled || input.disabled) return;
|
|
18
|
+
await navigator.clipboard.writeText(selectedText());
|
|
19
|
+
replaceSelection('');
|
|
20
|
+
} },
|
|
21
|
+
{ name: 'Copy', iconName: 'lucide:Copy', shortcut: 'Cmd+C', disabled: !selectedText(), action: async () => {
|
|
22
|
+
await navigator.clipboard.writeText(selectedText());
|
|
23
|
+
} },
|
|
24
|
+
{ name: 'Paste', iconName: 'lucide:ClipboardPaste', shortcut: 'Cmd+V', disabled, action: async () => {
|
|
25
|
+
if (disabled || input.disabled) return;
|
|
26
|
+
replaceSelection(await navigator.clipboard.readText());
|
|
27
|
+
} },
|
|
28
|
+
{ divider: true as const },
|
|
29
|
+
{ name: 'Select All', iconName: 'lucide:TextCursorInput', shortcut: 'Cmd+A', action: () => input.select() },
|
|
30
|
+
];
|
|
31
|
+
};
|
|
@@ -166,9 +166,9 @@ export class DeesContextmenu extends DeesElement {
|
|
|
166
166
|
return !!active && (active === this || !!this.shadowRoot?.contains(active) || !!this.submenu?.ownsFocus(active));
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
public connectedCallback(): Promise<void> {
|
|
170
|
+
if (!this.hasAttribute('tabindex')) this.tabIndex = 0;
|
|
171
|
+
return super.connectedCallback();
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
/**
|