@crewlethq/ui 0.3.0 → 0.4.1

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.
Files changed (47) hide show
  1. package/dist/Card/Card.d.ts +23 -1
  2. package/dist/Card/index.js +1 -1
  3. package/dist/{Card-AHX3Q2JD.css → Card-RLR254LD.css} +22 -0
  4. package/dist/CodeBlock/CodeBlock.d.ts +68 -9
  5. package/dist/CodeBlock/index.js +3 -3
  6. package/dist/{CodeBlock-U2W6NNVC.css → CodeBlock-GYNJ2HI6.css} +50 -15
  7. package/dist/Copyable/CopyButton.d.ts +11 -1
  8. package/dist/Copyable/Copyable.d.ts +9 -1
  9. package/dist/Copyable/index.js +2 -2
  10. package/dist/DataTable/index.js +2 -2
  11. package/dist/DataView/index.js +3 -3
  12. package/dist/FilterChip/FilterChip.d.ts +37 -5
  13. package/dist/FilterChip/index.d.ts +1 -1
  14. package/dist/FilterChip/index.js +1 -1
  15. package/dist/Meter/Meter.d.ts +79 -2
  16. package/dist/Meter/index.d.ts +2 -2
  17. package/dist/Meter/index.js +1 -1
  18. package/dist/SegmentedControl/SegmentedControl.d.ts +36 -3
  19. package/dist/SegmentedControl/index.d.ts +1 -1
  20. package/dist/SegmentedControl/index.js +2 -2
  21. package/dist/Select/Select.d.ts +16 -1
  22. package/dist/Select/index.js +1 -1
  23. package/dist/Tabs/index.js +1 -1
  24. package/dist/Tabs/roving.d.ts +32 -1
  25. package/dist/ThemeSwitcher/index.js +3 -3
  26. package/dist/TimeWindowPicker/index.js +2 -2
  27. package/dist/{chunk-GUIF6Q6N.js → chunk-2HQR4U4D.js} +1 -1
  28. package/dist/{chunk-GHYY2WPF.js → chunk-34VGEDVV.js} +1 -1
  29. package/dist/{chunk-D3OADIM6.js → chunk-3QRYCPXQ.js} +7 -3
  30. package/dist/{chunk-V663QU4R.js → chunk-5VNUYO76.js} +16 -5
  31. package/dist/chunk-6ZFVFA56.js +108 -0
  32. package/dist/{chunk-HTDFCRJZ.js → chunk-D35VTV2Z.js} +20 -6
  33. package/dist/{chunk-T7FKOJKW.js → chunk-NDCATSW7.js} +11 -3
  34. package/dist/{chunk-5IMACPJS.js → chunk-OFHBK7RI.js} +5 -2
  35. package/dist/{chunk-YMWONFSI.js → chunk-QEUY3A5P.js} +19 -3
  36. package/dist/{chunk-G2QFRTJ2.js → chunk-WOYYD2YR.js} +1 -1
  37. package/dist/{chunk-EHMQQHYR.js → chunk-WQX73TWF.js} +1 -1
  38. package/dist/{chunk-LGCPQIOZ.js → chunk-YODNNSZL.js} +23 -9
  39. package/dist/{chunk-EPOOQZYA.js → chunk-ZFATEERP.js} +32 -6
  40. package/dist/generated/index.js +16 -16
  41. package/dist/index.js +16 -16
  42. package/dist/styles.css +74 -17
  43. package/dist/utils/index.d.ts +1 -1
  44. package/dist/utils/index.js +1 -1
  45. package/dist/utils/useClipboard.d.ts +56 -4
  46. package/package.json +3 -3
  47. package/dist/chunk-ARMRX2YV.js +0 -96
@@ -131,12 +131,34 @@ export interface CardTitleProps extends HTMLAttributes<HTMLHeadingElement> {
131
131
  * heading and an `h2` at the top of a page.
132
132
  */
133
133
  as?: `h${HeadingLevel}` | undefined;
134
+ /**
135
+ * A title with nowhere left to go ENDS in an ellipsis rather than being cut.
136
+ *
137
+ * The head is one line and the name block clips, so a title longer than the
138
+ * row is stopped at the header's edge — mid-word, with nothing to say a word
139
+ * was lost. `text-overflow` is the answer to that and it applies to a BLOCK
140
+ * container only, never to a flex one, and the title is flex: the text
141
+ * inside it is an anonymous flex item that no rule in this stylesheet can
142
+ * reach. So the ellipsis is a change of the title's own display, which is a
143
+ * change to what a caller's own children do inside it, which is why it is
144
+ * asked for rather than assumed.
145
+ *
146
+ * OFF BY DEFAULT, because it is not free: a truncating title lays its
147
+ * children out as one line of inline content instead of as centred,
148
+ * gap-separated flex items, so a caller who put a glyph or a tag inside the
149
+ * title gets it on the text baseline with no gap. `Card.Header`'s own `icon`
150
+ * slot is where a glyph belongs, and it is outside the title and unaffected.
151
+ * A title of plain text — which is every title in this package and every one
152
+ * in the engine dashboard — reads identically either way, and is exactly the
153
+ * case that was being cut.
154
+ */
155
+ truncate?: boolean | undefined;
134
156
  }
