@ni/ok-components 1.12.4 → 1.14.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/all-components-bundle.js +1037 -458
- package/dist/all-components-bundle.js.map +1 -1
- package/dist/all-components-bundle.min.js +4331 -4089
- package/dist/all-components-bundle.min.js.map +1 -1
- package/dist/custom-elements.json +406 -0
- package/dist/custom-elements.md +66 -0
- package/dist/esm/fv/all-fv.d.ts +2 -0
- package/dist/esm/fv/all-fv.js +2 -0
- package/dist/esm/fv/all-fv.js.map +1 -1
- package/dist/esm/fv/filter-bar/index.d.ts +30 -0
- package/dist/esm/fv/filter-bar/index.js +120 -0
- package/dist/esm/fv/filter-bar/index.js.map +1 -0
- package/dist/esm/fv/filter-bar/styles.d.ts +1 -0
- package/dist/esm/fv/filter-bar/styles.js +119 -0
- package/dist/esm/fv/filter-bar/styles.js.map +1 -0
- package/dist/esm/fv/filter-bar/template.d.ts +2 -0
- package/dist/esm/fv/filter-bar/template.js +34 -0
- package/dist/esm/fv/filter-bar/template.js.map +1 -0
- package/dist/esm/fv/filter-bar/types.d.ts +53 -0
- package/dist/esm/fv/filter-bar/types.js +2 -0
- package/dist/esm/fv/filter-bar/types.js.map +1 -0
- package/dist/esm/fv/toolbar/index.d.ts +36 -0
- package/dist/esm/fv/toolbar/index.js +253 -0
- package/dist/esm/fv/toolbar/index.js.map +1 -0
- package/dist/esm/fv/toolbar/styles.d.ts +1 -0
- package/dist/esm/fv/toolbar/styles.js +39 -0
- package/dist/esm/fv/toolbar/styles.js.map +1 -0
- package/dist/esm/fv/toolbar/template.d.ts +4 -0
- package/dist/esm/fv/toolbar/template.js +40 -0
- package/dist/esm/fv/toolbar/template.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/fv/filter-bar/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { attr, observable, volatile } from '@ni/fast-element';\nimport { DesignSystem, FoundationElement } from '@ni/fast-foundation';\nimport { styles } from './styles';\nimport { template } from './template';\nimport type {\n FvFilterBarDefinition,\n FvFilterBarEventDetail,\n FvFilterBarQueryDocument,\n FvFilterBarQueryNode,\n FvFilterBarToken\n} from './types';\n\nfunction isRecord(value: unknown): value is { [key: string]: unknown } {\n return typeof value === 'object' && value !== null;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ok-fv-filter-bar': FvFilterBar;\n }\n}\n\n/** Displays an active query and dispatches edit and clear actions. */\nexport class FvFilterBar extends FoundationElement {\n /** Text shown before the active query expression. */\n @attr\n public label = 'Filters';\n\n /** Prevents editing and clearing the displayed query. */\n @attr({ mode: 'boolean' })\n public disabled = false;\n\n /** Controls visibility of the clear button when a query is active. */\n @attr({ attribute: 'show-clear-button', mode: 'boolean' })\n public showClearButton = true;\n\n /** Query document shared with FvQueryBuilder. */\n @observable\n public query: FvFilterBarQueryDocument = { version: 1, root: null };\n\n /** Field and operator labels used to display the query. */\n @observable\n public definition?: FvFilterBarDefinition;\n\n /** @internal */\n public editButton!: HTMLButtonElement;\n\n public get expression(): string {\n return this.queryTokens.map(token => (token.kind === 'predicate'\n ? `${token.field} ${token.operator}${token.value}`\n : token.text)).join('');\n }\n\n /** @internal */\n @volatile\n public get queryTokens(): readonly FvFilterBarToken[] {\n return this.query.root ? this.formatNode(this.query.root, true) : [];\n }\n\n public handleEditClick(): void {\n this.dispatchEvent(new CustomEvent<FvFilterBarEventDetail>('edit-filter', {\n bubbles: true,\n composed: true,\n detail: { query: this.query }\n }));\n }\n\n public handleClearClick(): void {\n this.editButton.focus();\n this.dispatchEvent(new CustomEvent<FvFilterBarEventDetail>('clear-filter', {\n bubbles: true,\n composed: true,\n detail: { query: this.query }\n }));\n }\n\n private formatNode(node: FvFilterBarQueryNode, isRoot = false): FvFilterBarToken[] {\n if (node.kind === 'predicate') {\n const field = this.definition?.fields.find(candidate => candidate.id === node.fieldId);\n const operator = this.definition?.operators.find(candidate => candidate.id === node.operatorId);\n const valueSchema = operator?.operand?.kind === 'schema' ? operator.operand.schema : field?.valueSchema;\n return [{\n kind: 'predicate',\n field: field?.label ?? node.fieldId,\n operator: operator?.label ?? node.operatorId,\n value: node.value === undefined ? '' : ` ${this.formatValue(node.value, valueSchema)}`\n }];\n }\n\n const tokens: FvFilterBarToken[] = [];\n for (const child of node.children) {\n const childTokens = this.formatNode(child);\n if (childTokens.length > 0) {\n if (tokens.length > 0) {\n tokens.push({ kind: 'logic', text: ` ${node.combinator.toUpperCase()} ` });\n }\n tokens.push(...childTokens);\n }\n }\n return tokens.length > 0 && !isRoot\n ? [{ kind: 'group', text: '(' }, ...tokens, { kind: 'group', text: ')' }]\n : tokens;\n }\n\n private formatValue(value: unknown, schema: unknown): string {\n if (Array.isArray(value)) {\n return value.map(item => this.formatValue(item, schema)).join(', ');\n }\n\n if (isRecord(schema) && Array.isArray(schema.oneOf)) {\n for (const option of schema.oneOf) {\n if (isRecord(option) && option.const === value && typeof option.title === 'string') {\n return option.title;\n }\n }\n }\n\n if (value !== null && typeof value === 'object') {\n return JSON.stringify(value) ?? '';\n }\n if (typeof value === 'string') {\n return value;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return value.toString();\n }\n return 'null';\n }\n}\n\nconst okFvFilterBar = FvFilterBar.compose({\n baseName: 'fv-filter-bar',\n template,\n styles\n});\n\nDesignSystem.getOrCreate().withPrefix('ok').register(okFvFilterBar());\nexport const fvFilterBarTag = 'ok-fv-filter-bar';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import("@ni/fast-element").ElementStyles;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { css } from '@ni/fast-element';
|
|
2
|
+
import { bodyDisabledFontColor, bodyFont, bodyFontColor, borderHoverColor, borderWidth, controlHeight, fillHoverColor, iconColor, iconSize, linkProminentFontColor, sectionBackgroundColor, smallPadding, standardPadding } from '@ni/nimble-components/dist/esm/theme-provider/design-tokens';
|
|
3
|
+
import { display } from '../../utilities/style/display';
|
|
4
|
+
export const styles = css `
|
|
5
|
+
@layer base, hover, focusVisible, disabled, top;
|
|
6
|
+
|
|
7
|
+
@layer base {
|
|
8
|
+
${display('flex')}
|
|
9
|
+
|
|
10
|
+
:host {
|
|
11
|
+
min-width: 0;
|
|
12
|
+
font: ${bodyFont};
|
|
13
|
+
color: ${bodyFontColor};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.filter-bar {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
min-width: 0;
|
|
20
|
+
max-width: 100%;
|
|
21
|
+
height: 30px;
|
|
22
|
+
padding: 0 ${standardPadding};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.filter-bar-edit,
|
|
26
|
+
.filter-bar-clear {
|
|
27
|
+
-webkit-appearance: none;
|
|
28
|
+
appearance: none;
|
|
29
|
+
display: inline-flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
min-width: 0;
|
|
32
|
+
padding: 0;
|
|
33
|
+
border: none;
|
|
34
|
+
border-radius: 2px;
|
|
35
|
+
background: transparent;
|
|
36
|
+
color: inherit;
|
|
37
|
+
font: inherit;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.filter-bar-edit {
|
|
42
|
+
flex: 0 1 auto;
|
|
43
|
+
height: 30px;
|
|
44
|
+
gap: calc(2 * ${smallPadding});
|
|
45
|
+
text-align: start;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.filter-bar-content {
|
|
49
|
+
min-width: 0;
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
text-overflow: ellipsis;
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.filter-bar-label {
|
|
56
|
+
margin-inline-end: 10px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.query-expression {
|
|
60
|
+
padding: 0 ${smallPadding};
|
|
61
|
+
background-color: ${sectionBackgroundColor};
|
|
62
|
+
border-radius: 50px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.logical-operator {
|
|
66
|
+
font-weight: 600;
|
|
67
|
+
color: ${linkProminentFontColor};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.compound-expression-container {
|
|
71
|
+
font-size: medium;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.filter-bar-icon {
|
|
75
|
+
flex: 0 0 auto;
|
|
76
|
+
width: ${iconSize};
|
|
77
|
+
height: ${iconSize};
|
|
78
|
+
${iconColor.cssCustomProperty}: currentColor;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.filter-bar-clear {
|
|
82
|
+
${controlHeight.cssCustomProperty}: 24px;
|
|
83
|
+
flex: 0 0 auto;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
width: ${controlHeight};
|
|
86
|
+
height: ${controlHeight};
|
|
87
|
+
margin-inline-start: calc(2 * ${smallPadding});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@layer hover {
|
|
92
|
+
.filter-bar-clear:hover {
|
|
93
|
+
background: ${fillHoverColor};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@layer focusVisible {
|
|
98
|
+
.filter-bar-edit:focus-visible,
|
|
99
|
+
.filter-bar-clear:focus-visible {
|
|
100
|
+
outline: ${borderWidth} solid ${borderHoverColor};
|
|
101
|
+
outline-offset: -1px;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@layer disabled {
|
|
106
|
+
.filter-bar-edit:disabled,
|
|
107
|
+
.filter-bar-clear:disabled {
|
|
108
|
+
color: ${bodyDisabledFontColor};
|
|
109
|
+
cursor: not-allowed;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
:host([disabled]) .logical-operator {
|
|
113
|
+
color: inherit;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@layer top {}
|
|
118
|
+
`;
|
|
119
|
+
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../../src/fv/filter-bar/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EACH,qBAAqB,EACrB,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,cAAc,EACd,SAAS,EACT,QAAQ,EACR,sBAAsB,EACtB,sBAAsB,EACtB,YAAY,EACZ,eAAe,EAClB,MAAM,6DAA6D,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAExD,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,CAAA;;;;UAIf,OAAO,CAAC,MAAM,CAAC;;;;oBAIL,QAAQ;qBACP,aAAa;;;;;;;;;yBAST,eAAe;;;;;;;;;;;;;;;;;;;;;;4BAsBZ,YAAY;;;;;;;;;;;;;;;;yBAgBf,YAAY;gCACL,sBAAsB;;;;;;qBAMjC,sBAAsB;;;;;;;;;qBAStB,QAAQ;sBACP,QAAQ;cAChB,SAAS,CAAC,iBAAiB;;;;cAI3B,aAAa,CAAC,iBAAiB;;;qBAGxB,aAAa;sBACZ,aAAa;4CACS,YAAY;;;;;;0BAM9B,cAAc;;;;;;;uBAOjB,WAAW,UAAU,gBAAgB;;;;;;;;qBAQvC,qBAAqB;;;;;;;;;;CAUzC,CAAC","sourcesContent":["import { css } from '@ni/fast-element';\nimport {\n bodyDisabledFontColor,\n bodyFont,\n bodyFontColor,\n borderHoverColor,\n borderWidth,\n controlHeight,\n fillHoverColor,\n iconColor,\n iconSize,\n linkProminentFontColor,\n sectionBackgroundColor,\n smallPadding,\n standardPadding\n} from '@ni/nimble-components/dist/esm/theme-provider/design-tokens';\nimport { display } from '../../utilities/style/display';\n\nexport const styles = css`\n @layer base, hover, focusVisible, disabled, top;\n\n @layer base {\n ${display('flex')}\n\n :host {\n min-width: 0;\n font: ${bodyFont};\n color: ${bodyFontColor};\n }\n\n .filter-bar {\n display: flex;\n align-items: center;\n min-width: 0;\n max-width: 100%;\n height: 30px;\n padding: 0 ${standardPadding};\n }\n\n .filter-bar-edit,\n .filter-bar-clear {\n -webkit-appearance: none;\n appearance: none;\n display: inline-flex;\n align-items: center;\n min-width: 0;\n padding: 0;\n border: none;\n border-radius: 2px;\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n }\n\n .filter-bar-edit {\n flex: 0 1 auto;\n height: 30px;\n gap: calc(2 * ${smallPadding});\n text-align: start;\n }\n\n .filter-bar-content {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .filter-bar-label {\n margin-inline-end: 10px;\n }\n\n .query-expression {\n padding: 0 ${smallPadding};\n background-color: ${sectionBackgroundColor};\n border-radius: 50px;\n }\n\n .logical-operator {\n font-weight: 600;\n color: ${linkProminentFontColor};\n }\n\n .compound-expression-container {\n font-size: medium;\n }\n\n .filter-bar-icon {\n flex: 0 0 auto;\n width: ${iconSize};\n height: ${iconSize};\n ${iconColor.cssCustomProperty}: currentColor;\n }\n\n .filter-bar-clear {\n ${controlHeight.cssCustomProperty}: 24px;\n flex: 0 0 auto;\n justify-content: center;\n width: ${controlHeight};\n height: ${controlHeight};\n margin-inline-start: calc(2 * ${smallPadding});\n }\n }\n\n @layer hover {\n .filter-bar-clear:hover {\n background: ${fillHoverColor};\n }\n }\n\n @layer focusVisible {\n .filter-bar-edit:focus-visible,\n .filter-bar-clear:focus-visible {\n outline: ${borderWidth} solid ${borderHoverColor};\n outline-offset: -1px;\n }\n }\n\n @layer disabled {\n .filter-bar-edit:disabled,\n .filter-bar-clear:disabled {\n color: ${bodyDisabledFontColor};\n cursor: not-allowed;\n }\n\n :host([disabled]) .logical-operator {\n color: inherit;\n }\n }\n\n @layer top {}\n`;\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { html, ref, repeat, when } from '@ni/fast-element';
|
|
2
|
+
import { iconFilterTag } from '@ni/nimble-components/dist/esm/icons/filter';
|
|
3
|
+
import { iconXmarkTag } from '@ni/nimble-components/dist/esm/icons/xmark';
|
|
4
|
+
const queryTokenTemplate = html `${when(x => x.kind === 'predicate', html `<span class="query-expression"><b>${x => (x.kind === 'predicate' ? x.field : '')}</b> ${x => (x.kind === 'predicate' ? x.operator : '')}<b>${x => (x.kind === 'predicate' ? x.value : '')}</b></span>`)}${when(x => x.kind === 'logic', html `<span class="logical-operator">${x => (x.kind === 'logic' ? x.text : '')}</span>`)}${when(x => x.kind === 'group', html `<span class="compound-expression-container">${x => (x.kind === 'group' ? x.text : '')}</span>`)}`;
|
|
5
|
+
export const template = html `
|
|
6
|
+
<div class="filter-bar" role="group" aria-label="${x => x.label}">
|
|
7
|
+
<button
|
|
8
|
+
${ref('editButton')}
|
|
9
|
+
class="filter-bar-edit"
|
|
10
|
+
type="button"
|
|
11
|
+
aria-label="${x => `Edit filters: ${x.label}`}"
|
|
12
|
+
title="${x => x.expression}"
|
|
13
|
+
?disabled="${x => x.disabled}"
|
|
14
|
+
@click="${x => x.handleEditClick()}"
|
|
15
|
+
>
|
|
16
|
+
<${iconFilterTag} class="filter-bar-icon" aria-hidden="true"></${iconFilterTag}>
|
|
17
|
+
<span class="filter-bar-content">
|
|
18
|
+
<span class="filter-bar-label">${x => x.label}</span>${when(x => x.expression.length > 0, html `<span class="filter-bar-expression">${repeat(x => x.queryTokens, queryTokenTemplate)}</span>`)}
|
|
19
|
+
</span>
|
|
20
|
+
</button>
|
|
21
|
+
${when(x => x.showClearButton && x.expression.length > 0, html `
|
|
22
|
+
<button
|
|
23
|
+
class="filter-bar-clear"
|
|
24
|
+
type="button"
|
|
25
|
+
aria-label="Clear filters"
|
|
26
|
+
?disabled="${x => x.disabled}"
|
|
27
|
+
@click="${x => x.handleClearClick()}"
|
|
28
|
+
>
|
|
29
|
+
<${iconXmarkTag} class="filter-bar-icon" aria-hidden="true"></${iconXmarkTag}>
|
|
30
|
+
</button>
|
|
31
|
+
`)}
|
|
32
|
+
</div>
|
|
33
|
+
`;
|
|
34
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../../src/fv/filter-bar/template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAI1E,MAAM,kBAAkB,GAAG,IAAI,CAAkB,GAAG,IAAI,CAChD,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,EAC3B,IAAI,CAAkB,qCAAqC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAChO,GAAG,IAAI,CACJ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,EACvB,IAAI,CAAkB,kCAAkC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAC3G,GAAG,IAAI,CACJ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,EACvB,IAAI,CAAkB,+CAA+C,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CACxH,EAAE,CAAC;AAER,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAa;uDACc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK;;cAErD,GAAG,CAAC,YAAY,CAAC;;;0BAGL,CAAC,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,KAAK,EAAE;qBACpC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU;yBACb,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ;sBAClB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE;;eAE/B,aAAa,iDAAiD,aAAa;;iDAEzC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,UAAU,IAAI,CACvD,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAC5B,IAAI,CAAa,uCAAuC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,kBAAkB,CAAC,SAAS,CAClH;;;UAGP,IAAI,CACF,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EACjD,IAAI,CAAa;;;;;iCAKI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ;8BAClB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,EAAE;;uBAEhC,YAAY,iDAAiD,YAAY;;aAEnF,CACJ;;CAER,CAAC","sourcesContent":["import { html, ref, repeat, when } from '@ni/fast-element';\nimport { iconFilterTag } from '@ni/nimble-components/dist/esm/icons/filter';\nimport { iconXmarkTag } from '@ni/nimble-components/dist/esm/icons/xmark';\nimport type { FvFilterBar } from '.';\nimport type { FvFilterBarToken } from './types';\n\nconst queryTokenTemplate = html<FvFilterBarToken>`${when(\n x => x.kind === 'predicate',\n html<FvFilterBarToken>`<span class=\"query-expression\"><b>${x => (x.kind === 'predicate' ? x.field : '')}</b> ${x => (x.kind === 'predicate' ? x.operator : '')}<b>${x => (x.kind === 'predicate' ? x.value : '')}</b></span>`\n )}${when(\n x => x.kind === 'logic',\n html<FvFilterBarToken>`<span class=\"logical-operator\">${x => (x.kind === 'logic' ? x.text : '')}</span>`\n )}${when(\n x => x.kind === 'group',\n html<FvFilterBarToken>`<span class=\"compound-expression-container\">${x => (x.kind === 'group' ? x.text : '')}</span>`\n )}`;\n\nexport const template = html<FvFilterBar>`\n <div class=\"filter-bar\" role=\"group\" aria-label=\"${x => x.label}\">\n <button\n ${ref('editButton')}\n class=\"filter-bar-edit\"\n type=\"button\"\n aria-label=\"${x => `Edit filters: ${x.label}`}\"\n title=\"${x => x.expression}\"\n ?disabled=\"${x => x.disabled}\"\n @click=\"${x => x.handleEditClick()}\"\n >\n <${iconFilterTag} class=\"filter-bar-icon\" aria-hidden=\"true\"></${iconFilterTag}>\n <span class=\"filter-bar-content\">\n <span class=\"filter-bar-label\">${x => x.label}</span>${when(\n x => x.expression.length > 0,\n html<FvFilterBar>`<span class=\"filter-bar-expression\">${repeat(x => x.queryTokens, queryTokenTemplate)}</span>`\n )}\n </span>\n </button>\n ${when(\n x => x.showClearButton && x.expression.length > 0,\n html<FvFilterBar>`\n <button\n class=\"filter-bar-clear\"\n type=\"button\"\n aria-label=\"Clear filters\"\n ?disabled=\"${x => x.disabled}\"\n @click=\"${x => x.handleClearClick()}\"\n >\n <${iconXmarkTag} class=\"filter-bar-icon\" aria-hidden=\"true\"></${iconXmarkTag}>\n </button>\n `\n )}\n </div>\n`;\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Query document shape shared with FvQueryBuilder. */
|
|
2
|
+
export interface FvFilterBarPredicate {
|
|
3
|
+
readonly kind: 'predicate';
|
|
4
|
+
readonly fieldId: string;
|
|
5
|
+
readonly operatorId: string;
|
|
6
|
+
readonly value?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface FvFilterBarGroup {
|
|
9
|
+
readonly kind: 'group';
|
|
10
|
+
readonly combinator: 'and' | 'or';
|
|
11
|
+
readonly children: readonly FvFilterBarQueryNode[];
|
|
12
|
+
}
|
|
13
|
+
export type FvFilterBarQueryNode = FvFilterBarPredicate | FvFilterBarGroup;
|
|
14
|
+
export interface FvFilterBarQueryDocument {
|
|
15
|
+
readonly version: 1;
|
|
16
|
+
readonly root: FvFilterBarQueryNode | null;
|
|
17
|
+
}
|
|
18
|
+
export interface FvFilterBarDefinition {
|
|
19
|
+
readonly fields: readonly {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly label: string;
|
|
22
|
+
readonly valueSchema?: unknown;
|
|
23
|
+
}[];
|
|
24
|
+
readonly operators: readonly {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
readonly label: string;
|
|
27
|
+
readonly operand?: {
|
|
28
|
+
readonly kind: 'none' | 'field-value';
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: 'field-value-list';
|
|
31
|
+
readonly minItems?: number;
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: 'range';
|
|
34
|
+
readonly endpointSchema: unknown;
|
|
35
|
+
} | {
|
|
36
|
+
readonly kind: 'schema';
|
|
37
|
+
readonly schema: unknown;
|
|
38
|
+
};
|
|
39
|
+
}[];
|
|
40
|
+
}
|
|
41
|
+
export interface FvFilterBarEventDetail {
|
|
42
|
+
readonly query: FvFilterBarQueryDocument;
|
|
43
|
+
}
|
|
44
|
+
/** @internal */
|
|
45
|
+
export type FvFilterBarToken = {
|
|
46
|
+
readonly kind: 'predicate';
|
|
47
|
+
readonly field: string;
|
|
48
|
+
readonly operator: string;
|
|
49
|
+
readonly value: string;
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: 'logic' | 'group';
|
|
52
|
+
readonly text: string;
|
|
53
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/fv/filter-bar/types.ts"],"names":[],"mappings":"","sourcesContent":["/** Query document shape shared with FvQueryBuilder. */\nexport interface FvFilterBarPredicate {\n readonly kind: 'predicate';\n readonly fieldId: string;\n readonly operatorId: string;\n readonly value?: unknown;\n}\n\nexport interface FvFilterBarGroup {\n readonly kind: 'group';\n readonly combinator: 'and' | 'or';\n readonly children: readonly FvFilterBarQueryNode[];\n}\n\nexport type FvFilterBarQueryNode = FvFilterBarPredicate | FvFilterBarGroup;\n\nexport interface FvFilterBarQueryDocument {\n readonly version: 1;\n readonly root: FvFilterBarQueryNode | null;\n}\n\nexport interface FvFilterBarDefinition {\n readonly fields: readonly {\n readonly id: string,\n readonly label: string,\n readonly valueSchema?: unknown\n }[];\n readonly operators: readonly {\n readonly id: string,\n readonly label: string,\n readonly operand?: | { readonly kind: 'none' | 'field-value' }\n | { readonly kind: 'field-value-list', readonly minItems?: number }\n | { readonly kind: 'range', readonly endpointSchema: unknown }\n | { readonly kind: 'schema', readonly schema: unknown }\n }[];\n}\n\nexport interface FvFilterBarEventDetail {\n readonly query: FvFilterBarQueryDocument;\n}\n\n/** @internal */\nexport type FvFilterBarToken = | {\n readonly kind: 'predicate',\n readonly field: string,\n readonly operator: string,\n readonly value: string\n} | {\n readonly kind: 'logic' | 'group',\n readonly text: string\n};\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Toolbar } from '@ni/nimble-components/dist/esm/toolbar';
|
|
2
|
+
import type { MenuButton } from '@ni/nimble-components/dist/esm/menu-button';
|
|
3
|
+
import type { Menu } from '@ni/nimble-components/dist/esm/menu';
|
|
4
|
+
declare global {
|
|
5
|
+
interface HTMLElementTagNameMap {
|
|
6
|
+
'ok-fv-toolbar': FvToolbar;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A Nimble toolbar that automatically collapses declaratively configured actions.
|
|
11
|
+
*/
|
|
12
|
+
export declare class FvToolbar extends Toolbar {
|
|
13
|
+
overflowLabel: string;
|
|
14
|
+
/** @internal */
|
|
15
|
+
hasOverflow: boolean;
|
|
16
|
+
/** @internal */
|
|
17
|
+
overflowButton: MenuButton;
|
|
18
|
+
/** @internal */
|
|
19
|
+
overflowMenu: Menu;
|
|
20
|
+
private readonly items;
|
|
21
|
+
private readonly resizeObserver;
|
|
22
|
+
private readonly mutationObserver;
|
|
23
|
+
private readonly observedChildren;
|
|
24
|
+
private frame;
|
|
25
|
+
connectedCallback(): void;
|
|
26
|
+
disconnectedCallback(): void;
|
|
27
|
+
protected get allSlottedItems(): (HTMLElement | Node)[];
|
|
28
|
+
private observeMutations;
|
|
29
|
+
private readonly scheduleLayout;
|
|
30
|
+
private restoreItem;
|
|
31
|
+
private syncItems;
|
|
32
|
+
private updateLayout;
|
|
33
|
+
private showOverflow;
|
|
34
|
+
private fits;
|
|
35
|
+
}
|
|
36
|
+
export declare const fvToolbarTag = "ok-fv-toolbar";
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { attr, observable } from '@ni/fast-element';
|
|
3
|
+
import { DesignSystem } from '@ni/fast-foundation';
|
|
4
|
+
import { Toolbar } from '@ni/nimble-components/dist/esm/toolbar';
|
|
5
|
+
import { Button } from '@ni/nimble-components/dist/esm/button';
|
|
6
|
+
import { MenuItem, menuItemTag } from '@ni/nimble-components/dist/esm/menu-item';
|
|
7
|
+
import { styles } from './styles';
|
|
8
|
+
import { template } from './template';
|
|
9
|
+
/**
|
|
10
|
+
* A Nimble toolbar that automatically collapses declaratively configured actions.
|
|
11
|
+
*/
|
|
12
|
+
export class FvToolbar extends Toolbar {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.overflowLabel = 'More actions';
|
|
16
|
+
/** @internal */
|
|
17
|
+
this.hasOverflow = false;
|
|
18
|
+
this.items = new Map();
|
|
19
|
+
this.resizeObserver = new ResizeObserver(() => this.scheduleLayout());
|
|
20
|
+
this.mutationObserver = new MutationObserver(records => {
|
|
21
|
+
for (const record of records) {
|
|
22
|
+
const item = record.target instanceof Button ? this.items.get(record.target) : undefined;
|
|
23
|
+
if (item && record.attributeName === 'hidden') {
|
|
24
|
+
item.hidden = item.button.hidden;
|
|
25
|
+
}
|
|
26
|
+
if (item && record.attributeName === 'content-hidden') {
|
|
27
|
+
item.contentHidden = item.button.contentHidden;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
this.scheduleLayout();
|
|
31
|
+
});
|
|
32
|
+
this.observedChildren = new Set();
|
|
33
|
+
this.frame = 0;
|
|
34
|
+
this.scheduleLayout = () => {
|
|
35
|
+
if (!this.isConnected || this.frame !== 0) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.frame = requestAnimationFrame(() => {
|
|
39
|
+
this.frame = 0;
|
|
40
|
+
this.updateLayout();
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
connectedCallback() {
|
|
45
|
+
super.connectedCallback();
|
|
46
|
+
this.resizeObserver.observe(this);
|
|
47
|
+
this.observeMutations();
|
|
48
|
+
this.ownerDocument.fonts.addEventListener('loadingdone', this.scheduleLayout);
|
|
49
|
+
this.scheduleLayout();
|
|
50
|
+
}
|
|
51
|
+
disconnectedCallback() {
|
|
52
|
+
this.resizeObserver.disconnect();
|
|
53
|
+
this.mutationObserver.disconnect();
|
|
54
|
+
this.ownerDocument.fonts.removeEventListener('loadingdone', this.scheduleLayout);
|
|
55
|
+
cancelAnimationFrame(this.frame);
|
|
56
|
+
this.frame = 0;
|
|
57
|
+
for (const item of this.items.values()) {
|
|
58
|
+
this.restoreItem(item);
|
|
59
|
+
}
|
|
60
|
+
this.items.clear();
|
|
61
|
+
this.observedChildren.clear();
|
|
62
|
+
super.disconnectedCallback();
|
|
63
|
+
}
|
|
64
|
+
get allSlottedItems() {
|
|
65
|
+
return [
|
|
66
|
+
...super.allSlottedItems,
|
|
67
|
+
...(this.hasOverflow ? [this.overflowButton] : [])
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
observeMutations() {
|
|
71
|
+
this.mutationObserver.observe(this, {
|
|
72
|
+
subtree: true,
|
|
73
|
+
childList: true,
|
|
74
|
+
characterData: true,
|
|
75
|
+
attributes: true,
|
|
76
|
+
attributeFilter: [
|
|
77
|
+
'data-responsive', 'data-priority', 'disabled', 'hidden',
|
|
78
|
+
'content-hidden', 'aria-label', 'slot', 'class', 'style'
|
|
79
|
+
]
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
restoreItem(item) {
|
|
83
|
+
item.button.hidden = item.hidden;
|
|
84
|
+
item.button.toggleAttribute('content-hidden', item.contentHidden);
|
|
85
|
+
item.menuItem.remove();
|
|
86
|
+
}
|
|
87
|
+
syncItems() {
|
|
88
|
+
const buttons = Array.from(this.children).filter((child) => child instanceof Button && child.hasAttribute('data-responsive'));
|
|
89
|
+
for (const [button, item] of this.items) {
|
|
90
|
+
if (!buttons.includes(button)) {
|
|
91
|
+
this.restoreItem(item);
|
|
92
|
+
this.items.delete(button);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const button of buttons) {
|
|
96
|
+
const value = button.getAttribute('data-responsive')?.trim() ?? '';
|
|
97
|
+
const tokens = value.split(/\s+/);
|
|
98
|
+
const stages = ['label', 'icon', 'overflow'].filter(stage => tokens.includes(stage));
|
|
99
|
+
const priority = Number(button.getAttribute('data-priority') ?? 0);
|
|
100
|
+
if (stages.length === 0 || tokens.some(token => !stages.some(stage => stage === token))
|
|
101
|
+
|| !Number.isFinite(priority)
|
|
102
|
+
|| (stages.includes('icon') && !button.querySelector(':scope > [slot="start"]'))) {
|
|
103
|
+
throw new Error('ok-fv-toolbar requires valid data-responsive stages, a finite data-priority, and a direct start-slot icon for icon-only actions.');
|
|
104
|
+
}
|
|
105
|
+
let item = this.items.get(button);
|
|
106
|
+
if (!item) {
|
|
107
|
+
const menuItem = this.ownerDocument.createElement(menuItemTag);
|
|
108
|
+
menuItem.addEventListener('change', () => {
|
|
109
|
+
if (!button.disabled) {
|
|
110
|
+
button.click();
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
item = {
|
|
114
|
+
button,
|
|
115
|
+
stages,
|
|
116
|
+
priority,
|
|
117
|
+
hidden: button.hidden,
|
|
118
|
+
contentHidden: button.contentHidden,
|
|
119
|
+
menuItem
|
|
120
|
+
};
|
|
121
|
+
this.items.set(button, item);
|
|
122
|
+
}
|
|
123
|
+
item.stages = stages;
|
|
124
|
+
item.priority = priority;
|
|
125
|
+
item.menuItem.disabled = button.disabled;
|
|
126
|
+
const label = (button.getAttribute('aria-label')
|
|
127
|
+
?? Array.from(button.childNodes)
|
|
128
|
+
.filter(node => node.nodeType === Node.TEXT_NODE
|
|
129
|
+
|| (node instanceof Element && !node.hasAttribute('slot')))
|
|
130
|
+
.map(node => node.textContent).join('')).trim();
|
|
131
|
+
if (!label) {
|
|
132
|
+
throw new Error('ok-fv-toolbar responsive actions require a text label or aria-label.');
|
|
133
|
+
}
|
|
134
|
+
if (item.menuItem.textContent !== label) {
|
|
135
|
+
item.menuItem.textContent = label;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return buttons.map(button => this.items.get(button));
|
|
139
|
+
}
|
|
140
|
+
updateLayout() {
|
|
141
|
+
// Ignore our own presentation mutations, not application changes.
|
|
142
|
+
this.mutationObserver.disconnect();
|
|
143
|
+
try {
|
|
144
|
+
const menuFocus = this.shadowRoot?.activeElement;
|
|
145
|
+
const menuHadFocus = menuFocus && this.overflowButton.contains(menuFocus);
|
|
146
|
+
const previousMenuItems = Array.from(this.overflowMenu.children);
|
|
147
|
+
const items = this.syncItems();
|
|
148
|
+
const root = this.getRootNode();
|
|
149
|
+
const activeElement = root instanceof ShadowRoot ? root.activeElement : this.ownerDocument.activeElement;
|
|
150
|
+
const focusedItem = items.find(item => item.button === activeElement);
|
|
151
|
+
for (const child of this.observedChildren) {
|
|
152
|
+
if (child.parentElement !== this) {
|
|
153
|
+
this.resizeObserver.unobserve(child);
|
|
154
|
+
this.observedChildren.delete(child);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (const child of this.children) {
|
|
158
|
+
if (!this.observedChildren.has(child)) {
|
|
159
|
+
this.resizeObserver.observe(child);
|
|
160
|
+
this.observedChildren.add(child);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const overflow = new Set();
|
|
164
|
+
for (const item of items) {
|
|
165
|
+
item.button.hidden = item.hidden;
|
|
166
|
+
item.button.toggleAttribute('content-hidden', item.stages[0] === 'icon');
|
|
167
|
+
if (!item.hidden && item.stages[0] === 'overflow') {
|
|
168
|
+
overflow.add(item);
|
|
169
|
+
item.button.hidden = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
this.showOverflow(overflow.size > 0);
|
|
173
|
+
const ordered = items.filter(item => !item.hidden).reverse()
|
|
174
|
+
.sort((a, b) => a.priority - b.priority);
|
|
175
|
+
// ponytail: measure each transition; cache widths only if large toolbars make this costly.
|
|
176
|
+
for (const stage of ['icon', 'overflow']) {
|
|
177
|
+
for (const item of ordered) {
|
|
178
|
+
if (this.fits()) {
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
if (overflow.has(item) || !item.stages.includes(stage)) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (stage === 'icon') {
|
|
185
|
+
item.button.toggleAttribute('content-hidden', true);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
item.button.hidden = true;
|
|
189
|
+
overflow.add(item);
|
|
190
|
+
this.showOverflow(true);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const overflowItems = items.filter(item => overflow.has(item));
|
|
195
|
+
const nextMenuItems = overflowItems.map(item => item.menuItem);
|
|
196
|
+
if (nextMenuItems.length !== previousMenuItems.length
|
|
197
|
+
|| nextMenuItems.some((item, index) => previousMenuItems[index] !== item)) {
|
|
198
|
+
this.overflowButton.open = false;
|
|
199
|
+
this.overflowMenu.replaceChildren(...nextMenuItems);
|
|
200
|
+
}
|
|
201
|
+
if (!this.hasOverflow) {
|
|
202
|
+
this.overflowButton.open = false;
|
|
203
|
+
}
|
|
204
|
+
this.overflowButton.hidden = !this.hasOverflow;
|
|
205
|
+
this.overflowButton.classList.remove('unreserved');
|
|
206
|
+
this.reduceFocusableElements();
|
|
207
|
+
if (focusedItem && overflow.has(focusedItem)) {
|
|
208
|
+
this.overflowButton.focus();
|
|
209
|
+
}
|
|
210
|
+
else if (menuHadFocus) {
|
|
211
|
+
if (this.hasOverflow) {
|
|
212
|
+
if (this.overflowButton.open && menuFocus instanceof HTMLElement && menuFocus.isConnected) {
|
|
213
|
+
menuFocus.focus();
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
this.overflowButton.focus();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
Array.from(this.querySelectorAll('[tabindex="0"]'))
|
|
221
|
+
.find(control => !control.closest('[hidden], [disabled]'))?.focus();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
this.observeMutations();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
showOverflow(show) {
|
|
230
|
+
this.hasOverflow = show;
|
|
231
|
+
// Remove the trigger from width calculations without hiding its focused menu.
|
|
232
|
+
this.overflowButton.classList.toggle('unreserved', !show);
|
|
233
|
+
this.overflowButton.hidden = false;
|
|
234
|
+
}
|
|
235
|
+
fits() {
|
|
236
|
+
const region = this.shadowRoot.querySelector('.positioning-region');
|
|
237
|
+
return region.scrollWidth <= region.clientWidth;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
__decorate([
|
|
241
|
+
attr({ attribute: 'overflow-label' })
|
|
242
|
+
], FvToolbar.prototype, "overflowLabel", void 0);
|
|
243
|
+
__decorate([
|
|
244
|
+
observable
|
|
245
|
+
], FvToolbar.prototype, "hasOverflow", void 0);
|
|
246
|
+
const okFvToolbar = FvToolbar.compose({
|
|
247
|
+
baseName: 'fv-toolbar',
|
|
248
|
+
template,
|
|
249
|
+
styles
|
|
250
|
+
});
|
|
251
|
+
DesignSystem.getOrCreate().withPrefix('ok').register(okFvToolbar());
|
|
252
|
+
export const fvToolbarTag = 'ok-fv-toolbar';
|
|
253
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/fv/toolbar/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { attr, observable } from '@ni/fast-element';\nimport { DesignSystem } from '@ni/fast-foundation';\nimport { Toolbar } from '@ni/nimble-components/dist/esm/toolbar';\nimport { Button } from '@ni/nimble-components/dist/esm/button';\nimport type { MenuButton } from '@ni/nimble-components/dist/esm/menu-button';\nimport { MenuItem, menuItemTag } from '@ni/nimble-components/dist/esm/menu-item';\nimport type { Menu } from '@ni/nimble-components/dist/esm/menu';\nimport { styles } from './styles';\nimport { template } from './template';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ok-fv-toolbar': FvToolbar;\n }\n}\n\ntype Presentation = 'label' | 'icon' | 'overflow';\n\ninterface ResponsiveItem {\n button: Button;\n stages: Presentation[];\n priority: number;\n contentHidden: boolean;\n hidden: boolean;\n menuItem: MenuItem;\n}\n\n/**\n * A Nimble toolbar that automatically collapses declaratively configured actions.\n */\nexport class FvToolbar extends Toolbar {\n @attr({ attribute: 'overflow-label' })\n public overflowLabel = 'More actions';\n\n /** @internal */\n @observable\n public hasOverflow = false;\n\n /** @internal */\n public overflowButton!: MenuButton;\n\n /** @internal */\n public overflowMenu!: Menu;\n\n private readonly items = new Map<Button, ResponsiveItem>();\n private readonly resizeObserver = new ResizeObserver(() => this.scheduleLayout());\n private readonly mutationObserver = new MutationObserver(records => {\n for (const record of records) {\n const item = record.target instanceof Button ? this.items.get(record.target) : undefined;\n if (item && record.attributeName === 'hidden') {\n item.hidden = item.button.hidden;\n }\n if (item && record.attributeName === 'content-hidden') {\n item.contentHidden = item.button.contentHidden;\n }\n }\n this.scheduleLayout();\n });\n\n private readonly observedChildren = new Set<Element>();\n private frame = 0;\n\n public override connectedCallback(): void {\n super.connectedCallback();\n this.resizeObserver.observe(this);\n this.observeMutations();\n this.ownerDocument.fonts.addEventListener('loadingdone', this.scheduleLayout);\n this.scheduleLayout();\n }\n\n public override disconnectedCallback(): void {\n this.resizeObserver.disconnect();\n this.mutationObserver.disconnect();\n this.ownerDocument.fonts.removeEventListener('loadingdone', this.scheduleLayout);\n cancelAnimationFrame(this.frame);\n this.frame = 0;\n for (const item of this.items.values()) {\n this.restoreItem(item);\n }\n this.items.clear();\n this.observedChildren.clear();\n super.disconnectedCallback();\n }\n\n protected override get allSlottedItems(): (HTMLElement | Node)[] {\n return [\n ...super.allSlottedItems,\n ...(this.hasOverflow ? [this.overflowButton] : [])\n ];\n }\n\n private observeMutations(): void {\n this.mutationObserver.observe(this, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n attributeFilter: [\n 'data-responsive', 'data-priority', 'disabled', 'hidden',\n 'content-hidden', 'aria-label', 'slot', 'class', 'style'\n ]\n });\n }\n\n private readonly scheduleLayout = (): void => {\n if (!this.isConnected || this.frame !== 0) {\n return;\n }\n this.frame = requestAnimationFrame(() => {\n this.frame = 0;\n this.updateLayout();\n });\n };\n\n private restoreItem(item: ResponsiveItem): void {\n item.button.hidden = item.hidden;\n item.button.toggleAttribute('content-hidden', item.contentHidden);\n item.menuItem.remove();\n }\n\n private syncItems(): ResponsiveItem[] {\n const buttons = Array.from(this.children).filter(\n (child): child is Button => child instanceof Button && child.hasAttribute('data-responsive')\n );\n for (const [button, item] of this.items) {\n if (!buttons.includes(button)) {\n this.restoreItem(item);\n this.items.delete(button);\n }\n }\n for (const button of buttons) {\n const value = button.getAttribute('data-responsive')?.trim() ?? '';\n const tokens = value.split(/\\s+/);\n const stages = (['label', 'icon', 'overflow'] as const).filter(stage => tokens.includes(stage));\n const priority = Number(button.getAttribute('data-priority') ?? 0);\n if (stages.length === 0 || tokens.some(token => !stages.some(stage => stage === token))\n || !Number.isFinite(priority)\n || (stages.includes('icon') && !button.querySelector(':scope > [slot=\"start\"]'))) {\n throw new Error('ok-fv-toolbar requires valid data-responsive stages, a finite data-priority, and a direct start-slot icon for icon-only actions.');\n }\n let item = this.items.get(button);\n if (!item) {\n const menuItem = this.ownerDocument.createElement(menuItemTag);\n menuItem.addEventListener('change', () => {\n if (!button.disabled) {\n button.click();\n }\n });\n item = {\n button,\n stages,\n priority,\n hidden: button.hidden,\n contentHidden: button.contentHidden,\n menuItem\n };\n this.items.set(button, item);\n }\n item.stages = stages;\n item.priority = priority;\n item.menuItem.disabled = button.disabled;\n const label = (button.getAttribute('aria-label')\n ?? Array.from(button.childNodes)\n .filter(node => node.nodeType === Node.TEXT_NODE\n || (node instanceof Element && !node.hasAttribute('slot')))\n .map(node => node.textContent).join('')).trim();\n if (!label) {\n throw new Error('ok-fv-toolbar responsive actions require a text label or aria-label.');\n }\n if (item.menuItem.textContent !== label) {\n item.menuItem.textContent = label;\n }\n }\n return buttons.map(button => this.items.get(button)!);\n }\n\n private updateLayout(): void {\n // Ignore our own presentation mutations, not application changes.\n this.mutationObserver.disconnect();\n try {\n const menuFocus = this.shadowRoot?.activeElement;\n const menuHadFocus = menuFocus && this.overflowButton.contains(menuFocus);\n const previousMenuItems = Array.from(this.overflowMenu.children);\n const items = this.syncItems();\n const root = this.getRootNode();\n const activeElement = root instanceof ShadowRoot ? root.activeElement : this.ownerDocument.activeElement;\n const focusedItem = items.find(item => item.button === activeElement);\n for (const child of this.observedChildren) {\n if (child.parentElement !== this) {\n this.resizeObserver.unobserve(child);\n this.observedChildren.delete(child);\n }\n }\n for (const child of this.children) {\n if (!this.observedChildren.has(child)) {\n this.resizeObserver.observe(child);\n this.observedChildren.add(child);\n }\n }\n const overflow = new Set<ResponsiveItem>();\n for (const item of items) {\n item.button.hidden = item.hidden;\n item.button.toggleAttribute('content-hidden', item.stages[0] === 'icon');\n if (!item.hidden && item.stages[0] === 'overflow') {\n overflow.add(item);\n item.button.hidden = true;\n }\n }\n this.showOverflow(overflow.size > 0);\n const ordered = items.filter(item => !item.hidden).reverse()\n .sort((a, b) => a.priority - b.priority);\n // ponytail: measure each transition; cache widths only if large toolbars make this costly.\n for (const stage of ['icon', 'overflow'] as const) {\n for (const item of ordered) {\n if (this.fits()) {\n break;\n }\n if (overflow.has(item) || !item.stages.includes(stage)) {\n continue;\n }\n if (stage === 'icon') {\n item.button.toggleAttribute('content-hidden', true);\n } else {\n item.button.hidden = true;\n overflow.add(item);\n this.showOverflow(true);\n }\n }\n }\n const overflowItems = items.filter(item => overflow.has(item));\n const nextMenuItems = overflowItems.map(item => item.menuItem);\n if (nextMenuItems.length !== previousMenuItems.length\n || nextMenuItems.some((item, index) => previousMenuItems[index] !== item)) {\n this.overflowButton.open = false;\n this.overflowMenu.replaceChildren(...nextMenuItems);\n }\n if (!this.hasOverflow) {\n this.overflowButton.open = false;\n }\n this.overflowButton.hidden = !this.hasOverflow;\n this.overflowButton.classList.remove('unreserved');\n this.reduceFocusableElements();\n if (focusedItem && overflow.has(focusedItem)) {\n this.overflowButton.focus();\n } else if (menuHadFocus) {\n if (this.hasOverflow) {\n if (this.overflowButton.open && menuFocus instanceof HTMLElement && menuFocus.isConnected) {\n menuFocus.focus();\n } else {\n this.overflowButton.focus();\n }\n } else {\n Array.from(this.querySelectorAll<HTMLElement>('[tabindex=\"0\"]'))\n .find(control => !control.closest('[hidden], [disabled]'))?.focus();\n }\n }\n } finally {\n this.observeMutations();\n }\n }\n\n private showOverflow(show: boolean): void {\n this.hasOverflow = show;\n // Remove the trigger from width calculations without hiding its focused menu.\n this.overflowButton.classList.toggle('unreserved', !show);\n this.overflowButton.hidden = false;\n }\n\n private fits(): boolean {\n const region = this.shadowRoot!.querySelector<HTMLElement>('.positioning-region')!;\n return region.scrollWidth <= region.clientWidth;\n }\n}\n\nconst okFvToolbar = FvToolbar.compose({\n baseName: 'fv-toolbar',\n template,\n styles\n});\n\nDesignSystem.getOrCreate().withPrefix('ok').register(okFvToolbar());\nexport const fvToolbarTag = 'ok-fv-toolbar';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import("@ni/fast-element").ElementStyles;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { css } from '@ni/fast-element';
|
|
2
|
+
import { styles as toolbarStyles } from '@ni/nimble-components/dist/esm/toolbar/styles';
|
|
3
|
+
export const styles = css `
|
|
4
|
+
${toolbarStyles}
|
|
5
|
+
|
|
6
|
+
:host {
|
|
7
|
+
display: block;
|
|
8
|
+
min-width: 0;
|
|
9
|
+
max-width: 100%;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.positioning-region {
|
|
13
|
+
position: relative;
|
|
14
|
+
overflow-x: auto;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.unreserved {
|
|
18
|
+
position: absolute;
|
|
19
|
+
inset-inline-end: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
[part='start'],
|
|
23
|
+
[part='end'],
|
|
24
|
+
nimble-menu-button,
|
|
25
|
+
::slotted(*) {
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
[part='start'] {
|
|
30
|
+
margin: 0;
|
|
31
|
+
margin-inline-end: auto;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
[part='end'] {
|
|
35
|
+
margin: 0;
|
|
36
|
+
margin-inline-start: auto;
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
//# sourceMappingURL=styles.js.map
|