@kerfjs/ui 5.0.0-beta.70 → 5.0.0-beta.72
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/ai/component-catalog.json +21 -5
- package/ai/component-catalog.schema.json +2 -0
- package/ai/component-composition-rules.json +4 -4
- package/ai/component-composition.d.ts +2 -0
- package/ai/component-composition.json +23 -5
- package/ai/component-composition.schema.json +9 -0
- package/ai/components/toolbar-text.md +3 -1
- package/ai/components/toolbar.md +6 -6
- package/ai/public-api-signatures-v1.md +16 -11
- package/ai/skill.md +1 -1
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/analyzer/component-ownership.mjs +131 -16
- package/analyzer/index.d.mts +2 -1
- package/analyzer/index.mjs +298 -29
- package/dist/catalog.js +2 -2
- package/dist/{chunk-KCCEH7RF.js → chunk-3UE24JSC.js} +2 -2
- package/dist/{chunk-KCCEH7RF.js.map → chunk-3UE24JSC.js.map} +1 -1
- package/dist/{chunk-V3DX3K65.js → chunk-R5F5XUJ7.js} +19 -2
- package/dist/chunk-R5F5XUJ7.js.map +1 -0
- package/dist/{chunk-T2E5WBCZ.js → chunk-SLWCCHNP.js} +11 -2
- package/dist/chunk-SLWCCHNP.js.map +1 -0
- package/dist/{chunk-DC6CN2DS.js → chunk-VLYIPBCQ.js} +2 -2
- package/dist/{chunk-DC6CN2DS.js.map → chunk-VLYIPBCQ.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/nav-stack.js +1 -1
- package/dist/split-view.js +1 -1
- package/dist/styles/app-tab.css +3 -0
- package/dist/styles/chip.css +6 -0
- package/dist/styles/toolbar-text.css +20 -0
- package/dist/styles/toolbar.css +10 -1
- package/dist/toolbar-text.d.ts +15 -10
- package/dist/toolbar-text.js +1 -1
- package/dist/wire-tab-bars.js +1 -1
- package/dist/workbench.js +1 -1
- package/docs/accessibility.md +5 -0
- package/docs/component-contract.md +17 -2
- package/docs/component-selection.md +27 -16
- package/docs/layout.md +4 -0
- package/docs/ui-analyzer.md +42 -5
- package/docs/ui-doctor.md +28 -3
- package/doctor/config.schema.json +23 -1
- package/doctor/index.d.mts +2 -1
- package/doctor/index.mjs +42 -2
- package/package.json +1 -1
- package/dist/chunk-T2E5WBCZ.js.map +0 -1
- package/dist/chunk-V3DX3K65.js.map +0 -1
package/dist/styles/toolbar.css
CHANGED
|
@@ -161,7 +161,8 @@
|
|
|
161
161
|
compact group's own smaller group size never redefines it.
|
|
162
162
|
`--_kui-toolbar-band` is context the toolbar provides: a ToolbarControlGroup
|
|
163
163
|
and a ToolbarText each center themselves in it from their own stylesheets
|
|
164
|
-
(toolbar-control-group.css, toolbar-text.css).
|
|
164
|
+
(toolbar-control-group.css, toolbar-text.css). A direct primary wa-button in
|
|
165
|
+
trailing is centered here without altering its Web Awesome chrome. */
|
|
165
166
|
.kui-toolbar {
|
|
166
167
|
--_kui-toolbar-band: var(--kui-toolbar-group-size, calc(2px + 2.625rem));
|
|
167
168
|
}
|
|
@@ -237,6 +238,14 @@
|
|
|
237
238
|
flex-wrap: wrap;
|
|
238
239
|
}
|
|
239
240
|
|
|
241
|
+
.kui-toolbar__trailing > wa-button {
|
|
242
|
+
margin-block-start: max(
|
|
243
|
+
0px,
|
|
244
|
+
calc((var(--_kui-toolbar-band) - var(--wa-form-control-height)) / 2)
|
|
245
|
+
);
|
|
246
|
+
flex: 0 0 auto;
|
|
247
|
+
}
|
|
248
|
+
|
|
240
249
|
/* An expanded trailing search gets a complete second row while the leading
|
|
241
250
|
identity and optional center controls remain in the first. The query styles
|
|
242
251
|
descendants because a toolbar cannot query its own container width. */
|
package/dist/toolbar-text.d.ts
CHANGED
|
@@ -9,14 +9,6 @@ interface ToolbarTextBaseProps {
|
|
|
9
9
|
className?: string;
|
|
10
10
|
/** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */
|
|
11
11
|
id?: string;
|
|
12
|
-
/**
|
|
13
|
-
* Expose heading semantics (`role="heading"` + `aria-level`) so the text acts as
|
|
14
|
-
* a heading landmark — e.g. a page's primary title. Omit to keep the plain span
|
|
15
|
-
* (the default), which suits a dialog title referenced via `aria-labelledby`.
|
|
16
|
-
*/
|
|
17
|
-
headingLevel?: HeadingLevel;
|
|
18
|
-
/** Render the text as an unanimated loading skeleton instead of its value. */
|
|
19
|
-
placeholder?: boolean;
|
|
20
12
|
/**
|
|
21
13
|
* Show a trailing ellipsis (…) where the text is truncated — on the single line
|
|
22
14
|
* (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip
|
|
@@ -40,7 +32,20 @@ type ToolbarTextWrappingProps = {
|
|
|
40
32
|
wrap?: false;
|
|
41
33
|
maxLines?: never;
|
|
42
34
|
};
|
|
43
|
-
type ToolbarTextProps = ToolbarTextBaseProps &
|
|
44
|
-
|
|
35
|
+
type ToolbarTextProps = ToolbarTextBaseProps & (({
|
|
36
|
+
action?: never;
|
|
37
|
+
/** Expose `role="heading"` and `aria-level` on read-only text. */
|
|
38
|
+
headingLevel?: HeadingLevel;
|
|
39
|
+
/** Render an unanimated loading skeleton instead of the text. */
|
|
40
|
+
placeholder?: boolean;
|
|
41
|
+
} & ToolbarTextWrappingProps) | {
|
|
42
|
+
/** Render a native button with this delegated action; the app owns editing. */
|
|
43
|
+
action: string;
|
|
44
|
+
headingLevel?: never;
|
|
45
|
+
placeholder?: never;
|
|
46
|
+
wrap?: never;
|
|
47
|
+
maxLines?: never;
|
|
48
|
+
});
|
|
49
|
+
declare function ToolbarText({ text, size, className, id, headingLevel, placeholder, wrap, ellipsis, maxLines, fill, slot, action, }: ToolbarTextProps): kerfjs.SafeHtml;
|
|
45
50
|
|
|
46
51
|
export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
|
package/dist/toolbar-text.js
CHANGED
package/dist/wire-tab-bars.js
CHANGED
package/dist/workbench.js
CHANGED
package/docs/accessibility.md
CHANGED
|
@@ -322,6 +322,11 @@ role/level pair used throughout Kerf UI. Keep the levels meaningful and
|
|
|
322
322
|
non-skipping within a view. Pass trailing
|
|
323
323
|
controls as a labeled `ToolbarControlGroup` when that group needs an accessible
|
|
324
324
|
name. Keep supporting copy as app-owned content below the toolbar.
|
|
325
|
+
An editable title can use `ToolbarText action="edit-title"`; it renders a native
|
|
326
|
+
button whose full text remains its accessible name even when visually truncated.
|
|
327
|
+
Click, Enter, and Space activate the delegated action. The app owns the input
|
|
328
|
+
shown while editing and its focus lifecycle. Because the button cannot also be
|
|
329
|
+
a heading landmark, expose a heading elsewhere when the view needs one.
|
|
325
330
|
|
|
326
331
|
## FloatingToolbar
|
|
327
332
|
|
|
@@ -70,6 +70,12 @@ every consumer catalog a `.kerf-ui-profile.json` declares, whatever the class
|
|
|
70
70
|
prefix. `create-kerf-component` packages declare the same fields in
|
|
71
71
|
`kerf.components.json`.
|
|
72
72
|
|
|
73
|
+
`descendantRootAttribute` optionally names a `data-*` attribute on a
|
|
74
|
+
cataloged `rootElement`. It anchors ownership of arbitrary unclassed child
|
|
75
|
+
element selectors below that exact root, as `LucideIcon` does with
|
|
76
|
+
`svg[data-lucide]`. Without the attribute in a selector, the analyzer uses
|
|
77
|
+
only cataloged `descendantElements` and composed JSX evidence.
|
|
78
|
+
|
|
73
79
|
An entry's optional `cssValueProps` makes CSS-adjacent prop grammar equally
|
|
74
80
|
machine-evaluable. Each path records the property grammar, finite shorthands,
|
|
75
81
|
canonical versus exceptional spacing choices, standalone and expression-only
|
|
@@ -593,8 +599,9 @@ The enclosing `Toolbar` owns zone alignment and responsive topology: use
|
|
|
593
599
|
choose `responsive="stack"` with `responsiveAt="compact" | "narrow"`,
|
|
594
600
|
`responsive="wrap"` for a heading whose title must stay whole, or
|
|
595
601
|
`responsive="center-priority"` instead of selecting its zone classes from a
|
|
596
|
-
parent stylesheet. No policy clips an action: the trailing zone
|
|
597
|
-
|
|
602
|
+
parent stylesheet. No policy clips an action: the trailing zone wraps whole
|
|
603
|
+
groups and its standalone primary `wa-button` onto another row; a stacked
|
|
604
|
+
center zone wraps its groups. The center zone never
|
|
598
605
|
compresses below its content width, so when the leading identity, center, and
|
|
599
606
|
trailing zones share a row that is too narrow, the leading title is what gives
|
|
600
607
|
way: it truncates with an ellipsis (as a platform toolbar's title does) instead
|
|
@@ -615,6 +622,12 @@ zone holds an optional icon `ToolbarControlGroup` and a direct extra-large
|
|
|
615
622
|
`ToolbarText`; app actions belong in a trailing `ToolbarControlGroup`. Omit the
|
|
616
623
|
icon group when it has no content. Supporting copy is app-owned content below
|
|
617
624
|
the toolbar so it cannot pull the icon or actions out of alignment.
|
|
625
|
+
For an editable title, `ToolbarText action="edit-title"` renders a native button
|
|
626
|
+
directly in the leading zone. It keeps content width when there is room, shrinks
|
|
627
|
+
and ellipsizes beside a status chip when space is tight, and emits the delegated
|
|
628
|
+
action on click or keyboard activation. The application owns the editing control
|
|
629
|
+
and the swap between display and edit states. An actionable title is a button,
|
|
630
|
+
so place any required heading landmark on another element in the view.
|
|
618
631
|
|
|
619
632
|
`ValueTable` composes typed `ValueTableRow` entries. A row owns its `dt`/`dd`
|
|
620
633
|
semantics and may receive a leading `SafeHtml` icon. Every row keeps 8px of
|
|
@@ -704,6 +717,8 @@ tab's trailing edge while peer tabs scroll beneath it, including in RTL.
|
|
|
704
717
|
With `snapTabs`, `wireTabBars` measures the pinned tab and settles peers at
|
|
705
718
|
whole-tab starts after scroll and selection changes, reserving end room so the
|
|
706
719
|
last peer can align there. The application still owns selection and tab order.
|
|
720
|
+
ResizeObserver notifications coalesce snap measurements into the next animation
|
|
721
|
+
frame, so inset writes cannot resize the observed strip during observer delivery.
|
|
707
722
|
|
|
708
723
|
CSS, the generated wrappers that make it
|
|
709
724
|
reachable, and the registration module are the package's only declared side
|
|
@@ -284,8 +284,10 @@ without styling its strip or tab internals.
|
|
|
284
284
|
Set the public
|
|
285
285
|
`--kui-app-tab-pinned-background` token if the surrounding surface differs from
|
|
286
286
|
the default; the pinned tab must cover peers as they scroll beneath it. Its
|
|
287
|
-
background also covers the strip's inline padding and border so scrolled labels
|
|
288
|
-
paint beside the pinned edge, including in right-to-left strips.
|
|
287
|
+
background also covers the strip's inline padding and border so scrolled labels
|
|
288
|
+
cannot paint beside the pinned edge, including in right-to-left strips. The
|
|
289
|
+
backing follows the tab's pill radius, keeping a selected pinned tab's corners
|
|
290
|
+
and shadow whole beside the overflow divider. Call
|
|
289
291
|
`wireTabBars` for keyboard selection and reorder behavior and its WebKit RTL
|
|
290
292
|
scroll correction.
|
|
291
293
|
|
|
@@ -383,14 +385,22 @@ A `FloatingToolbar` accepts `ToolbarControlGroup` directly as its children in
|
|
|
383
385
|
both floating and inline placement. Name the toolbar with `label`; group its
|
|
384
386
|
controls as you would in a `Toolbar` zone.
|
|
385
387
|
|
|
386
|
-
A `Toolbar` has three zones — `leading`, `center`, and `trailing`.
|
|
387
|
-
|
|
388
|
-
(
|
|
389
|
-
|
|
388
|
+
A `Toolbar` has three zones — `leading`, `center`, and `trailing`. The direct
|
|
389
|
+
zone children are `ToolbarText` (identity/title text) and `ToolbarControlGroup`
|
|
390
|
+
(a control or cluster of controls). The `trailing` zone also accepts one
|
|
391
|
+
standalone `wa-button` for the application's primary action. It keeps its own
|
|
392
|
+
Web Awesome brand, danger, size, and hover chrome, while Toolbar aligns it in
|
|
393
|
+
the control band and wraps it with other trailing items when space is tight.
|
|
394
|
+
For a title that opens an editor, set `action` on a direct `ToolbarText`: it
|
|
395
|
+
renders a native button that keeps its content width when possible and
|
|
396
|
+
ellipsizes before an adjacent status chip when the leading zone narrows. The
|
|
397
|
+
application handles the delegated action and replaces it with its own input
|
|
398
|
+
while editing. Use a separate heading landmark if the view needs one.
|
|
399
|
+
Do not drop other bare buttons, inputs, links, or arbitrary markup straight
|
|
390
400
|
into a zone; wrap controls in a `ToolbarControlGroup` so they get the shared
|
|
391
401
|
toolbar geometry, hover/pressed treatment, and grouping. `SegmentedControl`,
|
|
392
|
-
`Select`, a collapsible `TokenSearchField`, and Web Awesome controls
|
|
393
|
-
**inside** a `ToolbarControlGroup
|
|
402
|
+
`Select`, a collapsible `TokenSearchField`, and other Web Awesome controls live
|
|
403
|
+
**inside** a `ToolbarControlGroup`. For a page, panel, or
|
|
394
404
|
dialog heading, put an extra-large `ToolbarText` directly in the leading zone,
|
|
395
405
|
optionally preceded by a grouped icon, and group trailing actions. Keep supporting
|
|
396
406
|
copy below as app-owned content. Size the title to its track: a narrow
|
|
@@ -425,14 +435,15 @@ For a long-running icon action, set `busy` on its single-control
|
|
|
425
435
|
the control's dimensions, shows `LoadingSpinner` in its icon slot, sets
|
|
426
436
|
`aria-busy`, and makes the control inert until the app clears `busy`.
|
|
427
437
|
|
|
428
|
-
| Want
|
|
429
|
-
|
|
|
430
|
-
| Identity or title text
|
|
431
|
-
| One or more icon/text buttons
|
|
432
|
-
|
|
|
433
|
-
|
|
|
434
|
-
| A
|
|
435
|
-
| A
|
|
438
|
+
| Want | Put in the zone | Notes |
|
|
439
|
+
| ------------------------------ | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
440
|
+
| Identity or title text | `<ToolbarText text="…" size="large" />` (or `xlarge` for a page/panel title that fits) | Wrap in a `single` borderless group only when it must align with adjacent control pills |
|
|
441
|
+
| One or more icon/text buttons | `<ToolbarControlGroup>{buttons}</ToolbarControlGroup>` | Use `buttonAppearance="push"` for toggle buttons with `aria-pressed`; `single` for a lone control |
|
|
442
|
+
| One primary application action | `trailing={<wa-button variant="brand">New ticket…</wa-button>}` | The standalone button keeps its Web Awesome chrome; use `variant="danger"` when the action is destructive |
|
|
443
|
+
| An exclusive view switch | `<ToolbarControlGroup><SegmentedControl … /></ToolbarControlGroup>` | Not `TabBar`, which switches tabpanels |
|
|
444
|
+
| A value list | `<ToolbarControlGroup><Select … /></ToolbarControlGroup>` | Register `@kerfjs/ui/select/register` once; use `focusRingOwner="group"` so focus follows the group's geometry |
|
|
445
|
+
| A command menu | `<ToolbarControlGroup single nestedDropdown><PopupMenu … /></ToolbarControlGroup>` | Register `@kerfjs/ui/popup-menu/register` once; set `menuInset` on the group |
|
|
446
|
+
| A collapsible search box | `<ToolbarControlGroup single><TokenSearchField collapsible … /></ToolbarControlGroup>` | The group animates the iconic ↔ expanded states; `wireTokenSearchFields` manages expand/collapse/focus by default |
|
|
436
447
|
|
|
437
448
|
### Popup menu migration
|
|
438
449
|
|
package/docs/layout.md
CHANGED
|
@@ -309,6 +309,10 @@ zone holds an optional icon `ToolbarControlGroup` and a direct extra-large
|
|
|
309
309
|
`ToolbarText`; actions belong in a trailing group. Omit empty groups. Supporting
|
|
310
310
|
copy is app-owned content below the toolbar and aligns with the intended content
|
|
311
311
|
edge.
|
|
312
|
+
When the title itself opens editing, set `ToolbarText action="edit-title"` and
|
|
313
|
+
render it directly in the leading zone beside any status chip. The button
|
|
314
|
+
shrinks and ellipsizes at the space left by its siblings without filling unused
|
|
315
|
+
space; the app swaps its own input into the edit state.
|
|
312
316
|
|
|
313
317
|
## Spacing scale
|
|
314
318
|
|
package/docs/ui-analyzer.md
CHANGED
|
@@ -134,7 +134,21 @@ which element a selector styles.
|
|
|
134
134
|
are also reported below a hook class placed on an imported component, below
|
|
135
135
|
another entry's root in the same package, or at a direct child root whose
|
|
136
136
|
JSX parent composes only a component with that cataloged `rootElement` type.
|
|
137
|
-
|
|
137
|
+
Descendant selectors also see a composed child's cataloged root and
|
|
138
|
+
`descendantElements` below an own wrapper. `LucideIcon` publishes `svg` and
|
|
139
|
+
its Lucide SVG child tags, so `.wrapper > svg`, `.wrapper svg`, and
|
|
140
|
+
`.wrapper path` can name the composed icon. Ambiguous evidence from multiple
|
|
141
|
+
different child components is discarded. For arbitrary custom `IconNode`
|
|
142
|
+
child tags outside the cataloged SVG tag list, a selector rooted at
|
|
143
|
+
`svg[data-lucide]` identifies the descendant as
|
|
144
|
+
LucideIcon-owned (for example `svg[data-lucide] filter`). Without that root
|
|
145
|
+
marker, an unknown tag beneath a wrapper remains ambiguous and is not
|
|
146
|
+
attributed to LucideIcon or to unrelated HTML.
|
|
147
|
+
The scan follows returned JSX from
|
|
148
|
+
same-module function and arrow-function components at their call sites,
|
|
149
|
+
recursively with a cycle guard; their intrinsic elements remain app-owned.
|
|
150
|
+
The finding names the composing
|
|
151
|
+
class and component. A `::part()` subject is left to
|
|
138
152
|
`KUI-L011`; a rule that only sets custom properties is configuration, judged
|
|
139
153
|
by the token rules. Forcing a component's dimensions is one such restyle
|
|
140
154
|
and reports here once: `.kui-pane { width: 300px }`.
|
|
@@ -159,7 +173,12 @@ which element a selector styles.
|
|
|
159
173
|
component's public class. The package boundary applies by default;
|
|
160
174
|
component mode also protects sibling modules through `componentSource`.
|
|
161
175
|
A component's own source and its `placeableClasses` remain allowed. Findings
|
|
162
|
-
name the class, owner, write site, and configuration route.
|
|
176
|
+
name the class, owner, write site, and configuration route. This diagnostic
|
|
177
|
+
scans raw HTML in plain strings and the static segments of interpolated
|
|
178
|
+
template literals. When a substitution interrupts a class value, only
|
|
179
|
+
complete literal tokens are reported; split tokens are not guessed. It
|
|
180
|
+
skips test modules (`*.test.*`, `*.spec.*`, and files under `__tests__/`),
|
|
181
|
+
where HTML literals commonly assert on output rather than render markup.
|
|
163
182
|
|
|
164
183
|
Every message names the configuration to use (the component's typed props,
|
|
165
184
|
public tokens, and variants from its catalog entry) and ends with the same
|
|
@@ -210,16 +229,34 @@ even without a composition entry. Each entry owns its declared `styleSources`
|
|
|
210
229
|
for CSS and its selection `source` module for JSX; when `styleSources` is absent,
|
|
211
230
|
direct relative CSS imports from that source are used. The analyzer also derives
|
|
212
231
|
literal JSX `class` and `className` values whose BEM block is styled in those
|
|
213
|
-
stylesheets
|
|
232
|
+
stylesheets. A root or styled literal establishes ownership of the whole BEM
|
|
233
|
+
block: `block`, `block__*`, and `block--*`, including dynamically constructed
|
|
234
|
+
element and modifier names. The boundary applies to selectors and markup class
|
|
235
|
+
writes, including a class prop on an imported component. Modules that share a
|
|
236
|
+
stylesheet co-own its classes, whether the stylesheet was declared or directly
|
|
237
|
+
imported; a source that imports none of an owner's stylesheets remains foreign.
|
|
238
|
+
When several entries infer the same class from a shared stylesheet, the
|
|
239
|
+
analyzer does not arbitrarily assign that class to one of them for `KUI-L019`.
|
|
240
|
+
Other sibling files are foreign.
|
|
214
241
|
Paths are relative to the entry's package root. Set
|
|
215
242
|
`implicitComponentOwnership: true` to apply the same source and stylesheet
|
|
216
243
|
inference to modules with direct CSS imports that have no catalog entry.
|
|
244
|
+
For a shell stylesheet imported once but used by style-less modules, pass
|
|
245
|
+
`ownershipGroups: [{ styleSources: ['src/style.css'], sources: ['src/main.tsx', 'src/app/'] }]`.
|
|
246
|
+
The paths are relative to the analyzed package root. A source ending in `/`
|
|
247
|
+
matches every module below that directory; other source paths match exactly.
|
|
248
|
+
Each group owns the subject classes in its stylesheets for `KUI-L019` and
|
|
249
|
+
`KUI-L023`. Its stylesheet and listed source modules share one owner, while
|
|
250
|
+
sibling stylesheets and modules remain foreign. This option applies only with
|
|
251
|
+
`ownership: 'component'`.
|
|
217
252
|
Set `ownershipContext: 'any'` alongside component ownership when one sibling
|
|
218
253
|
component must not mention another sibling's class anywhere in a CSS selector.
|
|
219
254
|
The default, `'subject'`, checks the styled subject. Strict findings use
|
|
220
255
|
`KUI-L019` with `evidence.via: 'context'` and a `position` of `ancestor`,
|
|
221
|
-
`sibling`, `has`, `is`, `where`, or `not`.
|
|
222
|
-
|
|
256
|
+
`sibling`, `has`, `is`, `where`, or `not`. Set
|
|
257
|
+
`ownershipContext: 'any-package'` to apply the same context rule to components
|
|
258
|
+
from other packages, including their public classes and `[data-component]`
|
|
259
|
+
roots. The `'any'` mode continues to allow cross-package context.
|
|
223
260
|
|
|
224
261
|
### Retired: `KUI-L005` (forced component dimension)
|
|
225
262
|
|
package/docs/ui-doctor.md
CHANGED
|
@@ -37,11 +37,23 @@ application or third-party CSS that restyles another package's component
|
|
|
37
37
|
token a typed prop sets (`KUI-L021`), or styles it through a hook class on its
|
|
38
38
|
root (`KUI-L022`). It also reports source modules that render another
|
|
39
39
|
component's public classes as their own markup (`KUI-L023`), including raw HTML
|
|
40
|
-
and DOM class writes.
|
|
40
|
+
and DOM class writes. Raw HTML checks include complete static class tokens in
|
|
41
|
+
interpolated template literals. `KUI-L023` skips `*.test.*`, `*.spec.*`, and `__tests__/`
|
|
42
|
+
modules so output assertions are not mistaken for rendered markup. The ESLint
|
|
43
|
+
stage's `ui-component-ownership` rule reports
|
|
41
44
|
`KUI-L020` / `KUI-L021` in script. Each finding's `action` points at the
|
|
42
45
|
component's configuration, and its message routes a need with no prop to a
|
|
43
46
|
component-gap report instead of an override (see
|
|
44
47
|
[the analyzer rule](./ui-analyzer.md#component-ownership-kui-l019kui-l023)).
|
|
48
|
+
In component mode, `KUI-L019` also follows an owned wrapper to a composed
|
|
49
|
+
child's cataloged root and descendant element types. For example, a wrapper
|
|
50
|
+
around `LucideIcon` cannot size its `svg` or `path` with an element selector;
|
|
51
|
+
use the icon's props or style an app-owned element. The same check follows
|
|
52
|
+
local function components returned from the owner module and treats their
|
|
53
|
+
intrinsic markup as app-owned.
|
|
54
|
+
Unknown custom `IconNode` tags are identified when a selector explicitly
|
|
55
|
+
passes through the icon's `svg[data-lucide]` root; unrelated HTML descendants
|
|
56
|
+
without that marker remain app-owned.
|
|
45
57
|
The analyzer resolves a cataloged application component imported through an
|
|
46
58
|
exact or wildcard TypeScript `paths` alias from the nearest consumer tsconfig,
|
|
47
59
|
so the same root hook-class check applies in those files.
|
|
@@ -51,13 +63,26 @@ boundary between a consumer package's own cataloged components. The default is
|
|
|
51
63
|
`styleSources` (relative to its package root), or its direct relative CSS
|
|
52
64
|
imports when that list is absent, and its selection catalog `source` module.
|
|
53
65
|
Literal rendered BEM classes are inferred when those stylesheets style their
|
|
54
|
-
block.
|
|
66
|
+
block. An owned root or styled literal owns its whole `block__*` and `block--*`
|
|
67
|
+
family, including dynamic names and classes placed on imported components.
|
|
68
|
+
Modules sharing one of those stylesheets co-own its classes, so their
|
|
69
|
+
own markup does not borrow from one another. A class inferred by several
|
|
70
|
+
co-owners has no arbitrary single owner for CSS subject diagnostics.
|
|
71
|
+
Set `"implicitComponentOwnership": true` to include uncataloged modules
|
|
55
72
|
that directly import CSS. Component mode reports sibling CSS and hook-class
|
|
56
73
|
restyles while allowing a component's own stylesheet and module.
|
|
74
|
+
For an entry stylesheet shared by modules without direct CSS imports, configure
|
|
75
|
+
`"ownershipGroups": [{ "styleSources": ["src/style.css"], "sources": ["src/main.tsx", "src/app/"] }]`.
|
|
76
|
+
These package-relative paths define one owner: its stylesheet and every listed
|
|
77
|
+
source file or directory prefix. Doctor reports sibling CSS restyles and markup
|
|
78
|
+
class use as `KUI-L019` and `KUI-L023`. Source paths ending in `/` match a
|
|
79
|
+
directory subtree; other paths match exactly.
|
|
57
80
|
Set `"ownershipContext": "any"` to also report a same-package sibling's class
|
|
58
81
|
used as ancestor or sibling context or within `:has()`, `:is()`, `:where()`, or
|
|
59
82
|
`:not()`. The default `"subject"` keeps subject-only context policy; classes
|
|
60
|
-
from another package may still provide context.
|
|
83
|
+
from another package may still provide context. Set
|
|
84
|
+
`"ownershipContext": "any-package"` to include cross-package component
|
|
85
|
+
classes and `[data-component]` roots in the same selector-context checks.
|
|
61
86
|
|
|
62
87
|
The browser evaluator is different: it runs the application and is disabled by default. It only runs when configuration supplies `browser.url` or the command receives `--browser-url`. Start and authorize the target application separately.
|
|
63
88
|
|
|
@@ -10,8 +10,30 @@
|
|
|
10
10
|
"mode": { "enum": ["full", "changed"] },
|
|
11
11
|
"package": { "type": "string", "minLength": 1 },
|
|
12
12
|
"ownership": { "enum": ["package", "component"] },
|
|
13
|
-
"ownershipContext": { "enum": ["subject", "any"] },
|
|
13
|
+
"ownershipContext": { "enum": ["subject", "any", "any-package"] },
|
|
14
14
|
"implicitComponentOwnership": { "type": "boolean" },
|
|
15
|
+
"ownershipGroups": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["styleSources", "sources"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"styleSources": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"minItems": 1,
|
|
24
|
+
"uniqueItems": true,
|
|
25
|
+
"items": { "$ref": "#/$defs/path" }
|
|
26
|
+
},
|
|
27
|
+
"sources": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"minItems": 1,
|
|
30
|
+
"uniqueItems": true,
|
|
31
|
+
"items": { "$ref": "#/$defs/path" }
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"additionalProperties": false
|
|
35
|
+
}
|
|
36
|
+
},
|
|
15
37
|
"stages": {
|
|
16
38
|
"type": "object",
|
|
17
39
|
"properties": {
|
package/doctor/index.d.mts
CHANGED
|
@@ -23,8 +23,9 @@ export interface UiDoctorConfig {
|
|
|
23
23
|
mode?: 'full' | 'changed';
|
|
24
24
|
package?: string;
|
|
25
25
|
ownership?: 'package' | 'component';
|
|
26
|
-
ownershipContext?: 'subject' | 'any';
|
|
26
|
+
ownershipContext?: 'subject' | 'any' | 'any-package';
|
|
27
27
|
implicitComponentOwnership?: boolean;
|
|
28
|
+
ownershipGroups?: Array<{ styleSources: string[]; sources: string[] }>;
|
|
28
29
|
stages?: Partial<
|
|
29
30
|
Record<
|
|
30
31
|
'catalog' | 'typescript' | 'eslint' | 'analyzer' | 'browser',
|
package/doctor/index.mjs
CHANGED
|
@@ -134,6 +134,7 @@ export function validateUiDoctorConfig(
|
|
|
134
134
|
'ownership',
|
|
135
135
|
'ownershipContext',
|
|
136
136
|
'implicitComponentOwnership',
|
|
137
|
+
'ownershipGroups',
|
|
137
138
|
'stages',
|
|
138
139
|
'browser',
|
|
139
140
|
'cache',
|
|
@@ -163,9 +164,12 @@ export function validateUiDoctorConfig(
|
|
|
163
164
|
add('$.ownership', 'ownership must be package or component.');
|
|
164
165
|
if (
|
|
165
166
|
config.ownershipContext !== undefined &&
|
|
166
|
-
!['subject', 'any'].includes(config.ownershipContext)
|
|
167
|
+
!['subject', 'any', 'any-package'].includes(config.ownershipContext)
|
|
167
168
|
)
|
|
168
|
-
add(
|
|
169
|
+
add(
|
|
170
|
+
'$.ownershipContext',
|
|
171
|
+
'ownershipContext must be subject, any, or any-package.',
|
|
172
|
+
);
|
|
169
173
|
if (
|
|
170
174
|
config.implicitComponentOwnership !== undefined &&
|
|
171
175
|
typeof config.implicitComponentOwnership !== 'boolean'
|
|
@@ -174,6 +178,40 @@ export function validateUiDoctorConfig(
|
|
|
174
178
|
'$.implicitComponentOwnership',
|
|
175
179
|
'implicitComponentOwnership must be a boolean.',
|
|
176
180
|
);
|
|
181
|
+
if (config.ownershipGroups !== undefined) {
|
|
182
|
+
if (!Array.isArray(config.ownershipGroups))
|
|
183
|
+
add('$.ownershipGroups', 'ownershipGroups must be an array.');
|
|
184
|
+
else
|
|
185
|
+
config.ownershipGroups.forEach((group, index) => {
|
|
186
|
+
const path = `$.ownershipGroups[${index}]`;
|
|
187
|
+
if (!group || typeof group !== 'object' || Array.isArray(group)) {
|
|
188
|
+
add(path, 'Ownership group must be an object.');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
for (const key of Object.keys(group))
|
|
192
|
+
if (!['styleSources', 'sources'].includes(key))
|
|
193
|
+
add(`${path}.${key}`, 'Unknown ownership group property.');
|
|
194
|
+
for (const key of ['styleSources', 'sources']) {
|
|
195
|
+
const paths = group[key];
|
|
196
|
+
if (
|
|
197
|
+
!Array.isArray(paths) ||
|
|
198
|
+
!paths.length ||
|
|
199
|
+
paths.some(
|
|
200
|
+
(item) =>
|
|
201
|
+
typeof item !== 'string' ||
|
|
202
|
+
!item ||
|
|
203
|
+
item.startsWith('/') ||
|
|
204
|
+
/(^|\/)\.\.(\/|$)|[?*]/.test(item),
|
|
205
|
+
) ||
|
|
206
|
+
new Set(paths).size !== paths.length
|
|
207
|
+
)
|
|
208
|
+
add(
|
|
209
|
+
`${path}.${key}`,
|
|
210
|
+
`${key} must be a non-empty array of unique, relative paths without traversal or globs.`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
177
215
|
if (config.stages !== undefined) {
|
|
178
216
|
if (
|
|
179
217
|
!config.stages ||
|
|
@@ -1164,6 +1202,7 @@ export async function runUiDoctor({
|
|
|
1164
1202
|
ownershipContext: config.ownershipContext ?? 'subject',
|
|
1165
1203
|
implicitComponentOwnership:
|
|
1166
1204
|
config.implicitComponentOwnership ?? false,
|
|
1205
|
+
ownershipGroups: config.ownershipGroups ?? [],
|
|
1167
1206
|
signal,
|
|
1168
1207
|
})
|
|
1169
1208
|
: (async () => ({
|
|
@@ -1175,6 +1214,7 @@ export async function runUiDoctor({
|
|
|
1175
1214
|
ownershipContext: config.ownershipContext ?? 'subject',
|
|
1176
1215
|
implicitComponentOwnership:
|
|
1177
1216
|
config.implicitComponentOwnership ?? false,
|
|
1217
|
+
ownershipGroups: config.ownershipGroups ?? [],
|
|
1178
1218
|
knownRules: [...registeredRuleIds, ...eslintRuleIds],
|
|
1179
1219
|
}),
|
|
1180
1220
|
),
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/wire-tab-bars.ts"],"names":["target"],"mappings":"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","file":"chunk-T2E5WBCZ.js","sourcesContent":["import type { TabActivation } from './tab-bar.js';\n\nexport type TabReorderSource = 'pointer' | 'keyboard';\nexport type TabDropPosition = 'before' | 'after';\n\nexport interface TabReorder {\n barId: string;\n sourceId: string;\n targetId: string;\n position: TabDropPosition;\n source: TabReorderSource;\n}\n\nexport type { TabActivation } from './tab-bar.js';\n\nexport interface WireTabBarsOptions {\n onReorder: (change: TabReorder) => void;\n /**\n * How arrow / Home / End keys activate tabs (default `'automatic'`):\n * - `'automatic'` moves roving focus **and** selects the focused tab (clicks it).\n * - `'manual'` moves roving focus only; the user selects with Enter / Space / click\n * (the ARIA Tabs manual-activation pattern). Use this when activation is a heavy or\n * side-effecting action (e.g. a tab that loads a project) so arrowing through the\n * strip doesn't trigger it on every tab.\n *\n * A per-bar `data-tab-activation=\"manual\" | \"automatic\"` attribute (see the `TabBar`\n * `activation` prop) overrides this option for that strip.\n */\n activation?: TabActivation;\n}\n\ntype TabRoot = HTMLElement;\ntype TabBarRoot = HTMLElement;\n\nconst AUTO_SCROLL_EDGE_PX = 56;\nconst AUTO_SCROLL_MIN_PX_PER_SECOND = 180;\nconst AUTO_SCROLL_MAX_PX_PER_SECOND = 900;\n\nexport function reorderTabs<T>(\n items: readonly T[],\n getId: (item: T) => string,\n sourceId: string,\n targetId: string,\n position: TabDropPosition,\n): T[] {\n if (sourceId === targetId) return [...items];\n const source = items.find((item) => getId(item) === sourceId);\n if (!source || !items.some((item) => getId(item) === targetId))\n return [...items];\n const remaining = items.filter((item) => getId(item) !== sourceId);\n const targetIndex = remaining.findIndex((item) => getId(item) === targetId);\n remaining.splice(targetIndex + (position === 'after' ? 1 : 0), 0, source);\n return remaining;\n}\n\nfunction tabRoot(target: EventTarget | null): TabRoot | undefined {\n return target instanceof Element\n ? (target.closest<TabRoot>('[data-component=\"app-tab\"]') ?? undefined)\n : undefined;\n}\n\nfunction tabBar(tab: TabRoot): TabBarRoot | undefined {\n return tab.closest<TabBarRoot>('[data-component=\"tab-bar\"]') ?? undefined;\n}\n\nfunction tabId(tab: TabRoot): string | undefined {\n return tab.dataset.tabId || undefined;\n}\n\nfunction barId(bar: TabBarRoot): string | undefined {\n return bar.dataset.tabBarId || undefined;\n}\n\nfunction positionFor(event: DragEvent, tab: TabRoot): TabDropPosition {\n const bounds = tab.getBoundingClientRect();\n return event.clientX < bounds.left + bounds.width / 2 ? 'before' : 'after';\n}\n\nfunction tabsIn(bar: TabBarRoot): HTMLButtonElement[] {\n return [\n ...bar.querySelectorAll<HTMLButtonElement>(\n '[data-kui-tab-list] [role=\"tab\"]',\n ),\n ];\n}\n\nfunction reveal(tab: HTMLElement | null | undefined): void {\n tab?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n if (\n tab\n ?.closest('[data-kui-tab-list]')\n ?.querySelector(':scope > .kui-app-tab[data-pinned=\"true\"]')\n )\n revealInStrip(tab);\n}\n\n/**\n * Scroll a tab's own strip, and nothing else, so the tab is fully visible.\n * Used for selection changes the application makes without focus (adding and\n * selecting a tab): unlike `scrollIntoView`, it never scrolls the page to a\n * bar that is out of view.\n */\nfunction revealInStrip(tab: HTMLElement): void {\n const strip = tab.closest<HTMLElement>('[data-kui-tab-list]');\n if (!strip || strip.scrollWidth <= strip.clientWidth) return;\n const bounds = strip.getBoundingClientRect();\n const snap = strip.dataset.snapTabs === 'true';\n const box = snap\n ? (tab.closest<HTMLElement>('.kui-app-tab')?.getBoundingClientRect() ??\n tab.getBoundingClientRect())\n : tab.getBoundingClientRect();\n const pinned = strip.querySelector<HTMLElement>(\n ':scope > .kui-app-tab[data-pinned=\"true\"]',\n );\n if (pinned?.contains(tab)) return;\n const pinnedBox = pinned?.getBoundingClientRect();\n if (\n strip.ownerDocument.defaultView?.getComputedStyle(strip).direction === 'rtl'\n ) {\n const right = pinnedBox\n ? Math.min(bounds.right, pinnedBox.left)\n : bounds.right;\n if (snap) strip.scrollLeft += box.right - right;\n else if (box.right > right) strip.scrollLeft += box.right - right;\n else if (box.left < bounds.left)\n strip.scrollLeft -= Math.min(bounds.left - box.left, right - box.right);\n return;\n }\n const left = pinnedBox ? Math.max(bounds.left, pinnedBox.right) : bounds.left;\n if (snap) strip.scrollLeft += box.left - left;\n else if (box.left < left) strip.scrollLeft -= left - box.left;\n else if (box.right > bounds.right)\n strip.scrollLeft += Math.min(box.right - bounds.right, box.left - left);\n}\n\n/** The selected tab button of every bar below root, keyed by bar. */\nfunction selectedTabs(root: HTMLElement | Document) {\n const selected = new Map<TabBarRoot, HTMLElement>();\n root\n .querySelectorAll<HTMLElement>(\n '[data-kui-tab-list] [role=\"tab\"][aria-selected=\"true\"]',\n )\n .forEach((button) => {\n const tab = tabRoot(button);\n const bar = tab && tabBar(tab);\n if (bar && !selected.has(bar)) selected.set(bar, button);\n });\n return selected;\n}\n\n/** Wire reordering and keyboard navigation while leaving controlled state in the application. */\nexport function wireTabBars(\n root: HTMLElement | Document,\n { onReorder, activation = 'automatic' }: WireTabBarsOptions,\n): () => void {\n const ownerDocument = (\n root.nodeType === 9 ? (root as Document) : root.ownerDocument\n )!;\n const view = ownerDocument.defaultView!;\n // WebKit positions an RTL sticky flex child as though the negative scroll\n // offset were added to its inline-start inset. Measure the actual pinned\n // edge and correct only the displacement; Chromium and Firefox stay at 0.\n const pinnedOffsets = new WeakMap<HTMLElement, number>();\n const adjustedPinned = new Set<HTMLElement>();\n const snapExtras = new WeakMap<HTMLElement, number>();\n const snapStrips = new Set<HTMLElement>();\n const snapObserved = new Set<HTMLElement>();\n const snapResizeObserver = view.ResizeObserver\n ? new view.ResizeObserver(() => syncSnapInRoot())\n : undefined;\n const observeSnap = (element: HTMLElement) => {\n if (snapObserved.has(element)) return;\n snapObserved.add(element);\n snapResizeObserver?.observe(element);\n };\n const syncSnap = (strip: HTMLElement) => {\n const previousExtra = snapExtras.get(strip) ?? 0;\n if (strip.dataset.snapTabs !== 'true') {\n strip.style.removeProperty('--kui-tab-bar-snap-inset');\n strip.style.removeProperty('--kui-tab-bar-snap-end-extra');\n snapExtras.delete(strip);\n snapStrips.delete(strip);\n return;\n }\n snapStrips.add(strip);\n observeSnap(strip);\n const tabs = [\n ...strip.querySelectorAll<HTMLElement>(':scope > .kui-app-tab'),\n ];\n tabs.forEach(observeSnap);\n const pinned = tabs.find(\n (candidate) => candidate.dataset.pinned === 'true',\n );\n const last = tabs.at(-1);\n const bounds = strip.getBoundingClientRect();\n const pinnedBox = pinned?.getBoundingClientRect();\n const rtl = view.getComputedStyle(strip).direction === 'rtl';\n const inset = pinnedBox\n ? rtl\n ? bounds.right - pinnedBox.left\n : pinnedBox.right - bounds.left\n : 0;\n strip.style.setProperty(\n '--kui-tab-bar-snap-inset',\n `${Math.max(0, inset)}px`,\n );\n const baseMaximum = Math.max(\n 0,\n strip.scrollWidth - strip.clientWidth - previousExtra,\n );\n let extra = 0;\n if (baseMaximum > 0 && last && last !== pinned) {\n const lastBox = last.getBoundingClientRect();\n const desired = rtl\n ? -(\n strip.scrollLeft +\n lastBox.right -\n (pinnedBox?.left ?? bounds.right)\n )\n : strip.scrollLeft + lastBox.left - (pinnedBox?.right ?? bounds.left);\n extra = Math.max(0, desired - baseMaximum);\n }\n if (Math.abs(extra - previousExtra) >= 0.5) {\n strip.style.setProperty('--kui-tab-bar-snap-end-extra', `${extra}px`);\n snapExtras.set(strip, extra);\n }\n };\n const syncSnapInRoot = () => {\n for (const element of snapObserved)\n if (!root.contains(element)) {\n snapResizeObserver?.unobserve(element);\n snapObserved.delete(element);\n }\n for (const strip of snapStrips)\n if (!root.contains(strip)) {\n strip.style.removeProperty('--kui-tab-bar-snap-inset');\n strip.style.removeProperty('--kui-tab-bar-snap-end-extra');\n snapStrips.delete(strip);\n }\n root.querySelectorAll<HTMLElement>('[data-kui-tab-list]').forEach(syncSnap);\n };\n const syncPinned = (strip: HTMLElement) => {\n const pinned = strip.querySelector<HTMLElement>(\n ':scope > .kui-app-tab[data-pinned=\"true\"]',\n );\n if (!pinned) return;\n const previous = pinned.style.translate\n ? (pinnedOffsets.get(pinned) ?? 0)\n : 0;\n const style = view.getComputedStyle(strip);\n if (style.direction !== 'rtl' || strip.scrollWidth <= strip.clientWidth) {\n if (previous !== 0) pinned.style.translate = '';\n pinnedOffsets.set(pinned, 0);\n return;\n }\n const targetRight =\n strip.getBoundingClientRect().right -\n (Number.parseFloat(style.borderRightWidth) || 0) -\n (Number.parseFloat(style.paddingRight) || 0);\n const rawRight = pinned.getBoundingClientRect().right - previous;\n const delta = targetRight - rawRight;\n const next = Math.abs(delta) < 0.5 ? 0 : delta;\n if (next !== previous) pinned.style.translate = next ? `${next}px 0` : '';\n pinnedOffsets.set(pinned, next);\n adjustedPinned.add(pinned);\n };\n const syncPinnedInRoot = () =>\n root\n .querySelectorAll<HTMLElement>('[data-kui-tab-list]')\n .forEach(syncPinned);\n const onStripScroll = (event: Event) => {\n const target = event.target;\n if (target instanceof HTMLElement && target.matches('[data-kui-tab-list]'))\n syncPinned(target);\n };\n // CSS snap settles direct gestures in most engines. WebKit can leave a\n // programmatic smooth scroll between snap points, so normalize its final\n // position to the nearest whole peer at the pinned inset.\n const onStripScrollEnd = (event: Event) => {\n const strip = event.target;\n if (\n !(strip instanceof HTMLElement) ||\n !strip.matches('[data-kui-tab-list][data-snap-tabs=\"true\"]') ||\n Math.abs(strip.scrollLeft) < 0.5\n )\n return;\n const peers = [\n ...strip.querySelectorAll<HTMLElement>(\n ':scope > .kui-app-tab:not([data-pinned=\"true\"])',\n ),\n ];\n if (!peers.length) return;\n const bounds = strip.getBoundingClientRect();\n const pinned = strip.querySelector<HTMLElement>(\n ':scope > .kui-app-tab[data-pinned=\"true\"]',\n );\n const pinnedBox = pinned?.getBoundingClientRect();\n const rtl = view.getComputedStyle(strip).direction === 'rtl';\n const edge = rtl\n ? (pinnedBox?.left ?? bounds.right)\n : (pinnedBox?.right ?? bounds.left);\n const distances = peers.map((peer) => {\n const box = peer.getBoundingClientRect();\n return rtl ? box.right - edge : box.left - edge;\n });\n const closest = distances.reduce((best, distance) =>\n Math.abs(distance) < Math.abs(best) ? distance : best,\n );\n if (Math.abs(closest) > 0.5) strip.scrollLeft += closest;\n };\n let autoScroll:\n | {\n strip: HTMLElement;\n velocity: number;\n frame: number | undefined;\n previousTime: number | undefined;\n }\n | undefined;\n const stopAutoScroll = () => {\n if (!autoScroll) return;\n if (autoScroll.frame !== undefined)\n view.cancelAnimationFrame(autoScroll.frame);\n delete autoScroll.strip.dataset.tabAutoscroll;\n autoScroll = undefined;\n };\n const runAutoScroll = (time: number) => {\n const current = autoScroll;\n if (!current) return;\n current.frame = undefined;\n const elapsed =\n current.previousTime === undefined\n ? 1000 / 60\n : Math.min(40, Math.max(1, time - current.previousTime));\n current.previousTime = time;\n const maximum = Math.max(\n 0,\n current.strip.scrollWidth - current.strip.clientWidth,\n );\n const previous = current.strip.scrollLeft;\n current.strip.scrollLeft = Math.max(\n 0,\n Math.min(maximum, previous + (current.velocity * elapsed) / 1000),\n );\n if (current.strip.scrollLeft === previous) {\n stopAutoScroll();\n return;\n }\n current.frame = view.requestAnimationFrame(runAutoScroll);\n };\n const updateAutoScroll = (event: DragEvent, strip: HTMLElement) => {\n if (strip.scrollWidth <= strip.clientWidth) {\n stopAutoScroll();\n return;\n }\n const bounds = strip.getBoundingClientRect();\n const edge = Math.min(AUTO_SCROLL_EDGE_PX, bounds.width / 3);\n const startDistance = event.clientX - bounds.left;\n const endDistance = bounds.right - event.clientX;\n let direction = 0;\n let strength = 0;\n if (startDistance >= 0 && startDistance < edge) {\n direction = -1;\n strength = 1 - startDistance / edge;\n } else if (endDistance >= 0 && endDistance < edge) {\n direction = 1;\n strength = 1 - endDistance / edge;\n }\n if (direction === 0) {\n stopAutoScroll();\n return;\n }\n const maximum = Math.max(0, strip.scrollWidth - strip.clientWidth);\n if (\n (direction < 0 && strip.scrollLeft <= 0) ||\n (direction > 0 && strip.scrollLeft >= maximum)\n ) {\n stopAutoScroll();\n return;\n }\n const speed =\n AUTO_SCROLL_MIN_PX_PER_SECOND +\n (AUTO_SCROLL_MAX_PX_PER_SECOND - AUTO_SCROLL_MIN_PX_PER_SECOND) *\n strength *\n strength;\n if (autoScroll?.strip !== strip) stopAutoScroll();\n autoScroll ??= {\n strip,\n velocity: 0,\n frame: undefined,\n previousTime: undefined,\n };\n autoScroll.velocity = direction * speed;\n strip.dataset.tabAutoscroll = direction < 0 ? 'start' : 'end';\n autoScroll.frame ??= view.requestAnimationFrame(runAutoScroll);\n };\n let dragged: { barId: string; tabId: string } | undefined;\n const clearDropPositions = () =>\n root\n .querySelectorAll<HTMLElement>('[data-tab-drop-position]')\n .forEach((tab) => delete tab.dataset.tabDropPosition);\n const clear = () => {\n stopAutoScroll();\n dragged = undefined;\n root\n .querySelectorAll<HTMLElement>(\n '[data-tab-dragging], [data-tab-drop-position]',\n )\n .forEach((tab) => {\n delete tab.dataset.tabDragging;\n delete tab.dataset.tabDropPosition;\n });\n };\n let disposed = false;\n const afterControlledRender = (sourceBarId: string, sourceTabId: string) =>\n globalThis.queueMicrotask(() => {\n if (disposed) return;\n const tab = [\n ...root.querySelectorAll<TabRoot>('[data-component=\"app-tab\"]'),\n ].find((candidate) => {\n const candidateBar = tabBar(candidate);\n return (\n tabId(candidate) === sourceTabId &&\n candidateBar !== undefined &&\n barId(candidateBar) === sourceBarId\n );\n });\n const button = tab?.querySelector<HTMLButtonElement>('[role=\"tab\"]');\n button?.focus();\n reveal(button);\n });\n\n const onDragStart = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar = tab && tabBar(tab);\n const sourceId = tab && tabId(tab);\n const sourceBarId = bar && barId(bar);\n if (\n !tab ||\n tab.getAttribute('draggable') !== 'true' ||\n !sourceId ||\n !sourceBarId\n )\n return;\n dragged = { barId: sourceBarId, tabId: sourceId };\n tab.dataset.tabDragging = 'true';\n if (dragEvent.dataTransfer) {\n dragEvent.dataTransfer.effectAllowed = 'move';\n dragEvent.dataTransfer.setData(\n 'application/x-kerf-tab',\n `${sourceBarId}:${sourceId}`,\n );\n }\n };\n const onDragOver = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar =\n event.target instanceof Element\n ? (event.target.closest<TabBarRoot>('[data-component=\"tab-bar\"]') ??\n undefined)\n : undefined;\n const targetId = tab && tabId(tab);\n const targetBarId = bar && barId(bar);\n if (!dragged || !bar || targetBarId !== dragged.barId) {\n stopAutoScroll();\n clearDropPositions();\n return;\n }\n dragEvent.preventDefault();\n const strip = bar.querySelector<HTMLElement>('[data-kui-tab-list]');\n if (strip) updateAutoScroll(dragEvent, strip);\n else stopAutoScroll();\n clearDropPositions();\n if (!tab || !targetId || targetId === dragged.tabId) return;\n tab.dataset.tabDropPosition = positionFor(dragEvent, tab);\n if (dragEvent.dataTransfer) dragEvent.dataTransfer.dropEffect = 'move';\n };\n const onDrop = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar = tab && tabBar(tab);\n const targetId = tab && tabId(tab);\n const targetBarId = bar && barId(bar);\n if (\n !dragged ||\n !tab ||\n !targetId ||\n targetId === dragged.tabId ||\n targetBarId !== dragged.barId\n ) {\n clear();\n return;\n }\n dragEvent.preventDefault();\n const change: TabReorder = {\n barId: dragged.barId,\n sourceId: dragged.tabId,\n targetId,\n position: positionFor(dragEvent, tab),\n source: 'pointer',\n };\n clear();\n onReorder(change);\n afterControlledRender(change.barId, change.sourceId);\n };\n const onKeyDown = (event: Event) => {\n const keyboardEvent = event as KeyboardEvent;\n const button =\n event.target instanceof Element\n ? event.target.closest<HTMLButtonElement>('[role=\"tab\"]')\n : null;\n const tab = button && tabRoot(button);\n const bar = tab && tabBar(tab);\n if (!button || !tab || !bar) return;\n if (keyboardEvent.key === 'Delete' || keyboardEvent.key === 'Backspace') {\n const close = tab.querySelector<HTMLButtonElement>('.kui-app-tab__close');\n if (!close) return;\n keyboardEvent.preventDefault();\n close.click();\n return;\n }\n const tabs = tabsIn(bar);\n const current = tabs.indexOf(button);\n if (current < 0) return;\n if (\n keyboardEvent.altKey &&\n keyboardEvent.shiftKey &&\n (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowRight')\n ) {\n const target =\n tabs[current + (keyboardEvent.key === 'ArrowLeft' ? -1 : 1)];\n const sourceId = tabId(tab);\n const targetRoot = target && tabRoot(target);\n const targetId = targetRoot && tabId(targetRoot);\n const sourceBarId = barId(bar);\n if (\n !target ||\n !sourceId ||\n !targetId ||\n !sourceBarId ||\n tab.getAttribute('draggable') !== 'true'\n )\n return;\n keyboardEvent.preventDefault();\n onReorder({\n barId: sourceBarId,\n sourceId,\n targetId,\n position: keyboardEvent.key === 'ArrowLeft' ? 'before' : 'after',\n source: 'keyboard',\n });\n afterControlledRender(sourceBarId, sourceId);\n return;\n }\n let next: number | undefined;\n if (keyboardEvent.key === 'ArrowRight') next = (current + 1) % tabs.length;\n else if (keyboardEvent.key === 'ArrowLeft')\n next = (current - 1 + tabs.length) % tabs.length;\n else if (keyboardEvent.key === 'Home') next = 0;\n else if (keyboardEvent.key === 'End') next = tabs.length - 1;\n if (next === undefined) return;\n keyboardEvent.preventDefault();\n // A per-bar data-tab-activation attribute overrides the wireTabBars option.\n const perBar = bar.dataset.tabActivation;\n const mode: TabActivation =\n perBar === 'manual' || perBar === 'automatic' ? perBar : activation;\n const target = tabs[next]!;\n target.focus();\n // Manual activation moves roving focus only; the user selects with Enter / Space\n // (native on the tab <button>) or click. Automatic also selects the focused tab.\n if (mode === 'automatic') {\n target.click();\n // Activation can synchronously replace the controlled strip. Resolve the\n // logical tab again after rendering instead of leaving focus on a dead node.\n if (!target.isConnected)\n afterControlledRender(\n bar.dataset.tabBarId!,\n tabRoot(target)!.dataset.tabId!,\n );\n }\n reveal(target);\n };\n const onFocusIn = (event: Event) => {\n const tab =\n event.target instanceof HTMLElement &&\n event.target.matches('[role=\"tab\"]')\n ? event.target\n : undefined;\n reveal(tab);\n };\n\n root.addEventListener('dragstart', onDragStart);\n root.addEventListener('dragover', onDragOver);\n root.addEventListener('drop', onDrop);\n root.addEventListener('dragend', clear);\n root.addEventListener('keydown', onKeyDown);\n root.addEventListener('focusin', onFocusIn);\n root.addEventListener('scroll', onStripScroll, true);\n root.addEventListener('scrollend', onStripScrollEnd, true);\n syncSnapInRoot();\n root\n .querySelectorAll<HTMLElement>(\n '[data-kui-tab-list] [role=\"tab\"][aria-selected=\"true\"]',\n )\n .forEach(reveal);\n\n // Reveal a tab the application selects without focus (for example a tab it\n // adds and selects) once its controlled render lands. Only a change of a\n // bar's selected tab reveals, so a re-render that keeps the selection never\n // pulls a strip the user scrolled back to it.\n const selectedIds = new Map<string, string>();\n const recordSelection = () => {\n const changed: HTMLElement[] = [];\n for (const [bar, button] of selectedTabs(root)) {\n const id = barId(bar);\n const tab = tabRoot(button);\n const selectedId = tab && tabId(tab);\n if (!id || !selectedId) continue;\n if (selectedIds.has(id) && selectedIds.get(id) !== selectedId)\n changed.push(button);\n selectedIds.set(id, selectedId);\n }\n return changed;\n };\n recordSelection();\n syncPinnedInRoot();\n const selectionObserver = new view.MutationObserver(() => {\n if (disposed) return;\n syncSnapInRoot();\n for (const button of recordSelection())\n if (ownerDocument.activeElement !== button) revealInStrip(button);\n syncPinnedInRoot();\n });\n selectionObserver.observe(root, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['aria-selected', 'dir', 'data-snap-tabs', 'data-pinned'],\n });\n\n return () => {\n disposed = true;\n selectionObserver.disconnect();\n snapResizeObserver?.disconnect();\n for (const strip of snapStrips) {\n strip.style.removeProperty('--kui-tab-bar-snap-inset');\n strip.style.removeProperty('--kui-tab-bar-snap-end-extra');\n }\n snapStrips.clear();\n snapObserved.clear();\n clear();\n root.removeEventListener('dragstart', onDragStart);\n root.removeEventListener('dragover', onDragOver);\n root.removeEventListener('drop', onDrop);\n root.removeEventListener('dragend', clear);\n root.removeEventListener('keydown', onKeyDown);\n root.removeEventListener('focusin', onFocusIn);\n root.removeEventListener('scroll', onStripScroll, true);\n root.removeEventListener('scrollend', onStripScrollEnd, true);\n for (const pinned of adjustedPinned) pinned.style.translate = '';\n adjustedPinned.clear();\n };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/toolbar-text.tsx"],"names":[],"mappings":";;;;AAqDO,SAAS,WAAA,CAAY;AAAA,EAC1B,IAAA;AAAA,EACA,IAAA,GAAO,SAAA;AAAA,EACP,SAAA,GAAY,EAAA;AAAA,EACZ,EAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA,GAAc,KAAA;AAAA,EACd,IAAA,GAAO,KAAA;AAAA,EACP,QAAA,GAAW,IAAA;AAAA,EACX,QAAA,GAAW,IAAA;AAAA,EACX,IAAA,GAAO,KAAA;AAAA,EACP;AACF,CAAA,EAAqB;AACnB,EAAA,MAAM,MAAA,GAAS,IAAA,IAAQ,QAAA,IAAY,IAAA,IAAQ,QAAA,GAAW,CAAA;AACtD,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MAC5C,gBAAA,EAAe,cAAA;AAAA,MACf,WAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAW,OAAO,MAAA,GAAS,MAAA;AAAA,MAC3B,WAAA,EAAW,OAAO,MAAA,GAAS,MAAA;AAAA,MAC3B,eAAA,EAAe,WAAW,MAAA,GAAY,OAAA;AAAA,MACtC,gBAAA,EAAgB,MAAA,GAAS,MAAA,CAAO,QAAQ,CAAA,GAAI,MAAA;AAAA,MAC5C,KAAA,EAAO,MAAA,GAAS,CAAA,6BAAA,EAAgC,QAAQ,CAAA,CAAA,GAAK,MAAA;AAAA,MAC7D,kBAAA,EAAkB,cAAc,MAAA,GAAS,MAAA;AAAA,MACzC,EAAA;AAAA,MACA,IAAA,EAAM,eAAe,SAAA,GAAY,MAAA;AAAA,MACjC,YAAA,EAAY,YAAA,GAAe,MAAA,CAAO,YAAY,CAAA,GAAI,MAAA;AAAA,MAClD,WAAA,EAAW,cAAc,MAAA,GAAS,MAAA;AAAA,MAClC,IAAA;AAAA,MAEC,QAAA,EAAA,WAAA,mBACC,GAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAO,EAAA,CAAG,CAAC,CAAA,EAAG,CAAA,mBAExB,GAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAM,wBAAA,EAA0B,QAAA,EAAA,IAAA,EAAK;AAAA;AAAA,GAE/C;AAEJ","file":"chunk-V3DX3K65.js","sourcesContent":["import { em } from './css-values.js';\nimport { Skeleton } from './skeleton.js';\n\nexport type ToolbarTextSize =\n 'xlarge' | 'xlarge-fixed' | 'large' | 'default' | 'small' | 'xsmall';\n\n/** ARIA heading level for a title exposed as a heading landmark. */\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\ninterface ToolbarTextBaseProps {\n text: string;\n size?: ToolbarTextSize;\n className?: string;\n /** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */\n id?: string;\n /**\n * Expose heading semantics (`role=\"heading\"` + `aria-level`) so the text acts as\n * a heading landmark — e.g. a page's primary title. Omit to keep the plain span\n * (the default), which suits a dialog title referenced via `aria-labelledby`.\n */\n headingLevel?: HeadingLevel;\n /** Render the text as an unanimated loading skeleton instead of its value. */\n placeholder?: boolean;\n /**\n * Show a trailing ellipsis (…) where the text is truncated — on the single line\n * (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip\n * instead. Default true.\n */\n ellipsis?: boolean;\n /**\n * Grow to fill the free space of the flex row it sits in (a Toolbar zone),\n * truncating at the space left by its siblings instead of taking only its\n * text's width. Default false.\n */\n fill?: boolean;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n /**\n * Cap wrapped text to this many lines, truncating past it. Only takes effect with\n * `wrap`; ignored on a single line. `null`/omitted wraps without a line cap. Default null.\n */\n}\n\ntype ToolbarTextWrappingProps =\n | {\n /** Wrap onto multiple lines; combine with `maxLines` to cap them. */\n wrap: true;\n maxLines?: number | null;\n }\n | { wrap?: false; maxLines?: never };\n\nexport type ToolbarTextProps = ToolbarTextBaseProps & ToolbarTextWrappingProps;\n\nexport function ToolbarText({\n text,\n size = 'default',\n className = '',\n id,\n headingLevel,\n placeholder = false,\n wrap = false,\n ellipsis = true,\n maxLines = null,\n fill = false,\n slot,\n}: ToolbarTextProps) {\n const capped = wrap && maxLines != null && maxLines > 0;\n return (\n <span\n class={`kui-toolbar-text ${className}`.trim()}\n data-component=\"toolbar-text\"\n data-size={size}\n data-fill={fill ? 'true' : undefined}\n data-wrap={wrap ? 'true' : undefined}\n data-ellipsis={ellipsis ? undefined : 'false'}\n data-max-lines={capped ? String(maxLines) : undefined}\n style={capped ? `--kui-toolbar-text-max-lines:${maxLines}` : undefined}\n data-placeholder={placeholder ? 'true' : undefined}\n id={id}\n role={headingLevel ? 'heading' : undefined}\n aria-level={headingLevel ? String(headingLevel) : undefined}\n aria-busy={placeholder ? 'true' : undefined}\n slot={slot}\n >\n {placeholder ? (\n <Skeleton width={em(8)} />\n ) : (\n <span class=\"kui-toolbar-text__text\">{text}</span>\n )}\n </span>\n );\n}\n"]}
|