135
157
  export declare const Card: (({ variant, padding, interactive, rail, selected, as, href, asChild, className, children, ...rest }: CardProps) => import("react/jsx-runtime").JSX.Element) & {
136
158
  Header: ({ divided, icon, count, subtitle, actions, className, children, ...rest }: CardHeaderProps) => import("react/jsx-runtime").JSX.Element;
137
159
  Body: ({ padding, className, children, ...rest }: CardSectionProps) => import("react/jsx-runtime").JSX.Element;
138
160
  Footer: ({ variant, padding, className, children, ...rest }: CardFooterProps) => import("react/jsx-runtime").JSX.Element;
139
- Title: ({ as, className, children, ...rest }: CardTitleProps) => import("react/jsx-runtime").JSX.Element;
161
+ Title: ({ as, truncate, className, children, ...rest }: CardTitleProps) => import("react/jsx-runtime").JSX.Element;
140
162
  Description: ({ className, children, ...rest }: HTMLAttributes<HTMLParagraphElement>) => import("react/jsx-runtime").JSX.Element;
141
163
  };
142
164
  export {};
@@ -1,4 +1,4 @@
1
- export { Card } from '../chunk-T7FKOJKW.js';
1
+ export { Card } from '../chunk-NDCATSW7.js';
2
2
  export { useCardHeaderSlot } from '../chunk-AV2DXAEL.js';
3
3
  import '../chunk-BHHIIK3M.js';
4
4
  import '../chunk-PQADV6P6.js';
@@ -394,6 +394,28 @@
394
394
  color: var(--color-text-primary);
395
395
  }
396
396
 
397
+ /*
398
+ * A TITLE THAT ENDS RATHER THAN STOPS, where a caller has asked for it.
399
+ *
400
+ * The name block clips, which is what keeps the head one line, so a title
401
+ * wider than the row is cut at the header's edge — mid-word, with nothing to
402
+ * say a word was lost. `text-overflow` is the rule that says it, and it
403
+ * applies to a BLOCK container only: on the flex title above it does nothing
404
+ * at all, and the text it would act on is an anonymous flex item that no
405
+ * selector here can reach. So the modifier makes the title a block. Its
406
+ * children are then one line of inline content rather than centred flex items
407
+ * with a gap between them, which is the whole of what a caller opts into, and
408
+ * why this is a modifier rather than the base rule.
409
+ *
410
+ * `white-space: nowrap` is already the title's own, and `min-width: 0` is what
411
+ * lets the block shrink below its content inside the name block; both stand.
412
+ */
413
+ .crewlet-card__title--truncate {
414
+ display: block;
415
+ overflow: hidden;
416
+ text-overflow: ellipsis;
417
+ }
418
+
397
419
  /*
398
420
  * The subtitle gives up its tail and the title does not: a title is the
399
421
  * record's identity and has to be read in full, while a subtitle is context.
@@ -19,31 +19,90 @@ interface CodeBlockLook extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>
19
19
  * trace, and a sideways scrollbar on one of those is a line nobody finds the
20
20
  * end of. Turn it off for a block whose columns are the point: a diff, a
21
21
  * table of output, a log that is aligned on purpose.
22
+ *
23
+ * THIS IS THE WRAPPING PROP, and the only one. `plain` is about the header;
24
+ * see its note.
22
25
  */
23
26
  wrap?: boolean | undefined;
24
27
  /**
25
- * A ceiling, after which the block scrolls itself: a CSS length, or a number
26
- * of px. Without one a 900-line record pushes everything under it off the
27
- * screen.
28
+ * A ceiling for THIS block, after which it scrolls itself: a CSS length, or
29
+ * a number of px. Without one a 900-line record pushes everything under it
30
+ * off the screen.
31
+ *
32
+ * There is no default, and a ceiling every caller has to remember is a
33
+ * ceiling somebody forgets — so the value is also readable from the CASCADE.
34
+ * An application that wants one ceiling for every block it renders declares
35
+ * `--crewlet-codeblock-max-height` on an ancestor (`:root`, a route's own
36
+ * wrapper) and passes nothing here; this prop then OVERRIDES that ancestor
37
+ * for one block, `maxHeight="none"` escapes it, and with neither set the
38
+ * block is unbounded exactly as it has always been.
28
39
  */
29
40
  maxHeight?: string | number | undefined;
30
- /** No header at all, for a block inside a row that already has one. */
41
+ /**
42
+ * No HEADER at all, for a block inside a row that already has one: no
43
+ * filename strip, no language, no copy control.
44
+ *
45
+ * IT IS ABOUT THE CHROME, NEVER THE TEXT. A `plain` block still wraps, and a
46
+ * block with a header wraps too — wrapping is `wrap` and nothing else. The
47
+ * name is worth reading twice when porting, because it is a word other code
48
+ * blocks spend on the opposite meaning: a `plain` that turned WRAPPING off
49
+ * is `wrap={false}` here.
50
+ */
31
51
  plain?: boolean | undefined;
