@usefragments/ui 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/assets/ui.css +1261 -849
- package/dist/chart.cjs +60 -24
- package/dist/chart.js +38 -24
- package/dist/codeblock.cjs +14 -6
- package/dist/codeblock.js +14 -6
- package/dist/colorpicker.cjs +56 -9
- package/dist/colorpicker.js +34 -9
- package/dist/components/Badge/Badge.module.scss.cjs +23 -16
- package/dist/components/Badge/Badge.module.scss.js +23 -16
- package/dist/components/Badge/index.cjs +8 -1
- package/dist/components/Badge/index.d.ts +3 -0
- package/dist/components/Badge/index.d.ts.map +1 -1
- package/dist/components/Badge/index.js +8 -1
- package/dist/components/Button/Button.module.scss.cjs +16 -13
- package/dist/components/Button/Button.module.scss.js +16 -13
- package/dist/components/Button/index.d.ts +2 -1
- package/dist/components/Button/index.d.ts.map +1 -1
- package/dist/components/Chart/index.d.ts +3 -0
- package/dist/components/Chart/index.d.ts.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.module.scss.cjs +21 -21
- package/dist/components/CodeBlock/CodeBlock.module.scss.js +21 -21
- package/dist/components/CodeBlock/index.d.ts.map +1 -1
- package/dist/components/ColorPicker/index.d.ts +4 -0
- package/dist/components/ColorPicker/index.d.ts.map +1 -1
- package/dist/components/Combobox/Combobox.module.scss.cjs +23 -23
- package/dist/components/Combobox/Combobox.module.scss.js +23 -23
- package/dist/components/Command/Command.module.scss.cjs +11 -11
- package/dist/components/Command/Command.module.scss.js +11 -11
- package/dist/components/ConversationList/ConversationList.module.scss.cjs +13 -10
- package/dist/components/ConversationList/ConversationList.module.scss.js +14 -11
- package/dist/components/ConversationList/index.cjs +26 -22
- package/dist/components/ConversationList/index.d.ts +5 -1
- package/dist/components/ConversationList/index.d.ts.map +1 -1
- package/dist/components/ConversationList/index.js +27 -23
- package/dist/components/DataTable/DataTable.module.scss.cjs +35 -35
- package/dist/components/DataTable/DataTable.module.scss.js +35 -35
- package/dist/components/DataTable/index.d.ts +4 -1
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/DatePicker/index.d.ts +3 -0
- package/dist/components/DatePicker/index.d.ts.map +1 -1
- package/dist/components/Editor/index.d.ts +4 -1
- package/dist/components/Editor/index.d.ts.map +1 -1
- package/dist/components/Listbox/Listbox.module.scss.cjs +8 -8
- package/dist/components/Listbox/Listbox.module.scss.js +8 -8
- package/dist/components/Markdown/Markdown.module.scss.cjs +1 -1
- package/dist/components/Markdown/Markdown.module.scss.js +1 -1
- package/dist/components/Markdown/index.d.ts +16 -0
- package/dist/components/Markdown/index.d.ts.map +1 -1
- package/dist/components/Menu/Menu.module.scss.cjs +14 -14
- package/dist/components/Menu/Menu.module.scss.js +14 -14
- package/dist/components/Message/Message.module.scss.cjs +23 -20
- package/dist/components/Message/Message.module.scss.js +24 -21
- package/dist/components/Message/index.cjs +5 -1
- package/dist/components/Message/index.d.ts.map +1 -1
- package/dist/components/Message/index.js +5 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.scss.cjs +28 -28
- package/dist/components/NavigationMenu/NavigationMenu.module.scss.js +28 -28
- package/dist/components/Pagination/Pagination.module.scss.cjs +7 -7
- package/dist/components/Pagination/Pagination.module.scss.js +7 -7
- package/dist/components/Prompt/Prompt.module.scss.cjs +52 -22
- package/dist/components/Prompt/Prompt.module.scss.js +52 -22
- package/dist/components/Prompt/index.cjs +197 -2
- package/dist/components/Prompt/index.d.ts +98 -3
- package/dist/components/Prompt/index.d.ts.map +1 -1
- package/dist/components/Prompt/index.js +198 -3
- package/dist/components/Select/Select.module.scss.cjs +29 -17
- package/dist/components/Select/Select.module.scss.js +30 -18
- package/dist/components/Select/index.cjs +45 -8
- package/dist/components/Select/index.d.ts +17 -2
- package/dist/components/Select/index.d.ts.map +1 -1
- package/dist/components/Select/index.js +45 -8
- package/dist/components/Sidebar/Sidebar.module.scss.cjs +45 -42
- package/dist/components/Sidebar/Sidebar.module.scss.js +45 -42
- package/dist/components/Sidebar/index.cjs +2 -0
- package/dist/components/Sidebar/index.d.ts +6 -1
- package/dist/components/Sidebar/index.d.ts.map +1 -1
- package/dist/components/Sidebar/index.js +2 -0
- package/dist/components/Text/Text.module.scss.cjs +63 -51
- package/dist/components/Text/Text.module.scss.js +63 -51
- package/dist/components/Text/index.d.ts +4 -2
- package/dist/components/Text/index.d.ts.map +1 -1
- package/dist/data-table.cjs +210 -28
- package/dist/data-table.js +189 -29
- package/dist/datepicker.cjs +63 -14
- package/dist/datepicker.js +41 -14
- package/dist/editor.cjs +67 -16
- package/dist/editor.js +45 -16
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/markdown.cjs +63 -19
- package/dist/markdown.js +41 -19
- package/fragments.json +1 -1
- package/package.json +2 -2
- package/src/components/Badge/Badge.contract.json +28 -5
- package/src/components/Badge/Badge.module.scss +21 -0
- package/src/components/Badge/index.tsx +10 -1
- package/src/components/Button/Button.contract.json +9 -3
- package/src/components/Button/Button.module.scss +34 -0
- package/src/components/Button/index.tsx +11 -1
- package/src/components/Chart/Chart.contract.json +7 -0
- package/src/components/Chart/index.tsx +44 -27
- package/src/components/CodeBlock/CodeBlock.contract.json +7 -0
- package/src/components/CodeBlock/CodeBlock.module.scss +7 -2
- package/src/components/CodeBlock/index.tsx +38 -6
- package/src/components/ColorPicker/ColorPicker.contract.json +9 -1
- package/src/components/ColorPicker/index.tsx +42 -12
- package/src/components/Combobox/Combobox.module.scss +4 -13
- package/src/components/ConversationList/ConversationList.contract.json +16 -1
- package/src/components/ConversationList/ConversationList.module.scss +12 -0
- package/src/components/ConversationList/index.tsx +40 -26
- package/src/components/DataTable/DataTable.contract.json +8 -1
- package/src/components/DataTable/DataTable.module.scss +5 -0
- package/src/components/DataTable/index.tsx +282 -51
- package/src/components/DatePicker/DatePicker.contract.json +8 -1
- package/src/components/DatePicker/index.tsx +51 -22
- package/src/components/Editor/Editor.contract.json +19 -1
- package/src/components/Editor/index.tsx +67 -23
- package/src/components/Listbox/Listbox.module.scss +3 -11
- package/src/components/Markdown/Markdown.contract.json +15 -1
- package/src/components/Markdown/Markdown.module.scss +18 -1
- package/src/components/Markdown/index.tsx +58 -24
- package/src/components/Menu/Menu.module.scss +12 -2
- package/src/components/Message/Message.contract.json +3 -0
- package/src/components/Message/Message.module.scss +33 -4
- package/src/components/Message/index.tsx +7 -1
- package/src/components/Pagination/Pagination.contract.json +1 -0
- package/src/components/Pagination/Pagination.module.scss +5 -4
- package/src/components/Prompt/Prompt.contract.json +40 -1
- package/src/components/Prompt/Prompt.module.scss +351 -4
- package/src/components/Prompt/index.tsx +324 -0
- package/src/components/Select/Select.contract.json +11 -1
- package/src/components/Select/Select.module.scss +80 -13
- package/src/components/Select/index.tsx +73 -7
- package/src/components/Sidebar/Sidebar.contract.json +1 -0
- package/src/components/Sidebar/Sidebar.module.scss +10 -0
- package/src/components/Sidebar/index.tsx +7 -0
- package/src/components/Text/Text.contract.json +7 -4
- package/src/components/Text/Text.module.scss +18 -0
- package/src/components/Text/index.tsx +4 -2
- package/src/index.ts +1 -0
- package/src/tokens/_mixins.scss +39 -4
- package/src/tokens/_variables.scss +30 -0
|
@@ -12,9 +12,15 @@ import styles from "./Select.module.scss";
|
|
|
12
12
|
|
|
13
13
|
export type SelectValue = string;
|
|
14
14
|
|
|
15
|
+
export type SelectVariant = "field" | "ghost";
|
|
16
|
+
|
|
15
17
|
export interface SelectOption {
|
|
16
18
|
value: SelectValue;
|
|
17
19
|
label: string;
|
|
20
|
+
/** A dimmed qualifier after the label, in the trigger as well as the list —
|
|
21
|
+
* a tier beside a model, a path beside a filename. Use it when the label
|
|
22
|
+
* alone is ambiguous but the qualifier is not what you are choosing by. */
|
|
23
|
+
hint?: string;
|
|
18
24
|
disabled?: boolean;
|
|
19
25
|
}
|
|
20
26
|
|
|
@@ -58,6 +64,11 @@ export interface SelectProps extends FormFieldProps {
|
|
|
58
64
|
/** Size variant.
|
|
59
65
|
* @default "md" */
|
|
60
66
|
size?: "sm" | "md" | "lg";
|
|
67
|
+
/** Visual treatment. `ghost` drops the field shell for a compact, borderless
|
|
68
|
+
* control — for toolbars and dense rows, where a bordered field reads as a
|
|
69
|
+
* form. Pair with `size="sm"`.
|
|
70
|
+
* @default "field" */
|
|
71
|
+
variant?: SelectVariant;
|
|
61
72
|
/** Wrapper class name */
|
|
62
73
|
className?: string;
|
|
63
74
|
}
|
|
@@ -65,10 +76,15 @@ export interface SelectProps extends FormFieldProps {
|
|
|
65
76
|
export interface SelectTriggerProps extends React.HTMLAttributes<HTMLButtonElement> {
|
|
66
77
|
children?: React.ReactNode;
|
|
67
78
|
placeholder?: string;
|
|
79
|
+
/** Leading adornment shown before the value — usually says what the choice is
|
|
80
|
+
* about, so the visible text can be the choice itself. Ignored when you pass
|
|
81
|
+
* your own `children`. */
|
|
82
|
+
icon?: React.ReactNode;
|
|
68
83
|
}
|
|
69
84
|
|
|
70
85
|
export interface SelectContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
71
|
-
|
|
86
|
+
/** Omit to render the items the root's `options` prop describes. */
|
|
87
|
+
children?: React.ReactNode;
|
|
72
88
|
sideOffset?: number;
|
|
73
89
|
align?: "start" | "center" | "end";
|
|
74
90
|
/** Maximum number of visible options before scrolling. Shows half of the next item as a scroll hint. @default 4 */
|
|
@@ -142,6 +158,12 @@ interface SelectContextValue {
|
|
|
142
158
|
registerItem: (value: SelectValue, content: React.ReactNode) => void;
|
|
143
159
|
unregisterItem: (value: SelectValue) => void;
|
|
144
160
|
size: "sm" | "md" | "lg";
|
|
161
|
+
variant: SelectVariant;
|
|
162
|
+
/** Items built from the `options` prop, so a custom composition can put the
|
|
163
|
+
* trigger where it wants without having to re-render the list itself — and
|
|
164
|
+
* without losing whatever the root knows about an option that a bare label
|
|
165
|
+
* does not carry. */
|
|
166
|
+
optionItems: React.ReactNode;
|
|
145
167
|
}
|
|
146
168
|
|
|
147
169
|
const SelectContext = React.createContext<SelectContextValue>({
|
|
@@ -149,8 +171,22 @@ const SelectContext = React.createContext<SelectContextValue>({
|
|
|
149
171
|
registerItem: () => {},
|
|
150
172
|
unregisterItem: () => {},
|
|
151
173
|
size: "md",
|
|
174
|
+
variant: "field",
|
|
175
|
+
optionItems: null,
|
|
152
176
|
});
|
|
153
177
|
|
|
178
|
+
// Label and qualifier as one node, so the trigger and the list render the same
|
|
179
|
+
// thing and the registry has a single entry to hand back.
|
|
180
|
+
function renderOptionLabel(option: SelectOption): React.ReactNode {
|
|
181
|
+
if (!option.hint) return option.label;
|
|
182
|
+
return (
|
|
183
|
+
<>
|
|
184
|
+
{option.label}
|
|
185
|
+
<span className={styles.hint}>{option.hint}</span>
|
|
186
|
+
</>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
154
190
|
// Walk the declared Select children at render time to build a value→label map.
|
|
155
191
|
// The trigger reads this so a preselected value renders its label immediately,
|
|
156
192
|
// without waiting for the lazily-portaled popup to mount and register items.
|
|
@@ -194,6 +230,7 @@ const SelectRoot = React.forwardRef<HTMLDivElement, SelectProps>(function Select
|
|
|
194
230
|
helperText,
|
|
195
231
|
error,
|
|
196
232
|
size: sizeProp,
|
|
233
|
+
variant = "field",
|
|
197
234
|
className,
|
|
198
235
|
}: SelectProps,
|
|
199
236
|
ref
|
|
@@ -235,7 +272,7 @@ const SelectRoot = React.forwardRef<HTMLDivElement, SelectProps>(function Select
|
|
|
235
272
|
() =>
|
|
236
273
|
options?.map((option) => (
|
|
237
274
|
<SelectItem key={option.value} value={option.value} disabled={option.disabled}>
|
|
238
|
-
{option
|
|
275
|
+
{renderOptionLabel(option)}
|
|
239
276
|
</SelectItem>
|
|
240
277
|
)) ?? null,
|
|
241
278
|
[options]
|
|
@@ -267,7 +304,7 @@ const SelectRoot = React.forwardRef<HTMLDivElement, SelectProps>(function Select
|
|
|
267
304
|
// registry, which still wins for items that register dynamically.
|
|
268
305
|
const declaredItems = React.useMemo(() => {
|
|
269
306
|
const map = new Map<SelectValue, React.ReactNode>();
|
|
270
|
-
options?.forEach((option) => map.set(option.value, option
|
|
307
|
+
options?.forEach((option) => map.set(option.value, renderOptionLabel(option)));
|
|
271
308
|
collectDeclaredItems(children, map);
|
|
272
309
|
return map;
|
|
273
310
|
}, [children, options]);
|
|
@@ -287,8 +324,19 @@ const SelectRoot = React.forwardRef<HTMLDivElement, SelectProps>(function Select
|
|
|
287
324
|
registerItem,
|
|
288
325
|
unregisterItem,
|
|
289
326
|
size,
|
|
327
|
+
variant,
|
|
328
|
+
optionItems,
|
|
290
329
|
}),
|
|
291
|
-
[
|
|
330
|
+
[
|
|
331
|
+
placeholder,
|
|
332
|
+
selectedValue,
|
|
333
|
+
resolvedItems,
|
|
334
|
+
registerItem,
|
|
335
|
+
unregisterItem,
|
|
336
|
+
size,
|
|
337
|
+
variant,
|
|
338
|
+
optionItems,
|
|
339
|
+
]
|
|
292
340
|
);
|
|
293
341
|
|
|
294
342
|
const { helperId, errorId, hasError, errorMessage } = useFormFieldIds("select", {
|
|
@@ -297,7 +345,13 @@ const SelectRoot = React.forwardRef<HTMLDivElement, SelectProps>(function Select
|
|
|
297
345
|
error,
|
|
298
346
|
});
|
|
299
347
|
|
|
300
|
-
const wrapperClasses = [
|
|
348
|
+
const wrapperClasses = [
|
|
349
|
+
styles.wrapper,
|
|
350
|
+
variant === "ghost" && styles.wrapperGhost,
|
|
351
|
+
className,
|
|
352
|
+
]
|
|
353
|
+
.filter(Boolean)
|
|
354
|
+
.join(" ");
|
|
301
355
|
const helperClasses = [styles.helper, hasError && styles.helperError].filter(Boolean).join(" ");
|
|
302
356
|
|
|
303
357
|
return (
|
|
@@ -337,7 +391,13 @@ const SelectRoot = React.forwardRef<HTMLDivElement, SelectProps>(function Select
|
|
|
337
391
|
);
|
|
338
392
|
});
|
|
339
393
|
|
|
340
|
-
function SelectTrigger({
|
|
394
|
+
function SelectTrigger({
|
|
395
|
+
children,
|
|
396
|
+
placeholder,
|
|
397
|
+
icon,
|
|
398
|
+
className,
|
|
399
|
+
...htmlProps
|
|
400
|
+
}: SelectTriggerProps) {
|
|
341
401
|
const context = React.useContext(SelectContext);
|
|
342
402
|
const placeholderText = placeholder ?? context.placeholder;
|
|
343
403
|
|
|
@@ -345,6 +405,10 @@ function SelectTrigger({ children, placeholder, className, ...htmlProps }: Selec
|
|
|
345
405
|
styles.trigger,
|
|
346
406
|
context.size === "sm" && styles.triggerSm,
|
|
347
407
|
context.size === "lg" && styles.triggerLg,
|
|
408
|
+
// Ghost unsets the field shell and the size mixin's box. Both are single
|
|
409
|
+
// classes like this one, so it wins on being declared after them in
|
|
410
|
+
// Select.module.scss — the one place that ordering is guaranteed.
|
|
411
|
+
context.variant === "ghost" && styles.triggerGhost,
|
|
348
412
|
className,
|
|
349
413
|
]
|
|
350
414
|
.filter(Boolean)
|
|
@@ -362,6 +426,7 @@ function SelectTrigger({ children, placeholder, className, ...htmlProps }: Selec
|
|
|
362
426
|
<BaseSelect.Trigger {...htmlProps} className={classes}>
|
|
363
427
|
{children ?? (
|
|
364
428
|
<>
|
|
429
|
+
{icon && <span className={styles.triggerIcon}>{icon}</span>}
|
|
365
430
|
<span className={styles.value}>{displayContent}</span>
|
|
366
431
|
<BaseSelect.Icon className={styles.icon}>
|
|
367
432
|
<ChevronDownIcon />
|
|
@@ -380,6 +445,7 @@ function SelectContent({
|
|
|
380
445
|
maxVisibleItems,
|
|
381
446
|
...htmlProps
|
|
382
447
|
}: SelectContentProps) {
|
|
448
|
+
const { optionItems } = React.useContext(SelectContext);
|
|
383
449
|
const popupClasses = [styles.popup, className].filter(Boolean).join(" ");
|
|
384
450
|
|
|
385
451
|
const popupStyle =
|
|
@@ -394,7 +460,7 @@ function SelectContent({
|
|
|
394
460
|
<BaseSelect.Portal>
|
|
395
461
|
<BaseSelect.Positioner sideOffset={sideOffset} align={align} className={styles.positioner}>
|
|
396
462
|
<BaseSelect.Popup {...htmlProps} className={popupClasses} style={popupStyle}>
|
|
397
|
-
{children}
|
|
463
|
+
{children ?? optionItems}
|
|
398
464
|
</BaseSelect.Popup>
|
|
399
465
|
</BaseSelect.Positioner>
|
|
400
466
|
</BaseSelect.Portal>
|
|
@@ -129,6 +129,7 @@
|
|
|
129
129
|
"Use SidebarProvider to enable external triggers and keyboard shortcuts",
|
|
130
130
|
"Use asChild with routing libraries (Next.js Link, React Router NavLink)",
|
|
131
131
|
"Sidebar.Item and Sidebar.SubItem onClick handlers receive the click event object",
|
|
132
|
+
"Use `multiline` on Sidebar.Item when the row is a list entry rather than a nav link — a title over a status or preview line. Without it the label is one truncating line",
|
|
132
133
|
"Use Sidebar.MenuSkeleton while loading navigation data"
|
|
133
134
|
],
|
|
134
135
|
"accessibility": [
|
|
@@ -388,6 +388,16 @@ $fui-sidebar-rail-indicator-height-hover: 4rem !default; // 56px
|
|
|
388
388
|
padding-top: 0.071rem;
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
// A row whose label is a block rather than a link: a title over a status line,
|
|
392
|
+
// a name over an address, a subject over a preview. The single-line ellipsis is
|
|
393
|
+
// right for navigation and wrong here, so it comes off — but the label keeps
|
|
394
|
+
// bounding itself, so anything inside it that truncates still truncates against
|
|
395
|
+
// the rail's width instead of widening it.
|
|
396
|
+
.itemMultiline .itemLabel {
|
|
397
|
+
white-space: normal;
|
|
398
|
+
min-width: 0;
|
|
399
|
+
}
|
|
400
|
+
|
|
391
401
|
.itemBadge {
|
|
392
402
|
display: flex;
|
|
393
403
|
align-items: center;
|
|
@@ -144,6 +144,11 @@ export interface SidebarItemProps extends Omit<React.HTMLAttributes<HTMLElement>
|
|
|
144
144
|
href?: string;
|
|
145
145
|
/** Click handler (renders as button) */
|
|
146
146
|
onClick?: React.MouseEventHandler<HTMLElement>;
|
|
147
|
+
/**
|
|
148
|
+
* Let the label be a block instead of one truncating line — a title over a
|
|
149
|
+
* status line, a subject over a preview. For list rows rather than nav links.
|
|
150
|
+
*/
|
|
151
|
+
multiline?: boolean;
|
|
147
152
|
/** Whether this item has a submenu */
|
|
148
153
|
hasSubmenu?: boolean;
|
|
149
154
|
/** Whether submenu is expanded (controlled) */
|
|
@@ -774,6 +779,7 @@ function SidebarItem({
|
|
|
774
779
|
badge,
|
|
775
780
|
href,
|
|
776
781
|
onClick,
|
|
782
|
+
multiline = false,
|
|
777
783
|
hasSubmenu = false,
|
|
778
784
|
expanded: controlledExpanded,
|
|
779
785
|
defaultExpanded = false,
|
|
@@ -796,6 +802,7 @@ function SidebarItem({
|
|
|
796
802
|
styles.item,
|
|
797
803
|
active && styles.itemActive,
|
|
798
804
|
disabled && styles.itemDisabled,
|
|
805
|
+
multiline && styles.itemMultiline,
|
|
799
806
|
hasSubmenu && styles.itemHasSubmenu,
|
|
800
807
|
expanded && styles.itemExpanded,
|
|
801
808
|
className,
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"variant: section-label",
|
|
20
20
|
"size: 2xs|xs|sm|base|md|lg|xl|2xl|3xl|4xl",
|
|
21
21
|
"weight: normal|medium|semibold|bold (default: \"normal\")",
|
|
22
|
-
"color: primary|secondary|tertiary|muted (default: \"primary\")",
|
|
22
|
+
"color: primary|secondary|tertiary|muted|success|warning|danger (default: \"primary\")",
|
|
23
23
|
"font: sans|mono (default: sans)",
|
|
24
24
|
"truncate: boolean",
|
|
25
25
|
"lineClamp: number",
|
|
@@ -102,14 +102,17 @@
|
|
|
102
102
|
},
|
|
103
103
|
"color": {
|
|
104
104
|
"type": "enum",
|
|
105
|
-
"description": "Text color (muted is an alias for tertiary)",
|
|
105
|
+
"description": "Text color (muted is an alias for tertiary; success/warning/danger are for a run of text that carries a state on its own \u2014 prefer Badge or Alert when the state deserves a container)",
|
|
106
106
|
"default": "\"primary\"",
|
|
107
107
|
"required": false,
|
|
108
108
|
"values": [
|
|
109
109
|
"primary",
|
|
110
110
|
"secondary",
|
|
111
111
|
"tertiary",
|
|
112
|
-
"muted"
|
|
112
|
+
"muted",
|
|
113
|
+
"success",
|
|
114
|
+
"warning",
|
|
115
|
+
"danger"
|
|
113
116
|
]
|
|
114
117
|
},
|
|
115
118
|
"font": {
|
|
@@ -216,7 +219,7 @@
|
|
|
216
219
|
"as: string - HTML element",
|
|
217
220
|
"size: 2xs|xs|sm|base|md|lg|xl|2xl|3xl|4xl - font size (px: 10/12/14/14/15/20/24/30/36/48)",
|
|
218
221
|
"weight: normal|medium|semibold|bold - font weight",
|
|
219
|
-
"color: primary|secondary|tertiary|muted - text color (muted = tertiary)",
|
|
222
|
+
"color: primary|secondary|tertiary|muted|success|warning|danger - text color (muted = tertiary; semantic colours for inline state)",
|
|
220
223
|
"font: sans|mono - font family",
|
|
221
224
|
"truncate: boolean - enable truncation",
|
|
222
225
|
"lineClamp: number - max lines"
|
|
@@ -88,6 +88,24 @@
|
|
|
88
88
|
color: var(--fui-text-tertiary, $fui-text-tertiary);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
// Semantic colours, for a run of text that carries a state on its own — a
|
|
92
|
+
// count going over budget, an inline "expired", a figure in the red. Where
|
|
93
|
+
// there is a component for the job (Badge, Alert, Callout) use that instead:
|
|
94
|
+
// this is for the cases where the state is one word inside a line, and
|
|
95
|
+
// wrapping it in a box would be more chrome than the fact is worth. Colour is
|
|
96
|
+
// never the only carrier — the words still have to say it.
|
|
97
|
+
.color-success {
|
|
98
|
+
color: var(--fui-color-success, $fui-color-success);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.color-warning {
|
|
102
|
+
color: var(--fui-color-warning, $fui-color-warning);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.color-danger {
|
|
106
|
+
color: var(--fui-color-danger, $fui-color-danger);
|
|
107
|
+
}
|
|
108
|
+
|
|
91
109
|
// Font family
|
|
92
110
|
.mono {
|
|
93
111
|
font-family: var(--fui-font-mono, $fui-font-mono);
|
|
@@ -19,9 +19,11 @@ export interface TextProps extends Omit<React.HTMLAttributes<HTMLElement>, 'colo
|
|
|
19
19
|
/** Font weight.
|
|
20
20
|
* @default "normal" */
|
|
21
21
|
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
22
|
-
/** Text color. `"muted"` is an alias for `"tertiary"`.
|
|
22
|
+
/** Text color. `"muted"` is an alias for `"tertiary"`. `"success" | "warning" | "danger"`
|
|
23
|
+
* are for a run of text that carries a state on its own; prefer Badge or Alert when the
|
|
24
|
+
* state deserves a container.
|
|
23
25
|
* @default "primary" */
|
|
24
|
-
color?: 'primary' | 'secondary' | 'tertiary' | 'muted';
|
|
26
|
+
color?: 'primary' | 'secondary' | 'tertiary' | 'muted' | 'success' | 'warning' | 'danger';
|
|
25
27
|
/** Font family.
|
|
26
28
|
* @default "sans" */
|
|
27
29
|
font?: 'sans' | 'mono';
|
package/src/index.ts
CHANGED
package/src/tokens/_mixins.scss
CHANGED
|
@@ -114,11 +114,41 @@
|
|
|
114
114
|
@mixin popup-container {
|
|
115
115
|
display: flex;
|
|
116
116
|
flex-direction: column;
|
|
117
|
-
gap:
|
|
118
|
-
padding: var(--fui-
|
|
117
|
+
gap: var(--fui-popup-row-gap, #{$fui-popup-row-gap});
|
|
118
|
+
padding: var(--fui-popup-padding, #{$fui-popup-padding});
|
|
119
119
|
box-shadow: var(--fui-shadow-md, #{$fui-shadow-md});
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Scroll viewport for the popups that peek — Select, Listbox, Combobox. Sets
|
|
123
|
+
// `--_popup-scroll-max-h`; the consumer applies it, because Base UI puts an
|
|
124
|
+
// inline `max-height: 100%` on some of these popups that only `!important` beats.
|
|
125
|
+
//
|
|
126
|
+
// Row *pitch*, not row height: `popup-container` is a flex column, so N rows
|
|
127
|
+
// occupy N pitches less the trailing gap. (A grouped popup nests its rows one
|
|
128
|
+
// level down, where that gap does not reach, so the peek there runs a few
|
|
129
|
+
// pixels shy of half a row — visible only as a slightly smaller sliver.)
|
|
130
|
+
// Every term here reads the token that actually draws it — which is the point.
|
|
131
|
+
// Written out longhand in three separate stylesheets, this calc billed
|
|
132
|
+
// $fui-space-2 of vertical padding for a row `popup-item` pads with
|
|
133
|
+
// $fui-space-1, deriving a 46px row against a real 32px one, so all three
|
|
134
|
+
// popups outgrew their own content and never scrolled at all.
|
|
135
|
+
@mixin popup-scroll-viewport {
|
|
136
|
+
--_popup-row-h: calc(
|
|
137
|
+
var(--fui-popup-item-font-size, #{$fui-popup-item-font-size}) *
|
|
138
|
+
var(--fui-line-height-normal, #{$fui-line-height-normal}) +
|
|
139
|
+
var(--fui-popup-item-padding-block, #{$fui-popup-item-padding-block}) * 2 +
|
|
140
|
+
var(--fui-popup-row-gap, #{$fui-popup-row-gap})
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// The + 2px is the 1px border `surface-elevated` draws top and bottom: these
|
|
144
|
+
// popups are border-box, so max-height pays for the frame before the rows.
|
|
145
|
+
--_popup-scroll-max-h: calc(
|
|
146
|
+
var(--_popup-row-h) *
|
|
147
|
+
var(--fui-select-max-items, 4.5) - var(--fui-popup-row-gap, #{$fui-popup-row-gap}) +
|
|
148
|
+
var(--fui-popup-padding, #{$fui-popup-padding}) * 2 + 2px
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
122
152
|
// ============================================
|
|
123
153
|
// Popup / menu item — canonical interaction model
|
|
124
154
|
// ============================================
|
|
@@ -136,16 +166,21 @@
|
|
|
136
166
|
// `&[data-highlighted] { @include popup-item-highlight; }` covers both. Custom popups
|
|
137
167
|
// should map `&:hover, &.<active>, &[data-highlighted]` to the same mixin.
|
|
138
168
|
|
|
139
|
-
// Dropdown/popup item base. Includes button-reset and text-base
|
|
169
|
+
// Dropdown/popup item base. Includes button-reset and text-base, then steps the
|
|
170
|
+
// size down to the popup scale — see $fui-popup-item-font-size. Select, Listbox
|
|
171
|
+
// and Combobox size their scroll viewport from the same token, so read it there
|
|
172
|
+
// too rather than re-deriving an item height from the body size.
|
|
140
173
|
@mixin popup-item {
|
|
141
174
|
@include button-reset;
|
|
142
175
|
@include text-base;
|
|
143
176
|
|
|
177
|
+
font-size: var(--fui-popup-item-font-size, #{$fui-popup-item-font-size});
|
|
144
178
|
display: flex;
|
|
145
179
|
align-items: center;
|
|
146
180
|
gap: var(--fui-space-1, #{$fui-space-1});
|
|
147
181
|
width: 100%;
|
|
148
|
-
padding: var(--fui-
|
|
182
|
+
padding: var(--fui-popup-item-padding-block, #{$fui-popup-item-padding-block})
|
|
183
|
+
var(--fui-popup-item-padding-inline, #{$fui-popup-item-padding-inline});
|
|
149
184
|
border-radius: var(--fui-radius-sm, #{$fui-radius-sm});
|
|
150
185
|
cursor: pointer;
|
|
151
186
|
outline: none;
|
|
@@ -73,6 +73,14 @@ $fui-font-size-2xl: 2.143rem !default; // 30px (large display)
|
|
|
73
73
|
$fui-font-size-3xl: 2.571rem !default; // 36px (h1, page title)
|
|
74
74
|
$fui-font-size-4xl: 3.429rem !default; // 48px (hero display)
|
|
75
75
|
|
|
76
|
+
// Dropdown/popup list items — menus, selects, comboboxes, listboxes, command
|
|
77
|
+
// palettes. One step below chrome text: a popup is a dense list scanned against
|
|
78
|
+
// the trigger that opened it, and matching the trigger's own size made the list
|
|
79
|
+
// read heavier than the control. Named rather than inlined because Select,
|
|
80
|
+
// Listbox and Combobox derive their scroll height from this exact number; a
|
|
81
|
+
// literal in the mixin would let the height calcs drift out of step with it.
|
|
82
|
+
$fui-popup-item-font-size: $fui-font-size-xs !default; // 12px
|
|
83
|
+
|
|
76
84
|
$fui-font-weight-normal: 400 !default;
|
|
77
85
|
$fui-font-weight-medium: 500 !default;
|
|
78
86
|
$fui-font-weight-semibold: 600 !default;
|
|
@@ -110,6 +118,20 @@ $fui-space-8: computed.$computed-space-8 !default;
|
|
|
110
118
|
$fui-space-10: computed.$computed-space-10 !default;
|
|
111
119
|
$fui-space-12: computed.$computed-space-12 !default;
|
|
112
120
|
|
|
121
|
+
// --------------------------------------------
|
|
122
|
+
// Popup geometry
|
|
123
|
+
// --------------------------------------------
|
|
124
|
+
// The insets a popup list and its rows carry. Named rather than inlined because
|
|
125
|
+
// Select, Listbox and Combobox size their scroll viewport from these exact
|
|
126
|
+
// numbers to show N rows plus a half-peek hint. Left as literals in the mixin,
|
|
127
|
+
// the two sides drifted: the height calcs charged $fui-space-2 of vertical
|
|
128
|
+
// padding for a row `popup-item` pads with $fui-space-1, so every one of those
|
|
129
|
+
// popups peeked further than its own "4.5 items" comment promised.
|
|
130
|
+
$fui-popup-padding: $fui-space-1 !default; // container inset
|
|
131
|
+
$fui-popup-row-gap: 1px !default; // flex gap between rows
|
|
132
|
+
$fui-popup-item-padding-block: $fui-space-1 !default;
|
|
133
|
+
$fui-popup-item-padding-inline: $fui-space-2 !default;
|
|
134
|
+
|
|
113
135
|
// --------------------------------------------
|
|
114
136
|
// Border Radius (derived from radius-style seed)
|
|
115
137
|
// --------------------------------------------
|
|
@@ -397,6 +419,7 @@ $fui-code-selection-bg: color-mix(
|
|
|
397
419
|
--fui-font-size-2xl: #{$fui-font-size-2xl};
|
|
398
420
|
--fui-font-size-3xl: #{$fui-font-size-3xl};
|
|
399
421
|
--fui-font-size-4xl: #{$fui-font-size-4xl};
|
|
422
|
+
--fui-popup-item-font-size: #{$fui-popup-item-font-size};
|
|
400
423
|
--fui-font-weight-normal: #{$fui-font-weight-normal};
|
|
401
424
|
--fui-font-weight-medium: #{$fui-font-weight-medium};
|
|
402
425
|
--fui-font-weight-semibold: #{$fui-font-weight-semibold};
|
|
@@ -427,6 +450,13 @@ $fui-code-selection-bg: color-mix(
|
|
|
427
450
|
--fui-space-10: #{$fui-space-10};
|
|
428
451
|
--fui-space-12: #{$fui-space-12};
|
|
429
452
|
|
|
453
|
+
// Popup geometry — the numbers Select/Listbox/Combobox derive their scroll
|
|
454
|
+
// viewport from. Override these and the peek height follows.
|
|
455
|
+
--fui-popup-padding: #{$fui-popup-padding};
|
|
456
|
+
--fui-popup-row-gap: #{$fui-popup-row-gap};
|
|
457
|
+
--fui-popup-item-padding-block: #{$fui-popup-item-padding-block};
|
|
458
|
+
--fui-popup-item-padding-inline: #{$fui-popup-item-padding-inline};
|
|
459
|
+
|
|
430
460
|
// Border radius
|
|
431
461
|
--fui-radius-sm: #{$fui-radius-sm};
|
|
432
462
|
--fui-radius-md: #{$fui-radius-md};
|