@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.
Files changed (66) hide show
  1. package/dist_bundle/bundle.js +2957 -2685
  2. package/dist_bundle/bundle.js.map +1 -1
  3. package/dist_ts_web/00_commitinfo_data.js +1 -1
  4. package/dist_ts_web/demos/inputs.d.ts +1 -1
  5. package/dist_ts_web/demos/inputs.js +5 -4
  6. package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.js +5 -4
  7. package/dist_ts_web/elements/00group-form/dees-form/dees-form.d.ts +5 -1
  8. package/dist_ts_web/elements/00group-form/dees-form/dees-form.js +27 -3
  9. package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.d.ts +1 -0
  10. package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.js +11 -2
  11. package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.d.ts +3 -0
  12. package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.demo.js +4 -1
  13. package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.js +51 -13
  14. package/dist_ts_web/elements/00group-input/dees-input-base/dees-input-base.d.ts +1 -8
  15. package/dist_ts_web/elements/00group-input/dees-input-base/dees-input-base.js +7 -2
  16. package/dist_ts_web/elements/00group-input/dees-input-datepicker/component.d.ts +18 -3
  17. package/dist_ts_web/elements/00group-input/dees-input-datepicker/component.js +181 -144
  18. package/dist_ts_web/elements/00group-input/dees-input-datepicker/date-validation.d.ts +13 -0
  19. package/dist_ts_web/elements/00group-input/dees-input-datepicker/date-validation.js +65 -0
  20. package/dist_ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.js +44 -56
  21. package/dist_ts_web/elements/00group-input/dees-input-datepicker/styles.js +6 -1
  22. package/dist_ts_web/elements/00group-input/dees-input-datepicker/template.js +11 -5
  23. package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.d.ts +8 -8
  24. package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.js +6 -53
  25. package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.d.ts +62 -0
  26. package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.demo.d.ts +1 -0
  27. package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.demo.js +3 -0
  28. package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.js +224 -0
  29. package/dist_ts_web/elements/00group-input/dees-input-textarea/index.d.ts +1 -0
  30. package/dist_ts_web/elements/00group-input/dees-input-textarea/index.js +2 -0
  31. package/dist_ts_web/elements/00group-input/index.d.ts +1 -0
  32. package/dist_ts_web/elements/00group-input/index.js +2 -1
  33. package/dist_ts_web/elements/00group-input/text-input-contextmenu.d.ts +29 -0
  34. package/dist_ts_web/elements/00group-input/text-input-contextmenu.js +29 -0
  35. package/dist_ts_web/elements/00group-overlay/dees-contextmenu/dees-contextmenu.d.ts +1 -1
  36. package/dist_ts_web/elements/00group-overlay/dees-contextmenu/dees-contextmenu.js +5 -4
  37. package/package.json +2 -2
  38. package/readme.md +78 -4
  39. package/scripts/check-bdtheme-ratchet.cjs +2 -2
  40. package/scripts/check-packed-consumer.mjs +48 -0
  41. package/ts_web/00_commitinfo_data.ts +1 -1
  42. package/ts_web/demos/inputs.ts +5 -4
  43. package/ts_web/elements/00group-dataview/dees-table/dees-table.ts +3 -2
  44. package/ts_web/elements/00group-form/dees-form/dees-form.ts +25 -2
  45. package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.ts +4 -0
  46. package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.demo.ts +3 -0
  47. package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.ts +37 -11
  48. package/ts_web/elements/00group-input/dees-input-base/dees-input-base.ts +6 -1
  49. package/ts_web/elements/00group-input/dees-input-datepicker/component.ts +149 -148
  50. package/ts_web/elements/00group-input/dees-input-datepicker/date-validation.ts +64 -0
  51. package/ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.ts +40 -40
  52. package/ts_web/elements/00group-input/dees-input-datepicker/styles.ts +5 -0
  53. package/ts_web/elements/00group-input/dees-input-datepicker/template.ts +10 -4
  54. package/ts_web/elements/00group-input/dees-input-text/dees-input-text.ts +5 -52
  55. package/ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.demo.ts +2 -0
  56. package/ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.ts +128 -0
  57. package/ts_web/elements/00group-input/dees-input-textarea/index.ts +1 -0
  58. package/ts_web/elements/00group-input/index.ts +1 -0
  59. package/ts_web/elements/00group-input/text-input-contextmenu.ts +31 -0
  60. package/ts_web/elements/00group-overlay/dees-contextmenu/dees-contextmenu.ts +3 -3
  61. package/readme.hints.md +0 -1233
  62. package/readme.icons.md +0 -1849
  63. package/readme.info.md +0 -80
  64. package/readme.plan.md +0 -671
  65. package/readme.playbook.md +0 -820
  66. 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
- ${component.renderDescription()}
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
- const input = this.shadowRoot!.querySelector('input')!;
412
- const hasSelection = input.selectionStart !== input.selectionEnd;
413
- return [
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,2 @@
1
+ import { focusedInputDemo } from '../../../demos/inputs.js';
2
+ export const demoFunc = () => focusedInputDemo('textarea');
@@ -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
- constructor() {
170
- super();
171
- this.tabIndex = 0;
169
+ public connectedCallback(): Promise<void> {
170
+ if (!this.hasAttribute('tabindex')) this.tabIndex = 0;
171
+ return super.connectedCallback();
172
172
  }
173
173
 
174
174
  /**