32
52
  }
33
53
  /**
34
- * `selectable` and `label` travel together, and the type is what says so: a
35
- * focusable `role="region"` with no accessible name is a tab stop a screen
36
- * reader announces as nothing, which is worse than the plain block it
37
- * replaced. A union rather than two optional props, because an optional label
38
- * beside an optional flag is a label somebody forgets.
54
+ * WHAT MAKES A BLOCK FOCUSABLE IS TWO DECISIONS, AND A NAME IS WHAT BOTH OWE.
55
+ *
56
+ * `selectable` is an INTENT only the caller has: this block is the record the
57
+ * screen is about, so while it has focus Command or Control plus A means this
58
+ * record rather than the document.
59
+ *
60
+ * `focusWhenScrollable` is an OBLIGATION the component discharges for itself.
61
+ * The `pre` is `overflow: auto`, so a ceiling or `wrap={false}` can turn any
62
+ * block into a scroll container, and a scroll container no keyboard can reach
63
+ * is content no keyboard can read. Whether a given block is one is a fact
64
+ * about the RENDERED BOX, not about any prop — the same code overflows at one
65
+ * viewport and not at the next — so the component measures it. A card carrying
66
+ * dozens of tool-argument blocks then gets a tab stop in front of the two that
67
+ * scroll rather than in front of all of them.
68
+ *
69
+ * They are separate because they answer different questions, and welding them
70
+ * gets both cases wrong: a block promoted for its scrollbar would start
71
+ * swallowing a chord nobody pressed for it, and a block that owns the chord
72
+ * would have to scroll to be named.
73
+ *
74
+ * `label` is the part they SHARE, and the type is what says so: a focusable
75
+ * `role="region"` with no accessible name is a tab stop a screen reader
76
+ * announces as nothing, which is worse than the plain block it replaced. So
77
+ * every arm that can take focus demands one, from either door, and the arm
78
+ * that cannot take focus refuses one — an optional label beside an optional
79
+ * flag is a label somebody forgets, and a label on a block that can never be
80
+ * named is a prop that silently does nothing.
39
81
  */
40
82
  export type CodeBlockProps = CodeBlockLook & ({
41
83
  /** Take Command or Control plus A while focused, and take focus. */
42
84
  selectable: true;
85
+ /** Also take focus when the rendered box turns out to scroll. */
86
+ focusWhenScrollable?: boolean | undefined;
43
87
  /** What this block is: "The turn record, as JSON". */
44
88
  label: string;
45
89
  } | {
46
90
  selectable?: false | undefined;
91
+ /**
92
+ * Take focus when, and only when, the rendered box scrolls — on
93
+ * either axis. The block is a named region then and nothing more: the
94
+ * select-all chord stays with the browser.
95
+ */
96
+ focusWhenScrollable: true;
97
+ /**
98
+ * What this block is. Required although the block may never take
99
+ * focus, because whether it does is a property of the box at one
100
+ * viewport and not of this call.
101
+ */
102
+ label: string;
103
+ } | {
104
+ selectable?: false | undefined;
105
+ focusWhenScrollable?: false | undefined;
47
106
  label?: never;
48
107
  });
49
108
  /**
@@ -1,6 +1,6 @@
1
- export { CodeBlock } from '../chunk-HTDFCRJZ.js';
2
- import '../chunk-D3OADIM6.js';
3
- import '../chunk-V663QU4R.js';
1
+ export { CodeBlock } from '../chunk-D35VTV2Z.js';
2
+ import '../chunk-3QRYCPXQ.js';
3
+ import '../chunk-5VNUYO76.js';
4
4
  import '../chunk-F27EPQ7D.js';
5
5
  import '../chunk-BZYDZBAN.js';
6
6
  import '../chunk-BZFWDIV6.js';
@@ -66,6 +66,42 @@
66
66
  white-space: pre-wrap;
67
67
  overflow-wrap: anywhere;
68
68
  tab-size: 2;
69
+ /*
70
+ * THE CEILING IS A CASCADING ONE, read here and declared nowhere, so the
71
+ * fallback IS the default: with nothing set the block is unbounded, as it
72
+ * has always been. `maxHeight` writes the property inline on the block, and
73
+ * an application that wants one ceiling for every block it renders writes it
74
+ * once on an ancestor instead — because a ceiling every call site has to
75
+ * remember is a ceiling somebody forgets, and the right height is a fact
76
+ * about the SURFACE the block sits on rather than about the component.
77
+ *
78
+ * Declared with a fallback rather than seeded on `.crewlet-codeblock`: a
79
+ * name a component declares on itself stops inheriting, so seeding it would
80
+ * be the one shape in which an ancestor's value can never arrive.
81
+ *
82
+ * ONE DECLARATION, in the rule below. `.crewlet-codeblock--bounded` is still
83
+ * emitted as the marker that this block set a ceiling of its own — the hook
84
+ * an application stylesheet and a reader of the DOM key on — but it draws
85
+ * nothing, because a second rule restating this height is how the two come
86
+ * to disagree.
87
+ */
88
+ }
89
+
90
+ /*
91
+ * The ceiling, at the specificity the rule it replaces already had.
92
+ *
93
+ * `.crewlet-codeblock .crewlet-codeblock__pre` rather than
94
+ * `.crewlet-codeblock__pre`, and the extra class earns its place: the rule
95
+ * that used to set this was `.crewlet-codeblock--bounded .crewlet-codeblock__pre`
96
+ * at (0,2,0), and an application overriding it wrote `.crewlet-codeblock__pre
97
+ * { max-height: … }` at (0,1,0) and expected to LOSE. Declaring it on the
98
+ * single class drops this to (0,1,0), which turns every one of those
99
+ * overrides into a tie decided by sheet order — so a consumer who wrote a
100
+ * losing rule on purpose silently starts winning. Same two-class weight,
101
+ * same outcome for anybody who already had an opinion.
102
+ */
103
+ .crewlet-codeblock .crewlet-codeblock__pre {
104
+ max-height: var(--crewlet-codeblock-max-height, none);
69
105
  }
