@design.estate/dees-catalog 3.97.0 → 3.98.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 +4701 -3485
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-button/dees-button/dees-button.d.ts +35 -0
- package/dist_ts_web/elements/00group-button/dees-button/dees-button.js +128 -5
- package/dist_ts_web/elements/00group-dataview/dees-statsgrid/dees-statsgrid.js +5 -6
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.d.ts +1 -0
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.js +21 -11
- package/dist_ts_web/elements/00group-feedback/dees-spinner/dees-spinner.js +12 -3
- package/dist_ts_web/elements/00group-form/dees-form-submit/dees-form-submit.d.ts +9 -0
- package/dist_ts_web/elements/00group-form/dees-form-submit/dees-form-submit.js +25 -2
- package/dist_ts_web/elements/00group-input/dees-input-multitoggle/dees-input-multitoggle.js +20 -4
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.d.ts +15 -0
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.js +36 -2
- package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.js +2 -1
- package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.d.ts +246 -0
- package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.js +439 -21
- package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.js +857 -23
- package/dist_ts_web/elements/00group-utility/dees-icon/dees-icon.d.ts +2 -1
- package/dist_ts_web/elements/00group-utility/dees-icon/dees-icon.js +52 -80
- package/dist_ts_web/elements/00theme.d.ts +2 -0
- package/dist_ts_web/elements/00theme.js +7 -1
- package/package.json +3 -3
- package/readme.hints.md +35 -0
- package/readme.md +85 -11
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-button/dees-button/dees-button.ts +128 -2
- package/ts_web/elements/00group-dataview/dees-statsgrid/dees-statsgrid.ts +5 -6
- package/ts_web/elements/00group-dataview/dees-table/dees-table.ts +43 -27
- package/ts_web/elements/00group-feedback/dees-spinner/dees-spinner.ts +11 -2
- package/ts_web/elements/00group-form/dees-form-submit/dees-form-submit.ts +18 -0
- package/ts_web/elements/00group-input/dees-input-multitoggle/dees-input-multitoggle.ts +19 -3
- package/ts_web/elements/00group-input/dees-input-text/dees-input-text.ts +29 -0
- package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts +1 -0
- package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.ts +418 -19
- package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.ts +969 -20
- package/ts_web/elements/00group-utility/dees-icon/dees-icon.ts +55 -91
- package/ts_web/elements/00theme.ts +8 -0
|
@@ -78,6 +78,15 @@ export class DeesButton extends DeesElement {
|
|
|
78
78
|
})
|
|
79
79
|
accessor insideForm: boolean = false;
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* stretches the inner button face to the host width.
|
|
83
|
+
* The host is `inline-block`, so `width: 100%` on the host alone leaves the
|
|
84
|
+
* `inline-flex` face shrink-wrapped. Opt in for stacked, full-bleed actions
|
|
85
|
+
* (login cards, sheets, empty states).
|
|
86
|
+
*/
|
|
87
|
+
@property({ type: Boolean, reflect: true, attribute: 'full-width' })
|
|
88
|
+
accessor fullWidth: boolean = false;
|
|
89
|
+
|
|
81
90
|
@property({ type: String, reflect: true })
|
|
82
91
|
accessor icon!: string;
|
|
83
92
|
|
|
@@ -124,6 +133,26 @@ export class DeesButton extends DeesElement {
|
|
|
124
133
|
display: none;
|
|
125
134
|
}
|
|
126
135
|
|
|
136
|
+
:host([full-width]) {
|
|
137
|
+
display: block;
|
|
138
|
+
width: 100%;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* A full-width button is sized by its container, so its label has to yield rather
|
|
142
|
+
than spill out of the face when the container is narrower than the text. */
|
|
143
|
+
:host([full-width]) .button {
|
|
144
|
+
width: 100%;
|
|
145
|
+
min-width: 0;
|
|
146
|
+
overflow: hidden;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
:host([full-width]) .textbox {
|
|
150
|
+
min-width: 0;
|
|
151
|
+
overflow: hidden;
|
|
152
|
+
text-overflow: ellipsis;
|
|
153
|
+
white-space: nowrap;
|
|
154
|
+
}
|
|
155
|
+
|
|
127
156
|
/* Form spacing styles */
|
|
128
157
|
:host([inside-form]) {
|
|
129
158
|
margin-bottom: var(--dees-spacing-lg);
|
|
@@ -363,10 +392,30 @@ export class DeesButton extends DeesElement {
|
|
|
363
392
|
display: none;
|
|
364
393
|
}
|
|
365
394
|
|
|
366
|
-
/* Focus state
|
|
395
|
+
/* Focus state.
|
|
396
|
+
The base .button rule sets outline:none for the pointer path; the ring is restored
|
|
397
|
+
here for :focus-visible only, so a mouse press never leaves a ring behind while
|
|
398
|
+
keyboard focus is always visible. This rule is the focus affordance keyboard users
|
|
399
|
+
actually see — do not remove it alongside the base outline:none. */
|
|
367
400
|
.button:focus-visible {
|
|
368
|
-
|
|
401
|
+
/* Deliberately NOT --dees-color-focus-ring: that token is a 0.45/0.55 alpha, which
|
|
402
|
+
composites to 1.85:1 (bright) and 2.31:1 (dark) against the surface behind the
|
|
403
|
+
button — under the 3:1 that WCAG 2.2 SC 1.4.11 requires of a focus indicator, and
|
|
404
|
+
effectively invisible as a navy hairline on a dark canvas. The opaque accent
|
|
405
|
+
measures 4.02:1 bright / 5.76:1 dark. Raising the alpha on the shared token would
|
|
406
|
+
fix every other control too and is the wider change. */
|
|
407
|
+
outline: 2px solid var(--dees-color-accent-primary);
|
|
369
408
|
outline-offset: 2px;
|
|
409
|
+
/* The indicator has to appear at once. The base .button rule carries
|
|
410
|
+
transition:all, which animates outline-color up from its initial currentColor —
|
|
411
|
+
measured as ~50ms of a near-white ring on a bright accent face, i.e. briefly
|
|
412
|
+
invisible right when the user needs it. Only the focused state opts out. */
|
|
413
|
+
transition: none;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* A disabled face is removed from the tab order, so it should never show a ring. */
|
|
417
|
+
.button.disabled:focus-visible {
|
|
418
|
+
outline: none;
|
|
370
419
|
}
|
|
371
420
|
|
|
372
421
|
/* Loading spinner */
|
|
@@ -488,7 +537,14 @@ export class DeesButton extends DeesElement {
|
|
|
488
537
|
class="button ${this.isHidden ? 'hidden' : ''} ${actualType} size-${actualSize} shape-${this.shape} ${this.status} ${this.disabled
|
|
489
538
|
? 'disabled'
|
|
490
539
|
: ''}"
|
|
540
|
+
role="button"
|
|
541
|
+
tabindex=${this.disabled ? -1 : 0}
|
|
542
|
+
aria-disabled=${this.disabled ? 'true' : 'false'}
|
|
543
|
+
aria-busy=${this.status === 'pending' ? 'true' : 'false'}
|
|
544
|
+
.ariaLabel=${showText ? null : this.text || null}
|
|
491
545
|
@click="${this.dispatchClick}"
|
|
546
|
+
@keydown=${this.handleKeydown}
|
|
547
|
+
@keyup=${this.handleKeyup}
|
|
492
548
|
>
|
|
493
549
|
${this.status === 'normal' ? html``: html`
|
|
494
550
|
<dees-spinner
|
|
@@ -519,6 +575,76 @@ export class DeesButton extends DeesElement {
|
|
|
519
575
|
);
|
|
520
576
|
}
|
|
521
577
|
|
|
578
|
+
/** the inner face — the element that actually carries focus */
|
|
579
|
+
private get faceElement(): HTMLElement | null {
|
|
580
|
+
return (this.shadowRoot?.querySelector('.button') as HTMLElement | null) || null;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* States in which the button must not react to input. `disabled` is obvious; the status
|
|
585
|
+
* faces matter too, because `.button.pending / .success / .error` already set
|
|
586
|
+
* `pointer-events: none` and keyboard activation has to refuse the same interactions a
|
|
587
|
+
* mouse cannot make.
|
|
588
|
+
*/
|
|
589
|
+
private get isInert(): boolean {
|
|
590
|
+
return this.disabled || this.status !== 'normal';
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Focus lives on the inner face inside the shadow root, so `host.focus()` has to forward
|
|
595
|
+
* or it is a silent no-op. Note that `dees-form-submit.focus()` is deliberately *not*
|
|
596
|
+
* wired to this — that method submits the form, and dees-form depends on it.
|
|
597
|
+
*/
|
|
598
|
+
public focus(optionsArg?: FocusOptions): void {
|
|
599
|
+
const face = this.faceElement;
|
|
600
|
+
if (face) {
|
|
601
|
+
face.focus(optionsArg);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
super.focus(optionsArg);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
public blur(): void {
|
|
608
|
+
this.faceElement?.blur();
|
|
609
|
+
super.blur();
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
private handleKeydown = (eventArg: KeyboardEvent): void => {
|
|
613
|
+
if (eventArg.key === ' ' || eventArg.key === 'Spacebar') {
|
|
614
|
+
// Native buttons activate Space on keyup. Swallow the keydown so the page cannot
|
|
615
|
+
// scroll underneath the focused button.
|
|
616
|
+
eventArg.preventDefault();
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
if (eventArg.key === 'Enter') {
|
|
620
|
+
eventArg.preventDefault();
|
|
621
|
+
this.activateFromKeyboard();
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
private handleKeyup = (eventArg: KeyboardEvent): void => {
|
|
626
|
+
if (eventArg.key === ' ' || eventArg.key === 'Spacebar') {
|
|
627
|
+
eventArg.preventDefault();
|
|
628
|
+
this.activateFromKeyboard();
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Keyboard activation goes through a real click rather than dispatching `clicked`
|
|
634
|
+
* directly, exactly as a native button does. Several catalog components bind `@click` on
|
|
635
|
+
* dees-button instead of `@clicked` (dees-workspace-diff-editor, the profile picture
|
|
636
|
+
* modal), and both listener styles have to keep working from the keyboard.
|
|
637
|
+
*
|
|
638
|
+
* `pointer-events: none` does not block a programmatic `click()`, so inertness is
|
|
639
|
+
* enforced here rather than left to CSS.
|
|
640
|
+
*/
|
|
641
|
+
private activateFromKeyboard(): void {
|
|
642
|
+
if (this.isInert) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
this.faceElement?.click();
|
|
646
|
+
}
|
|
647
|
+
|
|
522
648
|
public async firstUpdated() {
|
|
523
649
|
// Extract light DOM content (icon + text) and set as properties
|
|
524
650
|
this.extractLightDom();
|
|
@@ -823,14 +823,13 @@ export class DeesStatsGrid extends DeesElement {
|
|
|
823
823
|
<div class="grid-title"></div>
|
|
824
824
|
<div class="grid-actions">
|
|
825
825
|
${this.gridActions.map(action => html`
|
|
826
|
-
<dees-button
|
|
826
|
+
<dees-button
|
|
827
827
|
@clicked=${() => this.handleGridAction(action)}
|
|
828
828
|
type="outline"
|
|
829
829
|
size="sm"
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
</dees-button>
|
|
830
|
+
.text=${action.name}
|
|
831
|
+
.icon=${action.iconName || ''}
|
|
832
|
+
></dees-button>
|
|
834
833
|
`)}
|
|
835
834
|
</div>
|
|
836
835
|
</div>
|
|
@@ -1249,4 +1248,4 @@ export class DeesStatsGrid extends DeesElement {
|
|
|
1249
1248
|
// open an empty menu.)
|
|
1250
1249
|
DeesContextmenu.openContextMenuWithOptions(event, tile.actions as any);
|
|
1251
1250
|
}
|
|
1252
|
-
}
|
|
1251
|
+
}
|
|
@@ -735,22 +735,24 @@ export class DeesTable<T> extends DeesElement {
|
|
|
735
735
|
return html`
|
|
736
736
|
<td class="actionsCol">
|
|
737
737
|
<div class="actionsContainer">
|
|
738
|
-
${inRowActions
|
|
739
|
-
(actionArg) =>
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
738
|
+
${inRowActions
|
|
739
|
+
.filter((actionArg) => this.isActionRelevant(actionArg, itemArg))
|
|
740
|
+
.map(
|
|
741
|
+
(actionArg) => html`
|
|
742
|
+
<div
|
|
743
|
+
class="action"
|
|
744
|
+
@click=${() =>
|
|
745
|
+
actionArg.actionFunc({
|
|
746
|
+
item: itemArg,
|
|
747
|
+
table: this,
|
|
748
|
+
})}
|
|
749
|
+
>
|
|
750
|
+
${actionArg.iconName
|
|
751
|
+
? html` <dees-icon .icon=${actionArg.iconName}></dees-icon> `
|
|
752
|
+
: actionArg.name}
|
|
753
|
+
</div>
|
|
754
|
+
`
|
|
755
|
+
)}
|
|
754
756
|
</div>
|
|
755
757
|
</td>
|
|
756
758
|
`;
|
|
@@ -2119,7 +2121,11 @@ export class DeesTable<T> extends DeesElement {
|
|
|
2119
2121
|
this.startEditing(cell.item, cell.col);
|
|
2120
2122
|
return;
|
|
2121
2123
|
}
|
|
2122
|
-
const dblAction = this.dataActions.find(
|
|
2124
|
+
const dblAction = this.dataActions.find(
|
|
2125
|
+
(action) =>
|
|
2126
|
+
action.type?.includes('doubleClick')
|
|
2127
|
+
&& this.isActionRelevant(action, cell.item)
|
|
2128
|
+
);
|
|
2123
2129
|
if (dblAction) dblAction.actionFunc({ item: cell.item, table: this });
|
|
2124
2130
|
};
|
|
2125
2131
|
|
|
@@ -2145,16 +2151,19 @@ export class DeesTable<T> extends DeesElement {
|
|
|
2145
2151
|
this.emitSelectionChange();
|
|
2146
2152
|
this.requestUpdate();
|
|
2147
2153
|
}
|
|
2148
|
-
const userItems: plugins.tsclass.website.IMenuItem[] = this
|
|
2149
|
-
(
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
action
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2154
|
+
const userItems: plugins.tsclass.website.IMenuItem[] = this
|
|
2155
|
+
.getActionsForType('contextmenu')
|
|
2156
|
+
.filter((action) => this.isActionRelevant(action, item))
|
|
2157
|
+
.map(
|
|
2158
|
+
(action) => ({
|
|
2159
|
+
name: action.name,
|
|
2160
|
+
iconName: action.iconName as any,
|
|
2161
|
+
action: async () => {
|
|
2162
|
+
await action.actionFunc({ item, table: this });
|
|
2163
|
+
return null;
|
|
2164
|
+
},
|
|
2165
|
+
})
|
|
2166
|
+
);
|
|
2158
2167
|
const defaultItems: plugins.tsclass.website.IMenuItem[] = [
|
|
2159
2168
|
{
|
|
2160
2169
|
name:
|
|
@@ -2333,6 +2342,13 @@ export class DeesTable<T> extends DeesElement {
|
|
|
2333
2342
|
);
|
|
2334
2343
|
}
|
|
2335
2344
|
|
|
2345
|
+
private isActionRelevant(actionArg: ITableAction<T>, itemArg: T): boolean {
|
|
2346
|
+
return (
|
|
2347
|
+
!actionArg.actionRelevancyCheckFunc
|
|
2348
|
+
|| actionArg.actionRelevancyCheckFunc(itemArg)
|
|
2349
|
+
);
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2336
2352
|
getActionsForType(typeArg: ITableAction['type'][0]) {
|
|
2337
2353
|
const actions: ITableAction[] = [];
|
|
2338
2354
|
for (const action of this.dataActions) {
|
|
@@ -55,6 +55,10 @@ export class DeesSpinner extends DeesElement {
|
|
|
55
55
|
display: block;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/* --dees-spinner-color lets a spinner on a coloured surface (an accent button face,
|
|
59
|
+
a toast) match that surface's foreground. It is read as an inherited fallback
|
|
60
|
+
rather than declared on :host, because a :host declaration would outrank the
|
|
61
|
+
value an ancestor sets. Unset consumers keep the previous token. */
|
|
58
62
|
#loading {
|
|
59
63
|
position: relative;
|
|
60
64
|
transition: none;
|
|
@@ -62,9 +66,14 @@ export class DeesSpinner extends DeesElement {
|
|
|
62
66
|
justify-content: center;
|
|
63
67
|
align-content: center;
|
|
64
68
|
background: transparent;
|
|
65
|
-
border: 3px solid
|
|
69
|
+
border: 3px solid
|
|
70
|
+
color-mix(
|
|
71
|
+
in srgb,
|
|
72
|
+
var(--dees-spinner-color, var(--dees-color-text-primary)) 20%,
|
|
73
|
+
transparent
|
|
74
|
+
);
|
|
66
75
|
border-radius: 50%;
|
|
67
|
-
border-top-color: var(--dees-color-text-primary);
|
|
76
|
+
border-top-color: var(--dees-spinner-color, var(--dees-color-text-primary));
|
|
68
77
|
animation: spin 1s ease-in-out infinite;
|
|
69
78
|
-webkit-animation: spin 1s ease-in-out infinite;
|
|
70
79
|
}
|
|
@@ -47,6 +47,10 @@ export class DeesFormSubmit extends DeesElement {
|
|
|
47
47
|
@property({ type: String })
|
|
48
48
|
accessor iconPosition: 'left' | 'right' = 'left';
|
|
49
49
|
|
|
50
|
+
/** stretches the button face to the full host width — see dees-button.fullWidth */
|
|
51
|
+
@property({ type: Boolean, reflect: true, attribute: 'full-width' })
|
|
52
|
+
accessor fullWidth: boolean = false;
|
|
53
|
+
|
|
50
54
|
constructor() {
|
|
51
55
|
super();
|
|
52
56
|
}
|
|
@@ -58,6 +62,12 @@ export class DeesFormSubmit extends DeesElement {
|
|
|
58
62
|
:host {
|
|
59
63
|
display: inline-block;
|
|
60
64
|
}
|
|
65
|
+
/* The host has to stretch too, or forwarding fullWidth only widens the face inside a
|
|
66
|
+
still shrink-wrapped host and the property looks broken. */
|
|
67
|
+
:host([full-width]) {
|
|
68
|
+
display: block;
|
|
69
|
+
width: 100%;
|
|
70
|
+
}
|
|
61
71
|
dees-button {
|
|
62
72
|
width: 100%;
|
|
63
73
|
}
|
|
@@ -73,6 +83,7 @@ export class DeesFormSubmit extends DeesElement {
|
|
|
73
83
|
.icon=${this.icon}
|
|
74
84
|
.iconPosition=${this.iconPosition}
|
|
75
85
|
.text=${this.text}
|
|
86
|
+
.fullWidth=${this.fullWidth}
|
|
76
87
|
?disabled=${this.disabled}
|
|
77
88
|
@clicked=${this.submit}
|
|
78
89
|
></dees-button>
|
|
@@ -103,6 +114,13 @@ export class DeesFormSubmit extends DeesElement {
|
|
|
103
114
|
}
|
|
104
115
|
}
|
|
105
116
|
|
|
117
|
+
/**
|
|
118
|
+
* NOT a focus method despite the name — it submits. `dees-form.addBehaviours()` calls
|
|
119
|
+
* `getSubmitButton()?.focus()` when Enter is pressed in the last field, and that is the
|
|
120
|
+
* catalog's "Enter submits the form" behaviour. Renaming it would break every form, so it
|
|
121
|
+
* stays; keyboard *focus* reaches the submit through the inner dees-button face, which
|
|
122
|
+
* carries its own tabindex.
|
|
123
|
+
*/
|
|
106
124
|
public async focus() {
|
|
107
125
|
const domtools = await this.domtoolsPromise;
|
|
108
126
|
if (!this.disabled) {
|
|
@@ -89,8 +89,8 @@ export class DeesInputMultitoggle extends DeesInputBase<DeesInputMultitoggle> {
|
|
|
89
89
|
|
|
90
90
|
.option {
|
|
91
91
|
position: relative;
|
|
92
|
-
display:
|
|
93
|
-
|
|
92
|
+
display: grid;
|
|
93
|
+
place-items: center;
|
|
94
94
|
height: 100%;
|
|
95
95
|
padding: 0 var(--dees-spacing-lg);
|
|
96
96
|
border-radius: var(--dees-radius-sm);
|
|
@@ -104,6 +104,21 @@ export class DeesInputMultitoggle extends DeesInputBase<DeesInputMultitoggle> {
|
|
|
104
104
|
z-index: 2;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
.option-label,
|
|
108
|
+
.option::after {
|
|
109
|
+
grid-area: 1 / 1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Reserve the selected label's semibold width in every state so moving
|
|
113
|
+
the thumb never changes the segmented control's intrinsic width. */
|
|
114
|
+
.option::after {
|
|
115
|
+
content: attr(data-option);
|
|
116
|
+
visibility: hidden;
|
|
117
|
+
font-weight: 600;
|
|
118
|
+
pointer-events: none;
|
|
119
|
+
user-select: none;
|
|
120
|
+
}
|
|
121
|
+
|
|
107
122
|
/* Hairline separators between unselected segments; they fade out next to
|
|
108
123
|
the thumb. */
|
|
109
124
|
.option::before {
|
|
@@ -197,8 +212,9 @@ export class DeesInputMultitoggle extends DeesInputBase<DeesInputMultitoggle> {
|
|
|
197
212
|
index === 0 || isSelected || index - 1 === selectedIndex;
|
|
198
213
|
return html`<div
|
|
199
214
|
class="option ${isSelected ? 'selected' : ''} ${hideSeparator ? 'no-sep' : ''}"
|
|
215
|
+
data-option=${option}
|
|
200
216
|
@click=${() => this.handleSelection(option)}
|
|
201
|
-
>${option}</div>`;
|
|
217
|
+
><span class="option-label">${option}</span></div>`;
|
|
202
218
|
})}
|
|
203
219
|
</div>
|
|
204
220
|
</div>
|
|
@@ -67,6 +67,16 @@ export class DeesInputText extends DeesInputBase {
|
|
|
67
67
|
@property({ attribute: false })
|
|
68
68
|
accessor validConfirmed: boolean = false;
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* autofill hint forwarded to the inner input element.
|
|
72
|
+
* Empty by default, which renders no `autocomplete` attribute at all — byte-identical to
|
|
73
|
+
* this component before the property existed. Set it to a concrete token list to help
|
|
74
|
+
* password managers (`'username'`, `'current-password'`, `'one-time-code'`) or to opt an
|
|
75
|
+
* input into WebAuthn conditional mediation (`'username webauthn'`).
|
|
76
|
+
*/
|
|
77
|
+
@property({ type: String })
|
|
78
|
+
accessor autocomplete: string = '';
|
|
79
|
+
|
|
70
80
|
private validTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
71
81
|
|
|
72
82
|
public static styles = [
|
|
@@ -324,6 +334,25 @@ export class DeesInputText extends DeesInputBase {
|
|
|
324
334
|
`;
|
|
325
335
|
}
|
|
326
336
|
|
|
337
|
+
/**
|
|
338
|
+
* `autocomplete` is applied imperatively rather than bound in the template so that the
|
|
339
|
+
* default renders no attribute at all. Binding it would emit `autocomplete=""` on every
|
|
340
|
+
* text input in the catalog, and an empty or `on` value is not universally equivalent to
|
|
341
|
+
* an absent one across browser and password-manager heuristics.
|
|
342
|
+
*/
|
|
343
|
+
public updated(_changedProperties: Map<string, any>): void {
|
|
344
|
+
super.updated(_changedProperties);
|
|
345
|
+
const input = this.shadowRoot?.querySelector('input');
|
|
346
|
+
if (!input) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (this.autocomplete) {
|
|
350
|
+
input.setAttribute('autocomplete', this.autocomplete);
|
|
351
|
+
} else {
|
|
352
|
+
input.removeAttribute('autocomplete');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
327
356
|
firstUpdated() {
|
|
328
357
|
if (this.validationFunction && this.value) {
|
|
329
358
|
const result = this.validationFunction(this.value);
|