@design.estate/dees-catalog 9.7.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 +4164 -3800
- 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/dees-appui.js +6 -1
- 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-harness/dees-harness-composer/dees-harness-composer.js +14 -4
- package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.d.ts +4 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.js +52 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.d.ts +4 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.js +93 -20
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/renderers.js +7 -21
- package/dist_ts_web/elements/00group-harness/harness.helpers.d.ts +20 -0
- package/dist_ts_web/elements/00group-harness/harness.helpers.js +58 -1
- package/dist_ts_web/elements/00group-harness/interfaces.d.ts +6 -0
- package/dist_ts_web/elements/00group-harness/toolregistry.d.ts +8 -0
- package/dist_ts_web/elements/00group-harness/toolregistry.js +10 -2
- 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 +4 -4
- package/readme.md +73 -6
- 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/dees-appui.ts +4 -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-harness/dees-harness-composer/dees-harness-composer.ts +12 -3
- package/ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.ts +50 -0
- package/ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.ts +95 -23
- package/ts_web/elements/00group-harness/dees-harness-tool-card/renderers.ts +7 -20
- package/ts_web/elements/00group-harness/harness.helpers.ts +63 -0
- package/ts_web/elements/00group-harness/interfaces.ts +6 -0
- package/ts_web/elements/00group-harness/toolregistry.ts +22 -1
- 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
|
+
};
|
|
@@ -1134,6 +1134,10 @@ export class DeesAppui extends DeesElement {
|
|
|
1134
1134
|
|
|
1135
1135
|
// Resolve the initial route once; a deep link takes precedence over the default.
|
|
1136
1136
|
const initialRoute = this.viewRegistry.findByRoute(window.location.hash.slice(1));
|
|
1137
|
+
const initialViewId = initialRoute?.view.id ?? config.defaultView;
|
|
1138
|
+
// Mark the menu before it first renders: its first-tab auto-selection would otherwise
|
|
1139
|
+
// queue a navigation that races the initial route and can replace a deep link.
|
|
1140
|
+
if (initialViewId) this.setMainMenuSelection(initialViewId);
|
|
1137
1141
|
if (initialRoute) {
|
|
1138
1142
|
void this.queueNavigation(initialRoute.view.id, initialRoute.params, initialRoute.query, 'replace');
|
|
1139
1143
|
} else if (config.defaultView) {
|
|
@@ -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
|
}
|
|
@@ -264,7 +264,12 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
264
264
|
border: 0;
|
|
265
265
|
border-top: 1px solid var(--dees-color-border-subtle);
|
|
266
266
|
border-radius: 0;
|
|
267
|
-
|
|
267
|
+
/*
|
|
268
|
+
* The docked bar stays full-bleed — its background and top border belong to the whole
|
|
269
|
+
* column — while its content is inset to the transcript's measure, so the prompt lines up
|
|
270
|
+
* with the messages above it instead of floating as a narrower block.
|
|
271
|
+
*/
|
|
272
|
+
padding: 10px max(16px, calc((100% - var(--dees-harness-content-width, 820px)) / 2)) 12px;
|
|
268
273
|
}
|
|
269
274
|
|
|
270
275
|
:host([docked]) .composer.dragOver {
|
|
@@ -467,7 +472,7 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
467
472
|
this.value = '';
|
|
468
473
|
this.resetSuggestionInteraction();
|
|
469
474
|
this.emitInput('');
|
|
470
|
-
|
|
475
|
+
// Height is resized in updated(); measuring here would read the textarea before it rerenders.
|
|
471
476
|
}
|
|
472
477
|
|
|
473
478
|
public willUpdate(changedProperties: Map<PropertyKey, unknown>): void {
|
|
@@ -481,6 +486,10 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
481
486
|
this.attachmentGeneration++;
|
|
482
487
|
if (!this.attachmentsEnabled || this.disabled) this.handleDragLeave();
|
|
483
488
|
}
|
|
489
|
+
// The grown height is an inline style, so it has to be recomputed for every value change
|
|
490
|
+
// regardless of origin. A host that clears its bound value after send would otherwise leave
|
|
491
|
+
// the composer stuck at the height of the message it just sent.
|
|
492
|
+
if (changedProperties.has('value')) this.autogrow();
|
|
484
493
|
this.syncTextareaSuggestionAria();
|
|
485
494
|
}
|
|
486
495
|
|
|
@@ -714,7 +723,7 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
714
723
|
this.value = (event.currentTarget as HTMLTextAreaElement).value;
|
|
715
724
|
this.resetSuggestionInteraction();
|
|
716
725
|
this.emitInput(this.value);
|
|
717
|
-
|
|
726
|
+
// updated() grows the textarea for every value change, so typing needs no separate call.
|
|
718
727
|
};
|
|
719
728
|
|
|
720
729
|
private handleKeydown = (event: KeyboardEvent): void => {
|
package/ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.ts
CHANGED
|
@@ -325,8 +325,42 @@ export class DeesHarnessMessageList extends DeesElement {
|
|
|
325
325
|
transform: translate3d(0, var(--harness-virtual-offset), 0);
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
+
/*
|
|
329
|
+
* Prose is held to a readable measure while wide payloads keep the whole pane. Applied to
|
|
330
|
+
* the transcript rows themselves because a message host clips on both axes, so nothing
|
|
331
|
+
* nested inside one can bleed back out.
|
|
332
|
+
*/
|
|
333
|
+
.stack > *,
|
|
334
|
+
.virtualItem {
|
|
335
|
+
width: min(var(--dees-harness-content-width, 820px), 100%);
|
|
336
|
+
margin-inline: auto;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.stack > [data-tool-layout='full-row'],
|
|
340
|
+
.stack > [data-tool-layout='subtask'],
|
|
341
|
+
.virtualItem[data-tool-layout='full-row'],
|
|
342
|
+
.virtualItem[data-tool-layout='subtask'] {
|
|
343
|
+
width: 100%;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/*
|
|
347
|
+
* One or two subagents are read like any other content, so they stay on the measure. The
|
|
348
|
+
* pane is only worth taking once three or more cards have to sit side by side.
|
|
349
|
+
*/
|
|
350
|
+
.stack > [data-tool-layout='subtask'][data-tool-count='1'],
|
|
351
|
+
.stack > [data-tool-layout='subtask'][data-tool-count='2'],
|
|
352
|
+
.virtualItem[data-tool-layout='subtask'][data-tool-count='1'],
|
|
353
|
+
.virtualItem[data-tool-layout='subtask'][data-tool-count='2'] {
|
|
354
|
+
width: min(var(--dees-harness-content-width, 820px), 100%);
|
|
355
|
+
}
|
|
356
|
+
|
|
328
357
|
.toolGroup {
|
|
329
358
|
min-width: 0;
|
|
359
|
+
/*
|
|
360
|
+
* Column packing has to measure the row it happens in, not the scroller, now that a
|
|
361
|
+
* compact row is narrower than the pane.
|
|
362
|
+
*/
|
|
363
|
+
container: harness-transcript / inline-size;
|
|
330
364
|
}
|
|
331
365
|
|
|
332
366
|
.toolGrid {
|
|
@@ -882,6 +916,8 @@ export class DeesHarnessMessageList extends DeesElement {
|
|
|
882
916
|
<article
|
|
883
917
|
class="virtualItem"
|
|
884
918
|
data-virtual-key=${entry.item.key}
|
|
919
|
+
data-tool-layout=${this.timelineItemLayout(entry.item)}
|
|
920
|
+
data-tool-count=${this.timelineItemCount(entry.item)}
|
|
885
921
|
aria-posinset=${entry.index + 1}
|
|
886
922
|
aria-setsize=${this.timelineRenderItems.length}
|
|
887
923
|
style=${`--harness-virtual-offset: ${this.virtualOffsets[entry.index] ?? 0}px;`}
|
|
@@ -906,6 +942,19 @@ export class DeesHarnessMessageList extends DeesElement {
|
|
|
906
942
|
return indexes.sort((left, right) => left - right);
|
|
907
943
|
}
|
|
908
944
|
|
|
945
|
+
/** Mirrors the row's layout onto the virtual wrapper so width rules match the flowed stack. */
|
|
946
|
+
private timelineItemLayout(itemArg: TTimelineRenderItem): string {
|
|
947
|
+
if (itemArg.kind === 'tool-group') return itemArg.layout;
|
|
948
|
+
if (itemArg.kind === 'message') return itemArg.toolLayout ?? 'message';
|
|
949
|
+
return itemArg.kind;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/** Group size, mirrored onto the virtual wrapper so width rules match the flowed stack. */
|
|
953
|
+
private timelineItemCount(itemArg: TTimelineRenderItem): number {
|
|
954
|
+
if (itemArg.kind !== 'tool-group') return 1;
|
|
955
|
+
return itemArg.messages.length >= 4 ? 4 : itemArg.messages.length;
|
|
956
|
+
}
|
|
957
|
+
|
|
909
958
|
private renderTimelineItem(itemArg: TTimelineRenderItem, logicalIndexArg: number): TemplateResult {
|
|
910
959
|
if (itemArg.kind === 'tool-group') return this.renderToolGroup(itemArg);
|
|
911
960
|
if (itemArg.kind === 'message') return this.renderMessage(itemArg, itemArg.key);
|
|
@@ -978,6 +1027,7 @@ export class DeesHarnessMessageList extends DeesElement {
|
|
|
978
1027
|
data-timeline-key=${groupArg.key}
|
|
979
1028
|
data-tool-group-id=${groupArg.key}
|
|
980
1029
|
data-tool-layout=${groupArg.layout}
|
|
1030
|
+
data-tool-count=${count >= 4 ? 4 : count}
|
|
981
1031
|
>
|
|
982
1032
|
<div class=${`toolGrid ${countClass}`}>
|
|
983
1033
|
${repeat(
|