70
106
 
71
107
  /*
@@ -78,21 +114,20 @@
78
114
  }
79
115
 
80
116
  /*
81
- * Bounded, with its own scroll. Without a ceiling a long record pushes
82
- * everything under it off the screen, which on a detail page is every other
83
- * fact about the thing being read.
84
- */
85
- .crewlet-codeblock--bounded .crewlet-codeblock__pre {
86
- max-height: var(--crewlet-codeblock-max-height);
87
- }
88
-
89
- /*
90
- * A BLOCK THAT OWNS SELECT-ALL. Select-all is a document verb, so on a screen
91
- * that is mostly one record it took the nav, the stat row and every card with
92
- * it. While this block has focus the verb means "this record", which is the
93
- * only thing anybody was reaching for, and the ring is what says so: a
94
- * keyboard verb that changes meaning on focus, with no visible focus state, is
95
- * a secret.
117
+ * A BLOCK A READER CAN REACH. Two things put the block in the tab order and
118
+ * the role follows both: `selectable`, and `focusWhenScrollable` on a box that
119
+ * turned out to scroll.
120
+ *
121
+ * On the first, select-all is a document verb, so on a screen that is mostly
122
+ * one record it took the nav, the stat row and every card with it. While this
123
+ * block has focus the verb means "this record", which is the only thing
124
+ * anybody was reaching for, and the ring is what says so: a keyboard verb that
125
+ * changes meaning on focus, with no visible focus state, is a secret. On the
126
+ * second the block keeps no keys at all; the ring is there because a scroll
127
+ * container a reader arrows through has to say where they are.
128
+ *
129
+ * The text cursor is right for both, and it is what the caret-shape of a
130
+ * `pre` full of selectable text should always have been.
96
131
  */
