@design.estate/dees-catalog 9.8.0 → 10.0.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 +3971 -3756
- package/dist_bundle/bundle.js.map +1 -1
- package/dist_bundle/third-party-notices.md +783 -10
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/dist_ts_web/elements/00actionerror.d.ts +32 -0
- package/dist_ts_web/elements/00actionerror.js +45 -0
- package/dist_ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.d.ts +16 -0
- package/dist_ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.js +40 -3
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.d.ts +7 -0
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.js +17 -17
- package/dist_ts_web/elements/00group-feedback/dees-hint/dees-hint.d.ts +14 -3
- package/dist_ts_web/elements/00group-feedback/dees-hint/dees-hint.demo.js +23 -4
- package/dist_ts_web/elements/00group-feedback/dees-hint/dees-hint.js +77 -5
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.d.ts +23 -3
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.js +71 -11
- package/dist_ts_web/elements/00group-input/index.d.ts +0 -1
- package/dist_ts_web/elements/00group-input/index.js +1 -2
- package/dist_ts_web/elements/00group-overlay/dees-modal/dees-modal.d.ts +7 -0
- package/dist_ts_web/elements/00group-overlay/dees-modal/dees-modal.js +13 -3
- package/dist_ts_web/elements/index.d.ts +1 -2
- package/dist_ts_web/elements/index.js +2 -4
- package/package.json +3 -3
- package/readme.md +71 -4
- package/scripts/check-third-party-notices.mjs +261 -0
- package/third-party-notices.md +783 -10
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00actionerror.ts +66 -0
- package/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.ts +38 -2
- package/ts_web/elements/00group-dataview/dees-table/dees-table.ts +18 -16
- package/ts_web/elements/00group-feedback/dees-hint/dees-hint.demo.ts +22 -3
- package/ts_web/elements/00group-feedback/dees-hint/dees-hint.ts +71 -3
- package/ts_web/elements/00group-input/dees-input-text/dees-input-text.ts +73 -13
- package/ts_web/elements/00group-input/index.ts +0 -1
- package/ts_web/elements/00group-overlay/dees-modal/dees-modal.ts +13 -2
- package/ts_web/elements/index.ts +1 -4
- package/dist_ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.d.ts +0 -1
- package/dist_ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.js +0 -2
- package/dist_ts_web/elements/00group-input/dees-input-searchselect/index.d.ts +0 -1
- package/dist_ts_web/elements/00group-input/dees-input-searchselect/index.js +0 -2
- package/dist_ts_web/elements/dees-search/dees-search.d.ts +0 -1
- package/dist_ts_web/elements/dees-search/dees-search.js +0 -2
- package/dist_ts_web/elements/dees-search/index.d.ts +0 -1
- package/dist_ts_web/elements/dees-search/index.js +0 -2
- package/dist_ts_web/elements/dees-tooltip/dees-tooltip.d.ts +0 -1
- package/dist_ts_web/elements/dees-tooltip/dees-tooltip.js +0 -2
- package/dist_ts_web/elements/dees-tooltip/index.d.ts +0 -1
- package/dist_ts_web/elements/dees-tooltip/index.js +0 -2
- package/ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.ts +0 -0
- package/ts_web/elements/00group-input/dees-input-searchselect/index.ts +0 -1
- package/ts_web/elements/dees-search/dees-search.ts +0 -0
- package/ts_web/elements/dees-search/index.ts +0 -1
- package/ts_web/elements/dees-tooltip/dees-tooltip.ts +0 -1
- package/ts_web/elements/dees-tooltip/index.ts +0 -1
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@design.estate/dees-catalog',
|
|
6
|
-
version: '
|
|
6
|
+
version: '10.0.0',
|
|
7
7
|
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
|
8
8
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { DeesToast } from './00group-feedback/dees-toast/dees-toast.js';
|
|
2
|
+
|
|
3
|
+
/** Payload of the cancelable `dees-action-error` event. */
|
|
4
|
+
export interface IActionErrorDetail<TAction = unknown> {
|
|
5
|
+
/** Whatever the action callback rejected with. */
|
|
6
|
+
error: unknown;
|
|
7
|
+
/** The action descriptor whose callback rejected. */
|
|
8
|
+
action: TAction;
|
|
9
|
+
/**
|
|
10
|
+
* Where the action was invoked from. Catalog sources are `modal-menu`,
|
|
11
|
+
* `table-header`, `table-footer`, `table-row`, `table-rowmenu`,
|
|
12
|
+
* `table-contextmenu` and `table-doubleclick`.
|
|
13
|
+
*/
|
|
14
|
+
source: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The event a component dispatches before it falls back to a toast. */
|
|
18
|
+
export const actionErrorEventName = 'dees-action-error';
|
|
19
|
+
|
|
20
|
+
/** Human-readable text for an unknown rejection value. */
|
|
21
|
+
export const actionErrorMessage = (errorArg: unknown): string => {
|
|
22
|
+
if (errorArg instanceof Error) return errorArg.message || errorArg.name;
|
|
23
|
+
if (typeof errorArg === 'string' && errorArg) return errorArg;
|
|
24
|
+
const text = String(errorArg);
|
|
25
|
+
return text === '[object Object]' || text === 'undefined' || text === 'null' ? 'The action failed.' : text;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Announces a failed action on `hostArg` and, unless a listener cancels the
|
|
30
|
+
* event, surfaces it through the catalog's own notification primitive. The
|
|
31
|
+
* event is cancelable so an application can render its own error surface.
|
|
32
|
+
*/
|
|
33
|
+
export const reportActionError = <TAction>(
|
|
34
|
+
hostArg: HTMLElement,
|
|
35
|
+
detailArg: IActionErrorDetail<TAction>,
|
|
36
|
+
ownerElementArg?: Node | null,
|
|
37
|
+
): void => {
|
|
38
|
+
const handled = !hostArg.dispatchEvent(new CustomEvent<IActionErrorDetail<TAction>>(actionErrorEventName, {
|
|
39
|
+
detail: detailArg,
|
|
40
|
+
bubbles: true,
|
|
41
|
+
composed: true,
|
|
42
|
+
cancelable: true,
|
|
43
|
+
}));
|
|
44
|
+
if (handled) return;
|
|
45
|
+
void DeesToast.show({
|
|
46
|
+
message: actionErrorMessage(detailArg.error),
|
|
47
|
+
type: 'error',
|
|
48
|
+
ownerElement: ownerElementArg ?? undefined,
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Awaits an action callback so a rejected promise becomes a visible failure
|
|
54
|
+
* instead of an unhandled rejection. Successful actions resolve unchanged.
|
|
55
|
+
*/
|
|
56
|
+
export const runAction = async <TAction>(
|
|
57
|
+
hostArg: HTMLElement,
|
|
58
|
+
detailArg: { action: TAction; source: string; ownerElement?: Node | null },
|
|
59
|
+
runArg: () => unknown,
|
|
60
|
+
): Promise<void> => {
|
|
61
|
+
try {
|
|
62
|
+
await runArg();
|
|
63
|
+
} catch (error) {
|
|
64
|
+
reportActionError(hostArg, { error, action: detailArg.action, source: detailArg.source }, detailArg.ownerElement);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
@@ -442,7 +442,7 @@ export class DeesAppuiTabs extends DeesElement {
|
|
|
442
442
|
}
|
|
443
443
|
|
|
444
444
|
private renderTab(tab: interfaces.IMenuItem, _isHorizontal: boolean): TemplateResult {
|
|
445
|
-
const isSelected =
|
|
445
|
+
const isSelected = this.isTabSelected(tab);
|
|
446
446
|
const label = tab.label ?? tab.key;
|
|
447
447
|
return html`<div class="tab ${isSelected ? 'selectedTab' : ''} ${isSelected && this.showTabIndicator ? 'has-indicator' : ''}" role="presentation">
|
|
448
448
|
<button type="button" class="tab-select" role="tab" title=${label} aria-selected=${isSelected} tabindex=${isSelected ? 0 : -1}
|
|
@@ -494,6 +494,29 @@ export class DeesAppuiTabs extends DeesElement {
|
|
|
494
494
|
return tab.iconName ? html`<dees-icon .icon=${tab.iconName}></dees-icon>` : '';
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
+
/**
|
|
498
|
+
* The stable identity of a tab descriptor. `key` is the documented
|
|
499
|
+
* identifier; `label` is the fallback for descriptors that carry only a
|
|
500
|
+
* display name.
|
|
501
|
+
*/
|
|
502
|
+
private tabKey(tabArg: interfaces.IMenuItem | null | undefined): string | undefined {
|
|
503
|
+
return tabArg ? (tabArg.key ?? tabArg.label) : undefined;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** The current descriptor for a selection, matched by key rather than by object identity. */
|
|
507
|
+
private findTabByKey(tabArg: interfaces.IMenuItem | null): interfaces.IMenuItem | undefined {
|
|
508
|
+
const key = this.tabKey(tabArg);
|
|
509
|
+
if (key === undefined) return this.tabs.find((tab) => tab === tabArg);
|
|
510
|
+
return this.tabs.find((tab) => this.tabKey(tab) === key);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private isTabSelected(tabArg: interfaces.IMenuItem): boolean {
|
|
514
|
+
if (!this.selectedTab) return false;
|
|
515
|
+
if (tabArg === this.selectedTab) return true;
|
|
516
|
+
const key = this.tabKey(tabArg);
|
|
517
|
+
return key !== undefined && key === this.tabKey(this.selectedTab);
|
|
518
|
+
}
|
|
519
|
+
|
|
497
520
|
private selectTab(tabArg: interfaces.IMenuItem) {
|
|
498
521
|
this.selectedTab = tabArg;
|
|
499
522
|
tabArg.action();
|
|
@@ -612,6 +635,19 @@ export class DeesAppuiTabs extends DeesElement {
|
|
|
612
635
|
}
|
|
613
636
|
}
|
|
614
637
|
|
|
638
|
+
/**
|
|
639
|
+
* A host that binds a literal `.tabs=${[...]}` hands us new descriptor
|
|
640
|
+
* objects on every render. The selection follows the descriptor key, so the
|
|
641
|
+
* open view survives a rerender; adopting the fresh object here keeps index
|
|
642
|
+
* lookups (close, scroll-into-view) working without re-running the action.
|
|
643
|
+
*/
|
|
644
|
+
public willUpdate(changedProperties: Map<string, any>) {
|
|
645
|
+
super.willUpdate(changedProperties);
|
|
646
|
+
if (!changedProperties.has('tabs') || !this.selectedTab) return;
|
|
647
|
+
const current = this.findTabByKey(this.selectedTab);
|
|
648
|
+
if (current && current !== this.selectedTab) this.selectedTab = current;
|
|
649
|
+
}
|
|
650
|
+
|
|
615
651
|
async updated(changedProperties: Map<string, any>) {
|
|
616
652
|
super.updated(changedProperties);
|
|
617
653
|
|
|
@@ -626,7 +662,7 @@ export class DeesAppuiTabs extends DeesElement {
|
|
|
626
662
|
composed: true,
|
|
627
663
|
}));
|
|
628
664
|
}
|
|
629
|
-
} else if (this.selectedTab && !this.
|
|
665
|
+
} else if (this.selectedTab && !this.findTabByKey(this.selectedTab)) {
|
|
630
666
|
// Selected tab was removed => select first available
|
|
631
667
|
this.selectTab(this.tabs[0]);
|
|
632
668
|
} else if (!this.selectedTab) {
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
} from './data.js';
|
|
29
29
|
import { compileLucenePredicate } from './lucene.js';
|
|
30
30
|
import { themeDefaultStyles } from '../../00theme.js';
|
|
31
|
+
import { runAction } from '../../00actionerror.js';
|
|
31
32
|
import '../../00group-layout/dees-tile/dees-tile.js';
|
|
32
33
|
|
|
33
34
|
export type { Column, ISortDescriptor, ITableAction, ITableActionDataArg, TDisplayFunction } from './types.js';
|
|
@@ -614,12 +615,7 @@ export class DeesTable<T> extends DeesElement {
|
|
|
614
615
|
${headerActions.map(
|
|
615
616
|
(action) => html`<button type="button"
|
|
616
617
|
class="headerAction control"
|
|
617
|
-
@click=${() =>
|
|
618
|
-
action.actionFunc({
|
|
619
|
-
item: this.selectedDataRow,
|
|
620
|
-
table: this,
|
|
621
|
-
});
|
|
622
|
-
}}
|
|
618
|
+
@click=${() => this.runDataAction(action, this.selectedDataRow, 'table-header')}
|
|
623
619
|
>
|
|
624
620
|
${action.iconName
|
|
625
621
|
? html`<dees-icon aria-hidden="true" .iconSize=${14} .icon=${action.iconName}></dees-icon>
|
|
@@ -755,12 +751,7 @@ export class DeesTable<T> extends DeesElement {
|
|
|
755
751
|
${footerActions.map(
|
|
756
752
|
(action) => html`<button type="button"
|
|
757
753
|
class="footerAction control"
|
|
758
|
-
@click=${() =>
|
|
759
|
-
action.actionFunc({
|
|
760
|
-
item: this.selectedDataRow,
|
|
761
|
-
table: this,
|
|
762
|
-
});
|
|
763
|
-
}}
|
|
754
|
+
@click=${() => this.runDataAction(action, this.selectedDataRow, 'table-footer')}
|
|
764
755
|
>
|
|
765
756
|
${action.iconName
|
|
766
757
|
? html`<dees-icon aria-hidden="true" .iconSize=${14} .icon=${action.iconName}></dees-icon>
|
|
@@ -1586,7 +1577,7 @@ export class DeesTable<T> extends DeesElement {
|
|
|
1586
1577
|
return html`<td class="actionsCol"><div class="actionsContainer">
|
|
1587
1578
|
${relevant.slice(0, 3).map((action) => html`<button type="button" class="action control primaryRowAction"
|
|
1588
1579
|
title=${action.name} aria-label=${action.name} @keydown=${this.handleMenuKey}
|
|
1589
|
-
@click=${() =>
|
|
1580
|
+
@click=${() => this.runDataAction(action, item, 'table-row')}>
|
|
1590
1581
|
${action.iconName ? html`<dees-icon aria-hidden="true" .icon=${action.iconName} iconSize="16"></dees-icon>` : action.name}
|
|
1591
1582
|
</button>`)}
|
|
1592
1583
|
${overflow.length ? menuButton(overflow, 'primaryRowAction', 'More actions') : ''}
|
|
@@ -1597,7 +1588,7 @@ export class DeesTable<T> extends DeesElement {
|
|
|
1597
1588
|
private openRowActionsMenu(event: MouseEvent, item: T, actions: ITableAction<T>[]) {
|
|
1598
1589
|
void DeesContextmenu.openContextMenuWithOptions(event, actions.map((action) => ({
|
|
1599
1590
|
name: action.name, iconName: action.iconName,
|
|
1600
|
-
action: async () => { await
|
|
1591
|
+
action: async () => { await this.runDataAction(action, item, 'table-rowmenu'); return null; },
|
|
1601
1592
|
})));
|
|
1602
1593
|
}
|
|
1603
1594
|
|
|
@@ -2145,7 +2136,7 @@ export class DeesTable<T> extends DeesElement {
|
|
|
2145
2136
|
action.type?.includes('doubleClick')
|
|
2146
2137
|
&& this.isActionRelevant(action, cell.item)
|
|
2147
2138
|
);
|
|
2148
|
-
if (dblAction)
|
|
2139
|
+
if (dblAction) void this.runDataAction(dblAction, cell.item, 'table-doubleclick');
|
|
2149
2140
|
};
|
|
2150
2141
|
|
|
2151
2142
|
private __onTbodyMousedown = (eventArg: MouseEvent) => {
|
|
@@ -2178,7 +2169,7 @@ export class DeesTable<T> extends DeesElement {
|
|
|
2178
2169
|
name: action.name,
|
|
2179
2170
|
iconName: action.iconName as any,
|
|
2180
2171
|
action: async () => {
|
|
2181
|
-
await
|
|
2172
|
+
await this.runDataAction(action, item, 'table-contextmenu');
|
|
2182
2173
|
return null;
|
|
2183
2174
|
},
|
|
2184
2175
|
})
|
|
@@ -2361,6 +2352,17 @@ export class DeesTable<T> extends DeesElement {
|
|
|
2361
2352
|
);
|
|
2362
2353
|
}
|
|
2363
2354
|
|
|
2355
|
+
/**
|
|
2356
|
+
* Runs a data action and awaits it, so a rejected callback surfaces as a
|
|
2357
|
+
* visible failure instead of an unhandled rejection. The error is announced
|
|
2358
|
+
* on the table through the cancelable `dees-action-error` event and, unless
|
|
2359
|
+
* a listener cancels it, shown as a notification.
|
|
2360
|
+
*/
|
|
2361
|
+
private async runDataAction(actionArg: ITableAction<T>, itemArg: T, sourceArg: string): Promise<void> {
|
|
2362
|
+
await runAction(this, { action: actionArg, source: sourceArg, ownerElement: this }, () =>
|
|
2363
|
+
actionArg.actionFunc({ item: itemArg, table: this }));
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2364
2366
|
private isActionRelevant(actionArg: ITableAction<T>, itemArg: T): boolean {
|
|
2365
2367
|
return (
|
|
2366
2368
|
!actionArg.actionRelevancyCheckFunc
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { html } from '@design.estate/dees-element';
|
|
2
|
+
import { demoPage, demoSection, demoCode } from '../../../demos/templates.js';
|
|
2
3
|
|
|
3
|
-
export const demoFunc = () => html`
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
export const demoFunc = () => demoPage('Hint', 'An inline note next to the thing it explains. Four types carry the catalog status colors; a plain sentence or slotted markup both work.', html`
|
|
5
|
+
${demoSection('Types', 'The type chooses the icon and the accent color. Info is the default.', html`
|
|
6
|
+
<dees-hint text="Workspace names are visible to everyone you invite."></dees-hint>
|
|
7
|
+
<dees-hint type="success" text="Your changes are saved and shared with your team."></dees-hint>
|
|
8
|
+
<dees-hint type="warning" text="This workspace has no owner. Assign one before the trial ends."></dees-hint>
|
|
9
|
+
<dees-hint type="error" text="The deployment could not reach the registry."></dees-hint>
|
|
10
|
+
${demoCode('<dees-hint type="warning" text="Assign an owner before the trial ends."></dees-hint>')}
|
|
11
|
+
`)}
|
|
12
|
+
${demoSection('Slotted content', 'Use the default slot when the note needs markup. Text and slotted content render together.', html`
|
|
13
|
+
<dees-hint type="info">Read the <a href="https://code.foss.global/design.estate/dees-catalog">component index</a> before adding a control.</dees-hint>
|
|
14
|
+
<dees-hint type="warning" text="Rotation is due.">
|
|
15
|
+
<strong> Keys older than 90 days stop working.</strong>
|
|
16
|
+
</dees-hint>
|
|
17
|
+
${demoCode('<dees-hint type="info">Read the <a href="…">component index</a> first.</dees-hint>')}
|
|
18
|
+
`)}
|
|
19
|
+
${demoSection('Empty hints', 'A hint with neither text nor slotted content reserves no space, so a conditional note needs no wrapper.', html`
|
|
20
|
+
<p class="dees-demo-caption">There is an empty hint between these two lines.</p>
|
|
21
|
+
<dees-hint></dees-hint>
|
|
22
|
+
<p class="dees-demo-caption">It renders nothing until it is given text.</p>
|
|
23
|
+
`)}
|
|
24
|
+
`);
|
|
@@ -5,13 +5,14 @@ import {
|
|
|
5
5
|
customElement,
|
|
6
6
|
html,
|
|
7
7
|
property,
|
|
8
|
-
|
|
8
|
+
state,
|
|
9
9
|
type TemplateResult,
|
|
10
10
|
} from '@design.estate/dees-element';
|
|
11
11
|
|
|
12
12
|
import * as domtools from '@design.estate/dees-domtools';
|
|
13
13
|
import { demoFunc } from './dees-hint.demo.js';
|
|
14
14
|
import { themeDefaultStyles } from '../../00theme.js';
|
|
15
|
+
import '../../00group-utility/dees-icon/dees-icon.js';
|
|
15
16
|
|
|
16
17
|
declare global {
|
|
17
18
|
interface HTMLElementTagNameMap {
|
|
@@ -19,13 +20,34 @@ declare global {
|
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
export type THintType = 'info' | 'success' | 'warning' | 'error';
|
|
24
|
+
|
|
25
|
+
const hintIcons: Record<THintType, string> = {
|
|
26
|
+
info: 'lucide:Info',
|
|
27
|
+
success: 'lucide:CircleCheck',
|
|
28
|
+
warning: 'lucide:TriangleAlert',
|
|
29
|
+
error: 'lucide:CircleAlert',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* An inline advisory note next to the thing it explains. Use `text` for a plain
|
|
34
|
+
* sentence and the default slot for markup; both render when both are set.
|
|
35
|
+
* For a hover-revealed label on a control, use the control's own tooltip.
|
|
36
|
+
*/
|
|
22
37
|
@customElement('dees-hint')
|
|
23
38
|
export class DeesHint extends DeesElement {
|
|
24
39
|
public static demo = demoFunc;
|
|
25
40
|
public static demoGroups = ['Feedback'];
|
|
26
41
|
|
|
42
|
+
@property({ type: String, reflect: true })
|
|
43
|
+
accessor type: THintType = 'info';
|
|
44
|
+
|
|
45
|
+
/** The hint sentence. Rendered before any slotted content. */
|
|
27
46
|
@property({ type: String })
|
|
28
|
-
accessor
|
|
47
|
+
accessor text: string = '';
|
|
48
|
+
|
|
49
|
+
@state()
|
|
50
|
+
private accessor hasSlottedContent = false;
|
|
29
51
|
|
|
30
52
|
constructor() {
|
|
31
53
|
super();
|
|
@@ -37,14 +59,60 @@ export class DeesHint extends DeesElement {
|
|
|
37
59
|
display: block;
|
|
38
60
|
font-family: var(--dees-font-family);
|
|
39
61
|
color: var(--dees-color-text-primary);
|
|
62
|
+
--dees-hint-accent: var(--dees-color-accent-primary);
|
|
40
63
|
}
|
|
41
64
|
|
|
65
|
+
:host([type='success']) { --dees-hint-accent: var(--dees-color-text-success); }
|
|
66
|
+
:host([type='warning']) { --dees-hint-accent: var(--dees-color-text-warning); }
|
|
67
|
+
:host([type='error']) { --dees-hint-accent: var(--dees-color-text-error); }
|
|
68
|
+
|
|
42
69
|
.mainbox {
|
|
43
70
|
box-sizing: border-box;
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: flex-start;
|
|
73
|
+
gap: var(--dees-spacing-sm);
|
|
74
|
+
padding: var(--dees-spacing-sm) var(--dees-spacing-md);
|
|
75
|
+
border: 1px solid color-mix(in srgb, var(--dees-hint-accent) 28%, transparent);
|
|
76
|
+
border-radius: var(--dees-radius-lg);
|
|
77
|
+
corner-shape: var(--dees-corner-shape);
|
|
78
|
+
background: color-mix(in srgb, var(--dees-hint-accent) 8%, transparent);
|
|
79
|
+
font-size: var(--dees-font-footnote-size);
|
|
80
|
+
line-height: var(--dees-font-footnote-line);
|
|
81
|
+
overflow-wrap: anywhere;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* An element with neither text nor slotted content reserves no space. */
|
|
85
|
+
.mainbox.empty {
|
|
86
|
+
display: none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.icon {
|
|
90
|
+
flex-shrink: 0;
|
|
91
|
+
color: var(--dees-hint-accent);
|
|
92
|
+
font-size: 16px;
|
|
93
|
+
/* Optically aligns the glyph with the first line of text. */
|
|
94
|
+
margin-top: 1px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.content {
|
|
98
|
+
min-width: 0;
|
|
99
|
+
color: var(--dees-color-text-secondary);
|
|
44
100
|
}
|
|
45
101
|
`];
|
|
46
102
|
|
|
47
103
|
public render(): TemplateResult {
|
|
48
|
-
|
|
104
|
+
const isEmpty = !this.text && !this.hasSlottedContent;
|
|
105
|
+
return html`
|
|
106
|
+
<div class="mainbox ${isEmpty ? 'empty' : ''}" role="note">
|
|
107
|
+
<dees-icon class="icon" aria-hidden="true" .icon=${hintIcons[this.type] ?? hintIcons.info}></dees-icon>
|
|
108
|
+
<div class="content">${this.text}<slot @slotchange=${this.handleSlotChange}></slot></div>
|
|
109
|
+
</div>
|
|
110
|
+
`;
|
|
49
111
|
}
|
|
112
|
+
|
|
113
|
+
private handleSlotChange = (eventArg: Event) => {
|
|
114
|
+
const assigned = (eventArg.target as HTMLSlotElement).assignedNodes({ flatten: true });
|
|
115
|
+
this.hasSlottedContent = assigned.some((node) =>
|
|
116
|
+
node.nodeType === Node.ELEMENT_NODE || (node.textContent ?? '').trim().length > 0);
|
|
117
|
+
};
|
|
50
118
|
}
|
|
@@ -21,17 +21,44 @@ declare global {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/** Native input types this field maps to. Anything richer has its own element. */
|
|
25
|
+
export type TTextInputType = 'text' | 'number' | 'date' | 'email' | 'url' | 'tel' | 'password';
|
|
26
|
+
|
|
27
|
+
/** Keyboard hint per type; `inputMode` on the host still wins. */
|
|
28
|
+
const defaultInputModes: Record<TTextInputType, string> = {
|
|
29
|
+
text: 'text',
|
|
30
|
+
number: 'decimal',
|
|
31
|
+
date: 'text',
|
|
32
|
+
email: 'email',
|
|
33
|
+
url: 'url',
|
|
34
|
+
tel: 'tel',
|
|
35
|
+
password: 'text',
|
|
36
|
+
};
|
|
37
|
+
|
|
24
38
|
@customElement('dees-input-text')
|
|
25
39
|
export class DeesInputText extends DeesInputBase {
|
|
26
40
|
public static demo = demoFunc;
|
|
27
41
|
public static demoGroups = ['Input'];
|
|
28
42
|
|
|
29
43
|
// INSTANCE
|
|
44
|
+
/**
|
|
45
|
+
* The native input type. `number` keeps the field's value numeric, so a form
|
|
46
|
+
* collects a number rather than a numeric string; every other type keeps the
|
|
47
|
+
* value a string. `password` masks the value; `isPasswordBool` additionally
|
|
48
|
+
* renders the reveal button.
|
|
49
|
+
*/
|
|
50
|
+
@property({
|
|
51
|
+
type: String,
|
|
52
|
+
reflect: true,
|
|
53
|
+
})
|
|
54
|
+
accessor type: TTextInputType = 'text';
|
|
55
|
+
|
|
56
|
+
/** A string for every type except `number`, which holds a number or null while empty. */
|
|
30
57
|
@property({
|
|
31
58
|
type: String,
|
|
32
59
|
reflect: true,
|
|
33
60
|
})
|
|
34
|
-
accessor value: string = '';
|
|
61
|
+
accessor value: string | number | null = '';
|
|
35
62
|
|
|
36
63
|
@property({
|
|
37
64
|
type: Boolean,
|
|
@@ -315,8 +342,8 @@ export class DeesInputText extends DeesInputBase {
|
|
|
315
342
|
<div class="maincontainer">
|
|
316
343
|
<input
|
|
317
344
|
class=${this.vintegrated ? '' : 'field-control'}
|
|
318
|
-
type="${this.
|
|
319
|
-
.value=${this.
|
|
345
|
+
type="${this.nativeInputType}"
|
|
346
|
+
.value=${this.valueAsText}
|
|
320
347
|
@input="${this.updateValue}"
|
|
321
348
|
.disabled=${this.disabled}
|
|
322
349
|
?required=${this.required}
|
|
@@ -324,7 +351,7 @@ export class DeesInputText extends DeesInputBase {
|
|
|
324
351
|
aria-invalid=${this.validationState === 'invalid' ? 'true' : 'false'}
|
|
325
352
|
aria-describedby="field-description field-validation"
|
|
326
353
|
placeholder=${this.placeholder || (this.label ? '' : 'Enter text…')}
|
|
327
|
-
inputmode=${this.inputMode || 'text'}
|
|
354
|
+
inputmode=${this.inputMode || defaultInputModes[this.type] || 'text'}
|
|
328
355
|
/>
|
|
329
356
|
${this.isPasswordBool
|
|
330
357
|
? html`
|
|
@@ -352,6 +379,39 @@ export class DeesInputText extends DeesInputBase {
|
|
|
352
379
|
`;
|
|
353
380
|
}
|
|
354
381
|
|
|
382
|
+
/** The type handed to the native input; masking wins over the declared type. */
|
|
383
|
+
private get nativeInputType(): string {
|
|
384
|
+
if (this.isPasswordBool || this.type === 'password') {
|
|
385
|
+
return this.showPasswordBool ? 'text' : 'password';
|
|
386
|
+
}
|
|
387
|
+
return this.type;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** The value as text, for validators, the context menu and the native element. */
|
|
391
|
+
private get valueAsText(): string {
|
|
392
|
+
return this.value === null || this.value === undefined ? '' : String(this.value);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Keeps `value` in the shape the current type promises, whatever a host
|
|
397
|
+
* binds: a number (or null while empty) for `number`, a string otherwise.
|
|
398
|
+
*/
|
|
399
|
+
private normalizeValue(valueArg: string | number | null | undefined): string | number | null {
|
|
400
|
+
if (this.type !== 'number') {
|
|
401
|
+
return valueArg === null || valueArg === undefined ? '' : String(valueArg);
|
|
402
|
+
}
|
|
403
|
+
if (valueArg === null || valueArg === undefined || valueArg === '') return null;
|
|
404
|
+
const parsed = typeof valueArg === 'number' ? valueArg : Number(valueArg);
|
|
405
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public willUpdate(changedProperties: Map<PropertyKey, unknown>): void {
|
|
409
|
+
super.willUpdate(changedProperties);
|
|
410
|
+
if (!changedProperties.has('value') && !changedProperties.has('type')) return;
|
|
411
|
+
const normalized = this.normalizeValue(this.value);
|
|
412
|
+
if (normalized !== this.value) this.value = normalized;
|
|
413
|
+
}
|
|
414
|
+
|
|
355
415
|
/**
|
|
356
416
|
* `autocomplete` is applied imperatively rather than bound in the template so that the
|
|
357
417
|
* default renders no attribute at all. Binding it would emit `autocomplete=""` on every
|
|
@@ -372,8 +432,8 @@ export class DeesInputText extends DeesInputBase {
|
|
|
372
432
|
}
|
|
373
433
|
|
|
374
434
|
firstUpdated() {
|
|
375
|
-
if (this.validationFunction && this.value) {
|
|
376
|
-
const result = this.validationFunction(this.
|
|
435
|
+
if (this.validationFunction && this.value !== null && this.value !== '') {
|
|
436
|
+
const result = this.validationFunction(this.valueAsText);
|
|
377
437
|
this.validationState = result.valid ? 'valid' : 'invalid';
|
|
378
438
|
this.validationText = result.message || '';
|
|
379
439
|
if (result.valid) {
|
|
@@ -387,10 +447,10 @@ export class DeesInputText extends DeesInputBase {
|
|
|
387
447
|
}
|
|
388
448
|
|
|
389
449
|
public async updateValue(eventArg: Event) {
|
|
390
|
-
const target
|
|
391
|
-
this.value = target.value;
|
|
450
|
+
const target = eventArg.target as HTMLInputElement;
|
|
451
|
+
this.value = this.normalizeValue(target.value);
|
|
392
452
|
if (this.validationFunction) {
|
|
393
|
-
const result = this.validationFunction(this.
|
|
453
|
+
const result = this.validationFunction(this.valueAsText);
|
|
394
454
|
this.validationState = result.valid ? 'valid' : 'invalid';
|
|
395
455
|
this.validationText = result.message || '';
|
|
396
456
|
if (result.valid) {
|
|
@@ -411,16 +471,16 @@ export class DeesInputText extends DeesInputBase {
|
|
|
411
471
|
public getContextMenuItems() {
|
|
412
472
|
return textInputContextMenuItems({
|
|
413
473
|
input: this.shadowRoot!.querySelector('input')!, disabled: this.disabled,
|
|
414
|
-
commit: value => { this.value = value; this.changeSubject.next(this); },
|
|
474
|
+
commit: value => { this.value = this.normalizeValue(value); this.changeSubject.next(this); },
|
|
415
475
|
});
|
|
416
476
|
}
|
|
417
477
|
|
|
418
|
-
public getValue(): string {
|
|
478
|
+
public getValue(): string | number | null {
|
|
419
479
|
return this.value;
|
|
420
480
|
}
|
|
421
481
|
|
|
422
|
-
public setValue(value: string): void {
|
|
423
|
-
this.value = value;
|
|
482
|
+
public setValue(value: string | number | null): void {
|
|
483
|
+
this.value = this.normalizeValue(value);
|
|
424
484
|
}
|
|
425
485
|
|
|
426
486
|
public async togglePasswordView() {
|
|
@@ -12,7 +12,6 @@ export * from './dees-input-phone/index.js';
|
|
|
12
12
|
export * from './dees-input-quantityselector/index.js';
|
|
13
13
|
export * from './dees-input-radiogroup/index.js';
|
|
14
14
|
export * from './dees-input-richtext/index.js';
|
|
15
|
-
export * from './dees-input-searchselect/index.js';
|
|
16
15
|
export * from './dees-input-tags/index.js';
|
|
17
16
|
export * from './dees-input-text/index.js';
|
|
18
17
|
export * from './dees-input-textarea/index.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../00plugins.js';
|
|
2
2
|
import { zIndexRegistry } from '../../00zindex.js';
|
|
3
|
+
import { runAction } from '../../00actionerror.js';
|
|
3
4
|
|
|
4
5
|
import { demoFunc } from './dees-modal.demo.js';
|
|
5
6
|
import {
|
|
@@ -49,7 +50,7 @@ export class DeesModal extends DeesElement {
|
|
|
49
50
|
let modal: DeesModal;
|
|
50
51
|
const confirm = async () => {
|
|
51
52
|
if (modal.promptPending) return;
|
|
52
|
-
const value = input.value.trim();
|
|
53
|
+
const value = String(input.value ?? '').trim();
|
|
53
54
|
const error = value ? options.validate?.(value) : 'Enter a name.';
|
|
54
55
|
if (error) {
|
|
55
56
|
input.validationState = 'invalid'; input.validationText = error;
|
|
@@ -332,7 +333,7 @@ export class DeesModal extends DeesElement {
|
|
|
332
333
|
<dees-button class="modal-action" .text=${action.name}
|
|
333
334
|
.disabled=${this.promptPending}
|
|
334
335
|
.type=${index === this.menuOptions.length - 1 ? 'accent' : 'secondary'}
|
|
335
|
-
@clicked=${() =>
|
|
336
|
+
@clicked=${() => this.runMenuAction(action)}></dees-button>
|
|
336
337
|
`)}
|
|
337
338
|
</div>
|
|
338
339
|
` : ''}
|
|
@@ -385,4 +386,14 @@ export class DeesModal extends DeesElement {
|
|
|
385
386
|
private async handleHelp() {
|
|
386
387
|
if (this.onHelp) await this.onHelp();
|
|
387
388
|
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* A menu action is awaited so a rejected callback surfaces as a visible
|
|
392
|
+
* failure instead of an unhandled rejection. The error is announced on the
|
|
393
|
+
* modal through the cancelable `dees-action-error` event and, unless a
|
|
394
|
+
* listener cancels it, shown as a notification inside this dialog.
|
|
395
|
+
*/
|
|
396
|
+
private async runMenuAction(actionArg: plugins.tsclass.website.IMenuItem<DeesModal>) {
|
|
397
|
+
await runAction(this, { action: actionArg, source: 'modal-menu', ownerElement: this.dialog }, () => actionArg.action(this));
|
|
398
|
+
}
|
|
388
399
|
}
|
package/ts_web/elements/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './00actionerror.js';
|
|
1
2
|
export * from './00zindex.js';
|
|
2
3
|
export * from './00theme.js';
|
|
3
4
|
|
|
@@ -17,7 +18,3 @@ export * from './00group-runtime/index.js';
|
|
|
17
18
|
export * from './00group-simple/index.js';
|
|
18
19
|
export * from './00group-utility/index.js';
|
|
19
20
|
export * from './00group-workspace/index.js';
|
|
20
|
-
|
|
21
|
-
// Standalone Components
|
|
22
|
-
export * from './dees-search/index.js';
|
|
23
|
-
export * from './dees-tooltip/index.js';
|
package/dist_ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist_ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export {};
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVlcy1pbnB1dC1zZWFyY2hzZWxlY3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90c193ZWIvZWxlbWVudHMvMDBncm91cC1pbnB1dC9kZWVzLWlucHV0LXNlYXJjaHNlbGVjdC9kZWVzLWlucHV0LXNlYXJjaHNlbGVjdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-input-searchselect.js';
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export * from './dees-input-searchselect.js';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90c193ZWIvZWxlbWVudHMvMDBncm91cC1pbnB1dC9kZWVzLWlucHV0LXNlYXJjaHNlbGVjdC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDhCQUE4QixDQUFDIn0=
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-search.js';
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export * from './dees-search.js';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvZGVlcy1zZWFyY2gvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxrQkFBa0IsQ0FBQyJ9
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import {} from '@design.estate/dees-element';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVlcy10b29sdGlwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHNfd2ViL2VsZW1lbnRzL2RlZXMtdG9vbHRpcC9kZWVzLXRvb2x0aXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sNkJBQTZCLENBQUMifQ==
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-tooltip.js';
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export * from './dees-tooltip.js';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvZGVlcy10b29sdGlwL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsbUJBQW1CLENBQUMifQ==
|
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-input-searchselect.js';
|
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-search.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import {} from '@design.estate/dees-element';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-tooltip.js';
|