97
132
  .crewlet-codeblock__pre[role='region'] {
98
133
  cursor: text;
@@ -41,6 +41,16 @@ export interface CopyButtonProps {
41
41
  size?: ButtonSize | undefined;
42
42
  variant?: ButtonVariant | undefined;
43
43
  title?: string | undefined;
44
+ /** How long `copiedLabel` stands before the control offers its action again. */
45
+ resetMs?: number | undefined;
46
+ /**
47
+ * How long `failedLabel` stands. Unset it is `resetMs`; `null` holds it
48
+ * until the next press. See [ClipboardOptions.failedResetMs] for why a
49
+ * refusal wants a clock of its own — and note the cost of `null`, which is
50
+ * that the control's accessible name stays "Copy failed" and so names a
51
+ * status rather than the action it still performs.
52
+ */
53
+ failedResetMs?: number | null | undefined;
44
54
  className?: string | undefined;
45
55
  }
46
56
  /**
@@ -51,4 +61,4 @@ export interface CopyButtonProps {
51
61
  * sighted reader gets and the status region is the only evidence anybody else
52
62
  * gets. Both are part of the control, not decoration on it.
53
63
  */
54
- export declare function CopyButton({ text, label, copiedLabel, failedLabel, copiedMessage, failedMessage, size, variant, title, className, }: CopyButtonProps): import("react/jsx-runtime").JSX.Element;
64
+ export declare function CopyButton({ text, label, copiedLabel, failedLabel, copiedMessage, failedMessage, size, variant, title, resetMs, failedResetMs, className, }: CopyButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -16,6 +16,14 @@ export interface CopyableProps {
16
16
  copiedMessage?: string | undefined;
17
17
  /** Read out on failure. */
18
18
  failedMessage?: string | undefined;
19
+ /** How long the tick stands before the control settles back. */
20
+ resetMs?: number | undefined;
21
+ /**
22
+ * How long the refusal glyph stands. Unset it is `resetMs`; `null` holds it
23
+ * until the next press. See [ClipboardOptions.failedResetMs] for why a
24
+ * refusal wants a clock of its own.
25
+ */
26
+ failedResetMs?: number | null | undefined;
19
27
  className?: string | undefined;
20
28
  }
21
29
  /**
@@ -31,4 +39,4 @@ export interface CopyableProps {
31
39
  * plain http origin anybody reads a remote dashboard at, the tick appeared and
32
40
  * the clipboard was empty.
33
41
  */
34
- export declare const Copyable: ({ value, display, variant, monospace, truncate, truncateLength, ariaLabel, title, copiedMessage, failedMessage, className, }: CopyableProps) => import("react/jsx-runtime").JSX.Element | null;
42
+ export declare const Copyable: ({ value, display, variant, monospace, truncate, truncateLength, ariaLabel, title, copiedMessage, failedMessage, resetMs, failedResetMs, className, }: CopyableProps) => import("react/jsx-runtime").JSX.Element | null;
@@ -1,5 +1,5 @@
1
- export { CopyButton, Copyable } from '../chunk-D3OADIM6.js';
2
- import '../chunk-V663QU4R.js';
1
+ export { CopyButton, Copyable } from '../chunk-3QRYCPXQ.js';
2
+ import '../chunk-5VNUYO76.js';
3
3
  import '../chunk-F27EPQ7D.js';
4
4
  import '../chunk-BZYDZBAN.js';
5
5
  import '../chunk-BZFWDIV6.js';
@@ -1,10 +1,10 @@
1
- export { ALL_ITEMS, CopyableCell, DATA_TABLE_LABELS, DataTable, RowActionsMenu } from '../chunk-EHMQQHYR.js';
1
+ export { ALL_ITEMS, CopyableCell, DATA_TABLE_LABELS, DataTable, RowActionsMenu } from '../chunk-WQX73TWF.js';
2
2
  import '../chunk-FBZAKNSX.js';
3
3
  import '../chunk-EUCC37WY.js';
4
4
  import '../chunk-2EOKFKHX.js';
5
5
  import '../chunk-AV2DXAEL.js';
6
6
  import '../chunk-2PRH365R.js';
7
- import '../chunk-V663QU4R.js';
7
+ import '../chunk-5VNUYO76.js';
8
8
  import '../chunk-PQADV6P6.js';
9
9
  import '../chunk-7RPGY3JE.js';
10
10
  import '../chunk-ITHXJHBB.js';
@@ -1,9 +1,9 @@
1
- export { DATA_VIEW_TOOLBAR_LABELS, DataView, DataViewToolbar, FILTER_AXIS_LABELS, FilterAxisBar, FilterAxisChip, applyColumnFilters, applyFilters, blankFilterValue, defaultOperator, filterDefsFromColumns, filterHasValue, filterPredicate, formatFilterValue, operatorOf, tableColumns } from '../chunk-GHYY2WPF.js';
1
+ export { DATA_VIEW_TOOLBAR_LABELS, DataView, DataViewToolbar, FILTER_AXIS_LABELS, FilterAxisBar, FilterAxisChip, applyColumnFilters, applyFilters, blankFilterValue, defaultOperator, filterDefsFromColumns, filterHasValue, filterPredicate, formatFilterValue, operatorOf, tableColumns } from '../chunk-34VGEDVV.js';
2
2
  import '../chunk-5EKIWVOR.js';
3
3
  import '../chunk-HOXGBLEY.js';
4
4
  import '../chunk-3CZ7XAVY.js';
5
5
  import '../chunk-SFZOKW2G.js';
6
- import '../chunk-EHMQQHYR.js';
6
+ import '../chunk-WQX73TWF.js';
7
7
  import '../chunk-FBZAKNSX.js';
8
8
  import '../chunk-EUCC37WY.js';
9
9
  import '../chunk-2EOKFKHX.js';
@@ -12,7 +12,7 @@ import '../chunk-DD5BBGBE.js';
12
12
  import '../chunk-GXQ3B46I.js';
13
13
  import '../chunk-AV2DXAEL.js';
14
14
  import '../chunk-2PRH365R.js';
15
- import '../chunk-V663QU4R.js';
15
+ import '../chunk-5VNUYO76.js';
16
16
  import '../chunk-GTOUGQQK.js';
17
17
  import '../chunk-O6232G37.js';
18
18
  import '../chunk-BHHIIK3M.js';
@@ -9,6 +9,23 @@ import { type ButtonHTMLAttributes, type ReactNode } from 'react';
9
9
  * at, so this is a prop rather than a styling flag.
10
10
  */
11
11
  export type FilterChipSemantics = 'toggle' | 'radio';
12
+ /**
13
+ * Whether an arrow key APPLIES the filter it lands on, or only moves to it.
14
+ *
15
+ * `automatic` is the platform's own radio group and the default: the choice
16
+ * changes as focus moves, which costs nothing where the choice costs nothing.
17
+ *
18
+ * `manual` moves focus alone, and Enter or Space applies — which a real
19
+ * button already turns into its own click. A filter row is the one place that
20
+ * is routinely wrong: a chip row that drives a URL parameter re-runs the
21
+ * screen's query on every change, so arrowing across five chips under
22
+ * `automatic` fires five queries nobody asked for and redraws the list under
23
+ * the reader four times on the way past.
24
+ *
25
+ * It says nothing about a `toggle` row, which has no arrow keys to qualify:
26
+ * every chip there is its own tab stop and its own switch.
27
+ */
28
+ export type FilterChipActivation = 'automatic' | 'manual';
12
29
  /** The attribute the group finds its own chips by, without a registry to keep. */
13
30
  export declare const FILTER_CHIP_ATTRIBUTE = "data-crewlet-filter-chip";
14
31
  export interface FilterChipProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type' | 'value' | 'onChange'> {
@@ -43,6 +60,15 @@ export interface FilterChipGroupProps {
43
60
  */
44
61
  label: string;
45
62
  semantics?: FilterChipSemantics | undefined;
63
+ /** Whether the arrows apply the filter they land on. Radio rows only. */
64
+ activate?: FilterChipActivation | undefined;
65
+ /**
66
+ * The sentence a manual row is described by, or `null` for none where the
67
+ * screen already says it elsewhere. Defaults to naming the key that
68
+ * applies, because a radio group that does not select as it moves is the
69
+ * unusual one and nothing else on the row says so.
70
+ */
71
+ activateHint?: string | null | undefined;
46
72
  /** The chosen value in radio mode. `null` is a deliberate none. */
47
73
  value?: string | null | undefined;
48
74
  onValueChange?: ((value: string | null) => void) | undefined;
@@ -60,9 +86,15 @@ export interface FilterChipGroupProps {
60
86
  /**
61
87
  * A named row of filters.
62
88
  *
63
- * In radio mode the arrows move AND choose, which is what the platform's own
64
- * radio group does: a reader arrowing through a set of choices is choosing,
65
- * and a group where focus and selection come apart makes them press Space
66
- * after every move for a reason they could not name.
89
+ * In radio mode the arrows move AND choose by default, which is what the
90
+ * platform's own radio group does: a reader arrowing through a set of choices
91
+ * is choosing, and a group where focus and selection come apart makes them
92
+ * press Space after every move for a reason they could not name.
93
+ *
94
+ * `activate="manual"` is where the reason CAN be named: a row driving a URL
95
+ * parameter runs the screen's query again on every change, and there the
96
+ * moves a reader makes on the way to the chip they want are queries nobody
97
+ * asked for. Then the row says which key applies, because a radio group that
98
+ * does not select as it moves is the unusual one.
67
99
  */
68
- export declare function FilterChipGroup({ label, semantics, value, onValueChange, allowNone, hideLabel, className, children, }: FilterChipGroupProps): import("react/jsx-runtime").JSX.Element;
100
+ export declare function FilterChipGroup({ label, semantics, activate, activateHint, value, onValueChange, allowNone, hideLabel, className, children, }: FilterChipGroupProps): import("react/jsx-runtime").JSX.Element;
@@ -1,3 +1,3 @@
1
1
  import './FilterChip.css';
2
2
  export { FILTER_CHIP_ATTRIBUTE, FilterChip, FilterChipGroup } from './FilterChip.js';
3
- export type { FilterChipGroupProps, FilterChipProps, FilterChipSemantics } from './FilterChip.js';
3
+ export type { FilterChipActivation, FilterChipGroupProps, FilterChipProps, FilterChipSemantics, } from './FilterChip.js';
@@ -1,3 +1,3 @@
1
- export { FILTER_CHIP_ATTRIBUTE, FilterChip, FilterChipGroup } from '../chunk-EPOOQZYA.js';
1
+ export { FILTER_CHIP_ATTRIBUTE, FilterChip, FilterChipGroup } from '../chunk-ZFATEERP.js';
2
2
  import '../chunk-BZFWDIV6.js';
3
3
  import '../chunk-ODEQTPFM.js';
@@ -8,8 +8,31 @@ import { type HTMLAttributes, type ReactNode } from 'react';
8
8
  * so without every call site remembering to.
9
9
  */
10
10
  export type MeterTone = 'brand' | 'success' | 'warning' | 'danger' | 'neutral';
11
+ /**
12
+ * WHICH DIRECTION A FULL BAR POINTS, which is the one thing the fill itself
13
+ * cannot say. A budget at 100 percent is a fault; a goal at 100 percent is the
14
+ * whole point of the goal. The same arithmetic, the opposite conclusion — so
15
+ * the polarity is a property of what is being measured and belongs to the call
16
+ * site, not to the ramp.
17
+ *
18
+ * `spent` is the default and is what every existing caller already gets.
19
+ *
20
+ * IT MOVES THE TONE RAMP AND NOTHING ELSE — in particular it does not reach
21
+ * for `role="progressbar"`. That role is for an operation the application is
22
+ * running while the user waits on it, which is why the meter role says authors
23
+ * "SHOULD NOT use the meter role to indicate progress". A goal that is eight
24
+ * tenths achieved is not an operation anybody is waiting on; it is a scalar
25
+ * measurement within a known range, which is a meter. A file upload is the
26
+ * other thing, and it is not this component.
27
+ */
28
+ export type MeterPolarity = 'spent' | 'progress';
11
29
  export interface MeterProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
12
30
  value: number;
31
+ /**
32
+ * The ceiling. NON-POSITIVE MEANS THERE IS NO CEILING, and the bar is then
33
+ * drawn as decoration rather than as a reading — see the note on the
34
+ * component.
35
+ */
13
36
  max: number;
14
37
  /**
15
38
  * What is being measured. REQUIRED, and LINKED to the bar: the meter this
@@ -28,6 +51,12 @@ export interface MeterProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'
28
51
  hint?: ReactNode;
29
52
  /** Overrides the tone derived from the fill. */
30
53
  tone?: MeterTone | undefined;
54
+ /**
55
+ * Which ramp the derived tone comes from. `spent` (the default) reads a full
56
+ * bar as a fault; `progress` reads it as an achievement. Ignored when `tone`
57
+ * is given, which overrides the ramp entirely.
58
+ */
59
+ polarity?: MeterPolarity | undefined;
31
60
  /**
32
61
  * `compact` is the bar inside a table cell. Its legend drops a type step;
33
62
  * the track does not, because the track is already the engine's 4px and
@@ -37,13 +66,61 @@ export interface MeterProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'
37
66
  /** Keep the label out of the picture. It stays in the accessibility tree. */
38
67
  hideLabel?: boolean | undefined;
39
68
  }
40
- /** At the top it is a fault, near the top it wants a person, below that it is just a number. */
69
+ /**
70
+ * The `spent` ramp: at the top it is a fault, near the top it wants a person,
71
+ * below that it is just a number.
72
+ *
73
+ * Keeps its plain name because renaming an export is breaking, and it is still
74
+ * the ramp a `Meter` uses when nobody says otherwise.
75
+ */
41
76
  export declare function meterTone(percent: number): MeterTone;
77
+ /**
78
+ * The `progress` ramp: finished is finished, and everything short of it is
79
+ * just a number.
80
+ *
81
+ * TWO STEPS RATHER THAN THREE, deliberately. The spent ramp has a middle step
82
+ * because a budget carries two escalating facts a reader has to act on — near
83
+ * the limit somebody should look, past it something is already wrong. A goal
84
+ * carries one: it is done or it is not. "Nearly done" is not a warning and
85
+ * "barely started" is only a fault against a DEADLINE, which is not one of
86
+ * this component's inputs — so a middle step here would be an alarm invented
87
+ * out of a number the meter cannot interpret.
88
+ */
89
+ export declare function progressTone(percent: number): MeterTone;
42
90
  /**
43
91
  * A budget, a share, a fill.
44
92
  *
45
93
  * THE TRACK IS ALWAYS DRAWN. A bar with no track is a bar whose maximum the
46
94
  * reader has to guess, and at 8 percent it is indistinguishable from a bar
47
95
  * that is simply short.
96
+ *
97
+ * A NON-POSITIVE MAXIMUM IS NOT A METER, because ARIA gives the meter role no
98
+ * way at all to say "there is no ceiling" and both attempts are a false claim.
99
+ * `max={0}` renders `aria-valuemax="0"` against an `aria-valuemin` of 0: a
100
+ * range of zero width, in which the fraction a reader is offered is 0/0 and
101
+ * every value but 0 breaks the role's own "the value of aria-valuenow MUST NOT
102
+ * fall below or exceed the computed values of aria-valuemin and aria-valuemax"
103
+ * — and a negative maximum additionally breaks "authors MUST ensure the value
104
+ * of aria-valuemax is greater than or equal to the value of aria-valuemin".
105
+ * Leaving the attribute off instead is WORSE rather than better, because
106
+ * missing is precisely when the role's implicit value takes over — "if
107
+ * aria-valuemax is missing or not a number, it defaults to 100" — so the bar
108
+ * stops saying nothing and starts announcing a confident fraction of a ceiling
109
+ * that nobody set.
110
+ *
111
+ * So the only honest move is to stop being a meter. The bar drops the role and
112
+ * every value attribute and is hidden from the accessibility tree as the
113
+ * decoration it is; the legend still names it and still carries the figure.
114
+ * (`DataTable`'s column resizer reaches the same conclusion from the same
115
+ * place: a maximum invented to fill the attribute is a number a reader is told
116
+ * they cannot pass.)
117
+ *
118
+ * THE REPORTED VALUE IS CLAMPED INTO THE SCALE. A budget lowered under a
119
+ * counter that has already passed it is an ordinary event, and it used to
120
+ * publish `aria-valuenow` outside `[min,max]` — the same normative MUST NOT as
121
+ * above, which assistive technology is therefore free to resolve however it
122
+ * likes. The clamp makes the pair valid and `aria-valuetext` keeps the true
123
+ * figures, so nothing is lost: what a reader hears is "140 of 100", not a bar
124
+ * that quietly rounds itself down to exactly the limit it has overrun.
48
125
  */
49
- export declare function Meter({ value, max, label, valueText, hint, tone, size, hideLabel, className, ...rest }: MeterProps): import("react/jsx-runtime").JSX.Element;
126
+ export declare function Meter({ value, max, label, valueText, hint, tone, polarity, size, hideLabel, className, ...rest }: MeterProps): import("react/jsx-runtime").JSX.Element;
@@ -1,3 +1,3 @@
1
1
  import './Meter.css';
2
- export { Meter, meterTone } from './Meter.js';
3
- export type { MeterProps, MeterTone } from './Meter.js';
2
+ export { Meter, meterTone, progressTone } from './Meter.js';
3
+ export type { MeterPolarity, MeterProps, MeterTone } from './Meter.js';
@@ -1,3 +1,3 @@
1
- export { Meter, meterTone } from '../chunk-LGCPQIOZ.js';
1
+ export { Meter, meterTone, progressTone } from '../chunk-YODNNSZL.js';
2
2
  import '../chunk-BZFWDIV6.js';
3
3
  import '../chunk-ODEQTPFM.js';
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode } from 'react';
2
2
  export type SegmentedSize = 'sm' | 'md';
3
3
  export interface SegmentedOption<T extends string = string> {
4
4
  value: T;
@@ -35,7 +35,8 @@ interface SegmentedShared<T extends string> {
35
35
  * `radio` is a SETTING or a filter (the theme, the density, a lens with no
36
36
  * panel): announced as a radio group, and the arrows select as they move,
37
37
  * because a choice that is not in the URL costs nothing to change on every
38
- * keypress.
38
+ * keypress. A choice that IS in the URL costs a query per keypress, and
39
+ * `activate` is where that row says so (see [SegmentedActivation]).
39
40
  *
40
41
  * `tabs` is a row of SECTIONS, and in this product a section pushes a history
41
42
  * entry: the arrows move focus and Enter or Space selects, so walking the row
@@ -45,10 +46,42 @@ interface SegmentedShared<T extends string> {
45
46
  type SegmentedSemantics = {
46
47
  semantics: 'tabs';
47
48
  panelId: string;
49
+ activate?: undefined;
50
+ activateHint?: undefined;
48
51
  } | {
49
52
  semantics: 'radio';
50
53
  panelId?: undefined;
54
+ /** Whether the arrows choose the option they land on. Radio rows only. */
55
+ activate?: SegmentedActivation | undefined;
56
+ /**
57
+ * The sentence a manual row is described by, or `null` for none where
58
+ * the screen already says it elsewhere. Defaults to naming the key that
59
+ * chooses, because a radio group that does not select as it moves is the
60
+ * unusual one and nothing else on the row says so.
61
+ */
62
+ activateHint?: string | null | undefined;
51
63
  };
64
+ /**
65
+ * Whether an arrow key CHOOSES the option it lands on, or only moves to it.
66
+ *
67
+ * `automatic` is the platform's own radio group and the default: the choice
68
+ * changes as focus moves, which costs nothing where the choice costs nothing.
69
+ *
70
+ * `manual` moves focus alone, and Enter or Space chooses — which a real
71
+ * button already turns into its own click. It is for the row whose choice is
72
+ * NOT free: a single-choice filter that drives a URL parameter re-runs the
73
+ * screen's query on every change, so arrowing across five options under
74
+ * `automatic` fires five queries nobody asked for and re-renders the screen
75
+ * under the reader four times on the way past. Those rows are not tabs
76
+ * either — they open no panel and `semantics="tabs"` would have a screen
77
+ * reader hunting for one — so the choice between the two semantics was never
78
+ * the choice this answers.
79
+ *
80
+ * It is offered on a radio row only. A `tabs` row is manual already, for the
81
+ * history entry its own doc describes, and an automatic one would be that
82
+ * bug on request.
83
+ */
84
+ export type SegmentedActivation = 'automatic' | 'manual';
52
85
  export type SegmentedControlProps<T extends string = string> = SegmentedShared<T> & SegmentedSemantics;
53
86
  /**
54
87
  * A row of mutually exclusive choices.
@@ -64,5 +97,5 @@ export type SegmentedControlProps<T extends string = string> = SegmentedShared<T
64
97
  * look like two different controls depending on which component a screen
65
98
  * reached for.
66
99
  */
67
- export declare function SegmentedControl<T extends string = string>({ label, options, value, onValueChange, size, className, semantics, panelId, }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
100
+ export declare function SegmentedControl<T extends string = string>({ label, options, value, onValueChange, size, className, semantics, panelId, activate, activateHint, }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
68
101
  export {};
@@ -1,4 +1,4 @@
1
1
  import '../Tabs/Tabs.css';
2
2
  import './SegmentedControl.css';
3
3
  export { SegmentedControl } from './SegmentedControl.js';
4
- export type { SegmentedControlProps, SegmentedOption, SegmentedSize } from './SegmentedControl.js';
4
+ export type { SegmentedActivation, SegmentedControlProps, SegmentedOption, SegmentedSize, } from './SegmentedControl.js';
@@ -1,5 +1,5 @@
1
- export { SegmentedControl } from '../chunk-ARMRX2YV.js';
2
- import '../chunk-YMWONFSI.js';
1
+ export { SegmentedControl } from '../chunk-6ZFVFA56.js';
2
+ import '../chunk-QEUY3A5P.js';
3
3
  import '../chunk-BHHIIK3M.js';
4
4
  import '../chunk-BZFWDIV6.js';
5
5
  import '../chunk-ODEQTPFM.js';