@surflane/ui 0.11.0 → 0.13.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.
Files changed (137) hide show
  1. package/dist/alert-dialog.d.ts +25 -7
  2. package/dist/alert-dialog.js +9 -4
  3. package/dist/app-shell.d.ts +10 -0
  4. package/dist/app-shell.js +7 -1
  5. package/dist/autocomplete.d.ts +2 -6
  6. package/dist/avatar.d.ts +7 -7
  7. package/dist/avatar.js +2 -2
  8. package/dist/bar-list.d.ts +31 -0
  9. package/dist/bar-list.js +31 -0
  10. package/dist/bottom-navigation.d.ts +6 -2
  11. package/dist/bottom-navigation.js +2 -2
  12. package/dist/button.d.ts +6 -11
  13. package/dist/button.js +8 -8
  14. package/dist/card.d.ts +15 -10
  15. package/dist/card.js +7 -2
  16. package/dist/chart.d.ts +58 -3
  17. package/dist/chart.js +330 -48
  18. package/dist/choice.d.ts +2 -2
  19. package/dist/class-names.d.ts +0 -4
  20. package/dist/class-names.js +0 -14
  21. package/dist/code.d.ts +19 -7
  22. package/dist/code.js +32 -38
  23. package/dist/combobox.d.ts +2 -8
  24. package/dist/combobox.js +2 -2
  25. package/dist/context-menu.d.ts +2 -8
  26. package/dist/context-menu.js +2 -2
  27. package/dist/css/bar-list.css +9 -0
  28. package/dist/css/chart.css +2 -0
  29. package/dist/css/dashboard.css +20 -0
  30. package/dist/css/rules/alert-dialog.css +8 -2
  31. package/dist/css/rules/app-shell.css +57 -10
  32. package/dist/css/rules/bar-list.css +101 -0
  33. package/dist/css/rules/bar.css +5 -12
  34. package/dist/css/rules/bottom-navigation.css +21 -2
  35. package/dist/css/rules/breadcrumbs.css +0 -6
  36. package/dist/css/rules/button.css +4 -12
  37. package/dist/css/rules/card.css +19 -0
  38. package/dist/css/rules/chart.css +87 -8
  39. package/dist/css/rules/choice.css +26 -2
  40. package/dist/css/rules/combobox.css +17 -16
  41. package/dist/css/rules/dashboard.css +118 -0
  42. package/dist/css/rules/date-picker.css +50 -27
  43. package/dist/css/rules/dialog.css +47 -7
  44. package/dist/css/rules/disclosure.css +17 -3
  45. package/dist/css/rules/field-control.css +9 -6
  46. package/dist/css/rules/file-upload.css +9 -5
  47. package/dist/css/rules/filter-bar.css +32 -2
  48. package/dist/css/rules/gauge.css +2 -2
  49. package/dist/css/rules/icon.css +0 -5
  50. package/dist/css/rules/input-group.css +23 -8
  51. package/dist/css/rules/link.css +0 -6
  52. package/dist/css/rules/menu.css +5 -2
  53. package/dist/css/rules/menubar.css +1 -1
  54. package/dist/css/rules/navigation-menu.css +31 -26
  55. package/dist/css/rules/number-field.css +8 -1
  56. package/dist/css/rules/otp-field.css +13 -3
  57. package/dist/css/rules/page-header.css +10 -0
  58. package/dist/css/rules/panel.css +59 -10
  59. package/dist/css/rules/popup.css +50 -0
  60. package/dist/css/rules/preview-card.css +0 -21
  61. package/dist/css/rules/scroll-area.css +20 -1
  62. package/dist/css/rules/select.css +29 -2
  63. package/dist/css/rules/sizing.css +25 -3
  64. package/dist/css/rules/skeleton.css +1 -1
  65. package/dist/css/rules/slider.css +16 -8
  66. package/dist/css/rules/spinner.css +1 -1
  67. package/dist/css/rules/stat.css +66 -0
  68. package/dist/css/rules/tabs.css +14 -6
  69. package/dist/css/rules/textarea.css +28 -0
  70. package/dist/css/rules/time-range-picker.css +14 -0
  71. package/dist/css/rules/toast.css +3 -1
  72. package/dist/css/rules/toggle.css +10 -3
  73. package/dist/css/rules/toolbar.css +10 -3
  74. package/dist/css/rules/tree.css +29 -4
  75. package/dist/css/styles.css +3 -0
  76. package/dist/css/time-range-picker.css +14 -0
  77. package/dist/dashboard.d.ts +58 -0
  78. package/dist/dashboard.js +60 -0
  79. package/dist/date-picker.d.ts +32 -3
  80. package/dist/date-picker.js +43 -4
  81. package/dist/dialog.d.ts +25 -7
  82. package/dist/dialog.js +9 -4
  83. package/dist/drawer.d.ts +10 -2
  84. package/dist/drawer.js +6 -3
  85. package/dist/icon.d.ts +15 -5
  86. package/dist/icon.js +16 -3
  87. package/dist/index.d.ts +9 -6
  88. package/dist/index.js +7 -4
  89. package/dist/inline-message.d.ts +2 -2
  90. package/dist/inline-message.js +7 -2
  91. package/dist/input.d.ts +2 -2
  92. package/dist/layout.d.ts +1 -3
  93. package/dist/layout.js +2 -2
  94. package/dist/native-select.d.ts +2 -2
  95. package/dist/navigation-menu.d.ts +16 -0
  96. package/dist/navigation-menu.js +6 -1
  97. package/dist/page-header.d.ts +10 -2
  98. package/dist/page-header.js +3 -3
  99. package/dist/panel.d.ts +6 -3
  100. package/dist/panel.js +2 -2
  101. package/dist/parts.d.ts +7 -3
  102. package/dist/parts.js +12 -4
  103. package/dist/popover.d.ts +6 -6
  104. package/dist/popover.js +3 -2
  105. package/dist/preview-card.d.ts +2 -8
  106. package/dist/preview-card.js +2 -2
  107. package/dist/select.d.ts +38 -8
  108. package/dist/select.js +110 -22
  109. package/dist/sheet.d.ts +10 -2
  110. package/dist/sheet.js +6 -3
  111. package/dist/size.d.ts +8 -6
  112. package/dist/size.js +2 -7
  113. package/dist/sizing.d.ts +13 -6
  114. package/dist/sizing.js +13 -4
  115. package/dist/sparkline.d.ts +2 -2
  116. package/dist/sparkline.js +2 -2
  117. package/dist/spinner.d.ts +2 -2
  118. package/dist/spinner.js +2 -2
  119. package/dist/stat.d.ts +24 -0
  120. package/dist/stat.js +15 -1
  121. package/dist/table.d.ts +12 -4
  122. package/dist/table.js +8 -3
  123. package/dist/textarea.d.ts +6 -3
  124. package/dist/textarea.js +2 -2
  125. package/dist/time-range-picker.d.ts +42 -0
  126. package/dist/time-range-picker.js +69 -0
  127. package/dist/toggle.d.ts +2 -2
  128. package/dist/toggle.js +2 -2
  129. package/dist/toolbar.d.ts +11 -10
  130. package/dist/toolbar.js +23 -1
  131. package/dist/tooltip.d.ts +1 -3
  132. package/dist/tooltip.js +4 -2
  133. package/dist/tree.d.ts +1 -3
  134. package/dist/tree.js +2 -2
  135. package/dist/typography.d.ts +10 -3
  136. package/dist/typography.js +4 -3
  137. package/package.json +18 -3
package/dist/select.js CHANGED
@@ -1,15 +1,37 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import * as React from 'react';
4
4
  import { Icon } from './icon.js';
5
5
  import { useComponentSize } from './sizing.js';
6
6
  import { Select as BaseSelect } from '@base-ui/react/select';
7
- import { sizeName, cx } from './parts.js';
7
+ import { cx, usePortalDirection } from './parts.js';
8
8
  const isGroup = (item) => 'options' in item;
9
9
  const flatten = (items) => items.flatMap((item) => (isGroup(item) ? item.options : [item]));
10
- /** A single-choice list. The ref reaches the trigger button, as triggerRef does. */
11
- export const Select = function Select({ options: items, value, defaultValue, onValueChange, className, inputRef, triggerRef, size, ref, ...props }) {
10
+ /** What a form reset reports: it has already happened, so it can't be canceled. */
11
+ function resetDetails(event) {
12
+ return {
13
+ reason: 'none',
14
+ event,
15
+ trigger: undefined,
16
+ cancel() { },
17
+ allowPropagation() { },
18
+ isCanceled: false,
19
+ isPropagationAllowed: false,
20
+ };
21
+ }
22
+ const multipleLabels = {
23
+ all: 'All',
24
+ none: 'None',
25
+ count: (count) => `${count} selected`,
26
+ };
27
+ // The value the "all" option carries; it never reaches a form or a callback.
28
+ const ALL = '\u0000all';
29
+ /** A choice from a list, or several with multiple. The ref reaches the trigger
30
+ * button, as triggerRef does. */
31
+ export const Select = function Select(allProps) {
32
+ const { options: items, className, inputRef, triggerRef, size, width = 'fill', ref, multiple, value: _value, defaultValue: _defaultValue, onValueChange: _onValueChange, labels: _labels, ...props } = allProps;
12
33
  const scheme = useComponentSize(size);
34
+ const dir = usePortalDirection();
13
35
  const options = flatten(items);
14
36
  // One stable callback, so the trigger ref is not detached on every render.
15
37
  const setTrigger = React.useCallback((node) => {
@@ -19,11 +41,16 @@ export const Select = function Select({ options: items, value, defaultValue, onV
19
41
  else if (target)
20
42
  target.current = node;
21
43
  }, [ref, triggerRef]);
22
- const [localValue, setLocalValue] = React.useState(defaultValue ?? options[0]?.value ?? '');
44
+ const many = allProps.multiple === true ? allProps : undefined;
45
+ const one = allProps.multiple === true ? undefined : allProps;
46
+ const initialValue = many
47
+ ? (many.defaultValue ?? [])
48
+ : (one.defaultValue ?? options[0]?.value ?? '');
49
+ const [localValue, setLocalValue] = React.useState(initialValue);
23
50
  const nativeInput = React.useRef(null);
24
- const initialValue = defaultValue ?? options[0]?.value ?? '';
25
- const resetCallback = React.useRef(onValueChange);
26
- resetCallback.current = onValueChange;
51
+ const resetCallback = React.useRef(undefined);
52
+ resetCallback.current = allProps.onValueChange;
53
+ const resetKey = many ? initialValue.toString() : initialValue;
27
54
  React.useEffect(() => {
28
55
  const owner = nativeInput.current?.form;
29
56
  if (!owner)
@@ -35,7 +62,9 @@ export const Select = function Select({ options: items, value, defaultValue, onV
35
62
  if (event.defaultPrevented)
36
63
  return;
37
64
  setLocalValue(initialValue);
38
- resetCallback.current?.(initialValue);
65
+ resetCallback.current?.((Array.isArray(initialValue)
66
+ ? [...initialValue]
67
+ : initialValue), resetDetails(event));
39
68
  }, 0);
40
69
  pending.add(timer);
41
70
  };
@@ -44,20 +73,79 @@ export const Select = function Select({ options: items, value, defaultValue, onV
44
73
  owner.removeEventListener('reset', reset);
45
74
  pending.forEach(clearTimeout);
46
75
  };
47
- }, [initialValue, props.form]);
48
- const selected = value ?? localValue;
76
+ // initialValue is read through resetKey, so a new array of the same values
77
+ // does not re-register the listener.
78
+ }, [resetKey, props.form]);
49
79
  const { name, form, required, disabled, id, ...aria } = props;
50
- return (_jsx("span", { className: cx('surf-select', className), "data-size": sizeName(size), "data-ui-size": scheme, "data-disabled": disabled || undefined, children: _jsxs(BaseSelect.Root, { items: options, value: selected, onValueChange: (v) => {
51
- setLocalValue(v ?? '');
52
- onValueChange?.(v ?? '');
53
- }, inputRef: (node) => {
54
- nativeInput.current = node;
55
- if (typeof inputRef === 'function')
56
- inputRef(node);
57
- else if (inputRef)
58
- inputRef.current = node;
59
- }, name: name, form: form, required: required, disabled: disabled, id: id, children: [_jsxs(BaseSelect.Trigger, { ...aria, ref: setTrigger, className: 'surf-select-trigger', "data-value": selected, children: [_jsx(BaseSelect.Value, {}), _jsx(BaseSelect.Icon, { className: 'surf-select-icon', children: _jsx(Icon, { name: 'chevron-down' }) })] }), _jsx(BaseSelect.Portal, { children: _jsx(BaseSelect.Positioner, { className: 'surf-positioner', sideOffset: 6, alignItemWithTrigger: false, children: _jsx(BaseSelect.Popup, { className: 'surf-select-popup', "data-ui-size": scheme, children: _jsx(BaseSelect.List, { children: items.map((item, index) => isGroup(item) ? (_jsxs(BaseSelect.Group, { className: 'surf-select-group', children: [_jsx(BaseSelect.GroupLabel, { className: 'surf-select-group-label', children: item.label }), item.options.map(renderOption)] }, index)) : (renderOption(item))) }) }) }) })] }) }));
80
+ const setInput = (node) => {
81
+ nativeInput.current = node;
82
+ if (typeof inputRef === 'function')
83
+ inputRef(node);
84
+ else if (inputRef)
85
+ inputRef.current = node;
86
+ };
87
+ // Several values: the "all" option leads the list, and the trigger sums them up.
88
+ const choosable = options.filter((option) => !option.disabled);
89
+ const text = { ...multipleLabels, ...(many?.labels ?? {}) };
90
+ const chosen = many
91
+ ? (many.value ?? localValue)
92
+ : [];
93
+ const everything = choosable.length > 0 &&
94
+ choosable.every((option) => chosen.includes(option.value));
95
+ const summary = (values) => values.length === 0
96
+ ? text.none
97
+ : choosable.every((option) => values.includes(option.value))
98
+ ? text.all
99
+ : values.length === 1
100
+ ? options.find((option) => option.value === values[0])?.label
101
+ : text.count(values.length);
102
+ const selected = many
103
+ ? everything
104
+ ? [ALL, ...chosen]
105
+ : chosen
106
+ : (one.value ?? localValue);
107
+ const value = many ? summary(chosen) : _jsx(BaseSelect.Value, {});
108
+ // Content width: every label the trigger can show, stacked in one cell.
109
+ const sizes = many
110
+ ? [
111
+ text.all,
112
+ text.none,
113
+ text.count(options.length),
114
+ ...options.map((option) => option.label),
115
+ ]
116
+ : options.map((option) => option.label);
117
+ return (_jsx("span", { className: cx('surf-select', className), "data-size": size, "data-ui-size": scheme, "data-width": width, "data-disabled": disabled || undefined, children: _jsxs(BaseSelect.Root, { items: options, multiple: multiple, value: selected, onValueChange: (next, eventDetails) => {
118
+ if (many) {
119
+ const list = next ?? [];
120
+ const toggledAll = list.includes(ALL) !== everything;
121
+ const values = toggledAll
122
+ ? everything
123
+ ? []
124
+ : choosable.map((option) => option.value)
125
+ : // Unchecking one option of "all" leaves the others.
126
+ list.filter((item) => item !== ALL);
127
+ many.onValueChange?.(values, eventDetails);
128
+ if (!eventDetails.isCanceled)
129
+ setLocalValue(values);
130
+ }
131
+ else {
132
+ one.onValueChange?.(next ?? '', eventDetails);
133
+ if (!eventDetails.isCanceled)
134
+ setLocalValue(next ?? '');
135
+ }
136
+ }, inputRef: setInput,
137
+ // A multiple Select submits its own inputs, without the "all" value.
138
+ name: many ? undefined : name, form: form, required: required, disabled: disabled, id: id, children: [_jsxs(BaseSelect.Trigger, { ...aria, ref: setTrigger, className: 'surf-select-trigger', "data-value": many ? chosen.join(' ') : selected, children: [width === 'content' ? (
139
+ // Every label sits in one cell, hidden but the chosen one, so the
140
+ // trigger is as wide as the longest.
141
+ _jsxs("span", { className: 'surf-select-sizer', children: [_jsx("span", { className: 'surf-select-value', children: value }), sizes.map((label, index) => (_jsx("span", { "aria-hidden": 'true', children: label }, index)))] })) : many ? (_jsx("span", { className: 'surf-select-value', children: value })) : (value), _jsx(BaseSelect.Icon, { className: 'surf-select-icon', children: _jsx(Icon, { name: 'chevron-down' }) })] }), many &&
142
+ name &&
143
+ chosen.map((item) => (_jsx("input", { type: 'hidden', name: name, value: item, form: form }, item))), _jsx(BaseSelect.Portal, { children: _jsx(BaseSelect.Positioner, { dir: dir, className: 'surf-positioner', sideOffset: 6, alignItemWithTrigger: false, children: _jsx(BaseSelect.Popup, { className: 'surf-select-popup', "data-ui-size": scheme, children: _jsxs(BaseSelect.List, { children: [many && (_jsxs(_Fragment, { children: [renderOption({
144
+ value: ALL,
145
+ label: text.all,
146
+ disabled: choosable.length === 0,
147
+ }), _jsx(BaseSelect.Separator, { className: 'surf-select-separator' })] })), items.map((item, index) => isGroup(item) ? (_jsxs(BaseSelect.Group, { className: 'surf-select-group', children: [_jsx(BaseSelect.GroupLabel, { className: 'surf-select-group-label', children: item.label }), item.options.map(renderOption)] }, index)) : (renderOption(item)))] }) }) }) })] }) }));
60
148
  };
61
149
  function renderOption(option) {
62
- return (_jsxs(BaseSelect.Item, { className: 'surf-select-item', value: option.value, "data-option-value": option.value, disabled: option.disabled, children: [_jsx(BaseSelect.ItemIndicator, { className: 'surf-select-check', children: _jsx(Icon, { name: 'check' }) }), _jsx(BaseSelect.ItemText, { children: option.label })] }, option.value));
150
+ return (_jsxs(BaseSelect.Item, { className: 'surf-select-item', value: option.value, "data-option-value": option.value === ALL ? undefined : option.value, disabled: option.disabled, children: [_jsx(BaseSelect.ItemIndicator, { className: 'surf-select-check', children: _jsx(Icon, { name: 'check' }) }), _jsx(BaseSelect.ItemText, { children: option.label })] }, option.value));
63
151
  }
package/dist/sheet.d.ts CHANGED
@@ -4,14 +4,22 @@ import { Footer, Header, type PanelOptions, type PanelSide } from './panel.js';
4
4
  declare function SheetRoot({ side, modal, ...props }: React.ComponentProps<typeof BaseDialog.Root> & {
5
5
  side?: PanelSide;
6
6
  }): React.JSX.Element;
7
- declare const SheetContent: ({ size, flush, closeLabel, className, children, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDialog.Popup> & PanelOptions & {
7
+ declare const SheetContent: ({ size, flush, closeLabel, focusBoundaryLabel, className, children, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDialog.Popup> & PanelOptions & {
8
+ /** Names the focus guards at the panel's edges, which VoiceOver reaches past
9
+ * the last control, as Dialog's do. */
10
+ focusBoundaryLabel?: string;
11
+ } & {
8
12
  ref?: React.Ref<HTMLDivElement>;
9
13
  }) => React.JSX.Element;
10
14
  declare function SheetTitle({ className, ...props }: React.ComponentProps<typeof BaseDialog.Title>): React.JSX.Element;
11
15
  declare function SheetDescription({ className, ...props }: React.ComponentProps<typeof BaseDialog.Description>): React.JSX.Element;
12
16
  export declare const Sheet: {
13
17
  Root: typeof SheetRoot;
14
- Content: ({ size, flush, closeLabel, className, children, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDialog.Popup> & PanelOptions & {
18
+ Content: ({ size, flush, closeLabel, focusBoundaryLabel, className, children, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDialog.Popup> & PanelOptions & {
19
+ /** Names the focus guards at the panel's edges, which VoiceOver reaches past
20
+ * the last control, as Dialog's do. */
21
+ focusBoundaryLabel?: string;
22
+ } & {
15
23
  ref?: React.Ref<HTMLDivElement>;
16
24
  }) => React.JSX.Element;
17
25
  Header: typeof Header;
package/dist/sheet.js CHANGED
@@ -2,7 +2,8 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from 'react';
4
4
  import { Icon } from './icon.js';
5
- import { skin } from './parts.js';
5
+ import { useDialogFocusBoundary } from './dialog-focus.js';
6
+ import { skin, usePortalDirection } from './parts.js';
6
7
  import { useComponentSize, normalizeSize } from './sizing.js';
7
8
  import { Dialog as BaseDialog } from '@base-ui/react/dialog';
8
9
  import { Body, Footer, Header, } from './panel.js';
@@ -13,10 +14,12 @@ const SheetSide = /* @__PURE__ */ React.createContext({
13
14
  function SheetRoot({ side = 'right', modal = true, ...props }) {
14
15
  return (_jsx(SheetSide.Provider, { value: { side, modal }, children: _jsx(BaseDialog.Root, { ...props, modal: modal }) }));
15
16
  }
16
- const SheetContent = function SheetContent({ size, flush, closeLabel = 'Close panel', className, children, ref, ...props }) {
17
+ const SheetContent = function SheetContent({ size, flush, closeLabel = 'Close panel', focusBoundaryLabel = 'Continue panel navigation', className, children, ref, ...props }) {
17
18
  const scheme = useComponentSize(size);
19
+ const dir = usePortalDirection();
20
+ const portalRef = useDialogFocusBoundary(focusBoundaryLabel);
18
21
  const { side, modal } = React.useContext(SheetSide);
19
- return (_jsx(BaseDialog.Portal, { children: _jsxs("div", { className: 'surf-panel-layer', "data-ui-size": scheme, children: [modal === true && (_jsx(BaseDialog.Backdrop, { className: 'surf-panel-backdrop' })), _jsx("div", { className: 'surf-panel-viewport', "data-side": side, "data-flush": flush || undefined, children: _jsxs(BaseDialog.Popup, { ...props, ref: ref, className: skin('surf-panel', className), "data-side": side, "data-size": normalizeSize(size) ?? 'default', "data-flush": flush || undefined, children: [_jsx(BaseDialog.Close, { className: 'surf-panel-close surf-icon-button', "aria-label": closeLabel, children: _jsx(Icon, { name: 'close' }) }), children] }) })] }) }));
22
+ return (_jsx(BaseDialog.Portal, { ref: portalRef, children: _jsxs("div", { className: 'surf-panel-layer', "data-ui-size": scheme, children: [modal === true && (_jsx(BaseDialog.Backdrop, { className: 'surf-panel-backdrop' })), _jsx("div", { className: 'surf-panel-viewport', "data-side": side, "data-flush": flush || undefined, children: _jsxs(BaseDialog.Popup, { dir: dir, ...props, ref: ref, className: skin('surf-panel', className), "data-side": side, "data-size": normalizeSize(size) ?? 'default', "data-flush": flush || undefined, children: [_jsx(BaseDialog.Close, { className: 'surf-panel-close surf-icon-button', "aria-label": closeLabel, children: _jsx(Icon, { name: 'close' }) }), children] }) })] }) }));
20
23
  };
21
24
  function SheetTitle({ className, ...props }) {
22
25
  return (_jsx(BaseDialog.Title, { ...props, className: skin('surf-panel-title', className) }));
package/dist/size.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export type ComponentSize = 'small' | 'default' | 'large';
2
- /**
3
- * @deprecated The short spellings sm, md and lg render as small, default and large.
4
- * Write the full names; the aliases are removed in a later minor.
5
- */
6
- export type ComponentSizeAlias = ComponentSize | 'sm' | 'md' | 'lg';
7
- export declare function normalizeSize(size?: ComponentSizeAlias): ComponentSize | undefined;
2
+ /** How tightly a screen packs its blocks: compact tightens gaps, insets and touch growth. */
3
+ export type ComponentDensity = 'default' | 'compact';
4
+ /** @deprecated The short spellings sm, md and lg were removed in 0.13, so this is
5
+ * ComponentSize under its former name. Write ComponentSize; this name is removed in a
6
+ * later minor. */
7
+ export type ComponentSizeAlias = ComponentSize;
8
+ /** A component's size: the one place components read it, kept for server components. */
9
+ export declare function normalizeSize(size?: ComponentSize): ComponentSize | undefined;
package/dist/size.js CHANGED
@@ -1,9 +1,4 @@
1
+ /** A component's size: the one place components read it, kept for server components. */
1
2
  export function normalizeSize(size) {
2
- return size === 'sm'
3
- ? 'small'
4
- : size === 'md'
5
- ? 'default'
6
- : size === 'lg'
7
- ? 'large'
8
- : size;
3
+ return size;
9
4
  }
package/dist/sizing.d.ts CHANGED
@@ -1,13 +1,20 @@
1
1
  import * as React from 'react';
2
- import { normalizeSize, type ComponentSize, type ComponentSizeAlias } from './size.js';
3
- export { normalizeSize, type ComponentSize, type ComponentSizeAlias };
2
+ import { normalizeSize, type ComponentDensity, type ComponentSize, type ComponentSizeAlias } from './size.js';
3
+ export { normalizeSize, type ComponentDensity, type ComponentSize, type ComponentSizeAlias, };
4
4
  export declare const ComponentSizeContext: React.Context<ComponentSize | undefined>;
5
5
  /** Explicit sizes override the surrounding scheme; portals retain React context. */
6
- export declare function useComponentSize(size?: ComponentSizeAlias): ComponentSize | undefined;
6
+ export declare function useComponentSize(size?: ComponentSize): ComponentSize | undefined;
7
+ export declare const ComponentDensityContext: React.Context<ComponentDensity | undefined>;
8
+ /** A component's own density, else the surrounding one, else default. */
9
+ export declare function useComponentDensity(density?: ComponentDensity): ComponentDensity;
7
10
  /** No layout box: grid/flex items and semantic children retain their own layout.
8
- * For table internals, place the provider outside the table, never inside tbody. */
9
- export declare function SizeProvider({ size, children, }: {
10
- size: ComponentSize;
11
+ * For table internals, place the provider outside the table, never inside tbody.
12
+ * Either prop may be left out to keep the surrounding size or density. */
13
+ export declare function SizeProvider({ size, density, children, }: {
14
+ size?: ComponentSize;
15
+ /** compact tightens the gaps and insets of the blocks inside, and keeps controls
16
+ * at their size on touch screens instead of growing them to 44px. */
17
+ density?: ComponentDensity;
11
18
  children: React.ReactNode;
12
19
  }): React.JSX.Element;
13
20
  /** The size context for a part a server component renders with an explicit size, so
package/dist/sizing.js CHANGED
@@ -2,17 +2,26 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import * as React from 'react';
4
4
  import { normalizeSize, } from './size.js';
5
- export { normalizeSize };
5
+ export { normalizeSize, };
6
6
  export const ComponentSizeContext = /* @__PURE__ */ React.createContext(undefined);
7
7
  /** Explicit sizes override the surrounding scheme; portals retain React context. */
8
8
  export function useComponentSize(size) {
9
9
  const inherited = React.useContext(ComponentSizeContext);
10
10
  return normalizeSize(size) ?? inherited;
11
11
  }
12
+ export const ComponentDensityContext = /* @__PURE__ */ React.createContext(undefined);
13
+ /** A component's own density, else the surrounding one, else default. */
14
+ export function useComponentDensity(density) {
15
+ const inherited = React.useContext(ComponentDensityContext);
16
+ return density ?? inherited ?? 'default';
17
+ }
12
18
  /** No layout box: grid/flex items and semantic children retain their own layout.
13
- * For table internals, place the provider outside the table, never inside tbody. */
14
- export function SizeProvider({ size, children, }) {
15
- return (_jsx(ComponentSizeContext.Provider, { value: size, children: _jsx("div", { className: 'surf-size-scope', "data-ui-size": size, children: children }) }));
19
+ * For table internals, place the provider outside the table, never inside tbody.
20
+ * Either prop may be left out to keep the surrounding size or density. */
21
+ export function SizeProvider({ size, density, children, }) {
22
+ const inheritedSize = React.useContext(ComponentSizeContext);
23
+ const inheritedDensity = React.useContext(ComponentDensityContext);
24
+ return (_jsx(ComponentSizeContext.Provider, { value: size ?? inheritedSize, children: _jsx(ComponentDensityContext.Provider, { value: density ?? inheritedDensity, children: _jsx("div", { className: 'surf-size-scope', "data-ui-size": size, "data-ui-density": density, children: children }) }) }));
16
25
  }
17
26
  /** The size context for a part a server component renders with an explicit size, so
18
27
  * the popups it portals keep that size. */
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import type { ComponentSizeAlias } from './size.js';
2
+ import type { ComponentSize } from './size.js';
3
3
  export type SparklineTrend = 'auto' | 'up' | 'down' | 'flat' | 'neutral';
4
4
  export type SparklineProps = Omit<React.ComponentPropsWithoutRef<'svg'>, 'children' | 'role' | 'aria-label'> & {
5
5
  /** Values in order. A missing value leaves a gap. */
@@ -8,7 +8,7 @@ export type SparklineProps = Omit<React.ComponentPropsWithoutRef<'svg'>, 'childr
8
8
  * line is an image; this is its name. */
9
9
  label: string;
10
10
  /** 24, 32 or 48px tall. Set --surf-sparkline-height for another height. */
11
- size?: ComponentSizeAlias;
11
+ size?: ComponentSize;
12
12
  /** A reference line: a value, the first value, or none. */
13
13
  baseline?: number | 'first' | 'none';
14
14
  /** Marks the last value with a dot. */
package/dist/sparkline.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cx, sizeName } from './class-names.js';
2
+ import { cx } from './class-names.js';
3
3
  const finite = (value) => typeof value === 'number' && Number.isFinite(value);
4
4
  /** A small trend line for cards and table cells: no axes, legend or tooltip. Use Chart
5
5
  * when people need to read values. */
@@ -41,7 +41,7 @@ export const Sparkline = function Sparkline({ data, label, size, baseline = 'non
41
41
  let end = data.length - 1;
42
42
  while (end >= 0 && !finite(data[end]))
43
43
  end--;
44
- return (_jsxs("svg", { ...props, ref: ref, role: 'img', "aria-label": label, className: cx('surf-sparkline', className), "data-size": sizeName(size), "data-tone": tone, viewBox: '0 0 100 100', preserveAspectRatio: 'none', focusable: 'false', children: [finite(reference) && (_jsx("path", { className: 'surf-sparkline-baseline', d: `M0 ${y(reference)}H100`, vectorEffect: 'non-scaling-stroke' })), _jsx("path", { className: 'surf-sparkline-line', d: path, vectorEffect: 'non-scaling-stroke' }), marker && end >= 0 && (
44
+ return (_jsxs("svg", { ...props, ref: ref, role: 'img', "aria-label": label, className: cx('surf-sparkline', className), "data-size": size, "data-tone": tone, viewBox: '0 0 100 100', preserveAspectRatio: 'none', focusable: 'false', children: [finite(reference) && (_jsx("path", { className: 'surf-sparkline-baseline', d: `M0 ${y(reference)}H100`, vectorEffect: 'non-scaling-stroke' })), _jsx("path", { className: 'surf-sparkline-line', d: path, vectorEffect: 'non-scaling-stroke' }), marker && end >= 0 && (
45
45
  // A zero-length segment with a round cap stays a circle however the
46
46
  // line stretches.
47
47
  _jsx("path", { className: 'surf-sparkline-marker', d: `M${x(end)} ${y(data[end])}h0`, vectorEffect: 'non-scaling-stroke' }))] }));
package/dist/spinner.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
- import { type ComponentSizeAlias } from './size.js';
2
+ import { type ComponentSize } from './size.js';
3
3
  export type SpinnerProps = Omit<React.ComponentPropsWithoutRef<'span'>, 'children'> & {
4
4
  label?: string;
5
5
  decorative?: boolean;
6
- size?: ComponentSizeAlias;
6
+ size?: ComponentSize;
7
7
  };
8
8
  /** Use decorative within a loading region that already owns the announcement. */
9
9
  export declare const Spinner: ({ label, decorative, size, className, ref, ...props }: SpinnerProps & {
package/dist/spinner.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { normalizeSize } from './size.js';
3
- import { sizeName, cx } from './class-names.js';
3
+ import { cx } from './class-names.js';
4
4
  /** Use decorative within a loading region that already owns the announcement. */
5
5
  export const Spinner = function Spinner({ label = 'Loading', decorative = false, size, className, ref, ...props }) {
6
6
  // An omitted size follows the surrounding size scope through CSS.
7
7
  const scheme = normalizeSize(size);
8
- return (_jsxs("span", { ...props, ref: ref, className: cx('surf-spinner', className), "data-size": sizeName(size), "data-ui-size": scheme, role: decorative ? undefined : 'status', "aria-hidden": decorative || undefined, children: [_jsxs("svg", { viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', "aria-hidden": 'true', children: [_jsx("circle", { cx: '12', cy: '12', r: '9', opacity: '.2' }), _jsx("path", { d: 'M12 3a9 9 0 0 1 9 9', strokeLinecap: 'round' })] }), !decorative && _jsx("span", { className: 'surf-feedback-sr', children: label })] }));
8
+ return (_jsxs("span", { ...props, ref: ref, className: cx('surf-spinner', className), "data-size": size, "data-ui-size": scheme, role: decorative ? undefined : 'status', "aria-hidden": decorative || undefined, children: [_jsxs("svg", { viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', "aria-hidden": 'true', children: [_jsx("circle", { cx: '12', cy: '12', r: '9', opacity: '.2' }), _jsx("path", { d: 'M12 3a9 9 0 0 1 9 9', strokeLinecap: 'round' })] }), !decorative && _jsx("span", { className: 'surf-feedback-sr', children: label })] }));
9
9
  };
package/dist/stat.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { type StatusTone } from './status.js';
2
3
  export type StatDirection = 'up' | 'down' | 'flat';
3
4
  export type StatChangeProps = Omit<React.ComponentPropsWithoutRef<'dd'>, 'children'> & {
4
5
  /** The change, such as 0.023 for 2.3%. Its sign sets the direction. */
@@ -11,6 +12,19 @@ export type StatChangeProps = Omit<React.ComponentPropsWithoutRef<'dd'>, 'childr
11
12
  * still points the way the value moved. */
12
13
  lowerIsBetter?: boolean;
13
14
  };
15
+ export type StatStatusProps = React.ComponentPropsWithoutRef<'dd'> & {
16
+ /** The value's state: caution for one to watch, danger for one past its limit. */
17
+ tone: StatusTone;
18
+ };
19
+ export type StatGroupProps = React.ComponentPropsWithoutRef<'div'> & {
20
+ /** What every value in the group measures, such as "GPU temperature". It shows
21
+ * above them and names the group. */
22
+ label: string;
23
+ /** Names the group without showing the label, where a heading above already says
24
+ * it, such as the title of the Dashboard.Panel that holds the group. Give it a
25
+ * name of its own, such as "Free disk by node", so the two groups read apart. */
26
+ labelHidden?: boolean;
27
+ };
14
28
  /** A key figure: what it measures, its value, how it moved and what it covers. Renders
15
29
  * a description list, so the label names the value for assistive technology. */
16
30
  export declare const Stat: {
@@ -29,10 +43,16 @@ export declare const Stat: {
29
43
  Change: ({ value, format, direction, lowerIsBetter, className, ref, ...props }: StatChangeProps & {
30
44
  ref?: React.Ref<HTMLElement>;
31
45
  }) => React.JSX.Element;
46
+ Status: ({ tone, className, children, ref, ...props }: StatStatusProps & {
47
+ ref?: React.Ref<HTMLElement>;
48
+ }) => React.JSX.Element;
32
49
  Caption: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
33
50
  size?: import("./size.js").ComponentSize;
34
51
  ref?: React.Ref<HTMLElement> | undefined;
35
52
  }) => React.JSX.Element;
53
+ Group: ({ label, labelHidden, className, children, ref, ...props }: StatGroupProps & {
54
+ ref?: React.Ref<HTMLDivElement>;
55
+ }) => React.JSX.Element;
36
56
  };
37
57
  export declare const StatRoot: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDListElement>, HTMLDListElement>, "ref">, "size"> & {
38
58
  size?: import("./size.js").ComponentSize;
@@ -45,7 +65,11 @@ export declare const StatRoot: ({ className, size, ref, ...rest }: Omit<Omit<Rea
45
65
  ref?: React.Ref<HTMLElement> | undefined;
46
66
  }) => React.JSX.Element, StatChange: ({ value, format, direction, lowerIsBetter, className, ref, ...props }: StatChangeProps & {
47
67
  ref?: React.Ref<HTMLElement>;
68
+ }) => React.JSX.Element, StatStatus: ({ tone, className, children, ref, ...props }: StatStatusProps & {
69
+ ref?: React.Ref<HTMLElement>;
48
70
  }) => React.JSX.Element, StatCaption: ({ className, size, ref, ...rest }: Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "size"> & {
49
71
  size?: import("./size.js").ComponentSize;
50
72
  ref?: React.Ref<HTMLElement> | undefined;
73
+ }) => React.JSX.Element, StatGroup: ({ label, labelHidden, className, children, ref, ...props }: StatGroupProps & {
74
+ ref?: React.Ref<HTMLDivElement>;
51
75
  }) => React.JSX.Element;
package/dist/stat.js CHANGED
@@ -3,6 +3,7 @@ import { Icon } from './icon.js';
3
3
  import { cx } from './class-names.js';
4
4
  import { elementPart } from './element-part.js';
5
5
  import { formatPercent } from './format.js';
6
+ import { StatusIcon } from './status.js';
6
7
  const signed = (value) => formatPercent(value, { signed: true });
7
8
  const arrows = { up: 'arrow-up', down: 'arrow-down', flat: 'minus' };
8
9
  /** A change against an earlier value: an arrow, a signed figure and its direction's
@@ -12,6 +13,17 @@ const ChangePart = function StatChange({ value, format = signed, direction, lowe
12
13
  const tone = way === 'flat' || !lowerIsBetter ? way : way === 'up' ? 'down' : 'up';
13
14
  return (_jsxs("dd", { ...props, ref: ref, "data-direction": way, "data-tone": tone, className: cx('surf-stat-change', className), children: [_jsx(Icon, { name: arrows[way] }), format(value)] }));
14
15
  };
16
+ /** The value's state against a threshold, such as "Below 30 GiB": the tone's icon and
17
+ * text in its color. A caution or danger status colors the value too, and the text
18
+ * says why, so color is never alone. */
19
+ const StatusPart = function StatStatus({ tone, className, children, ref, ...props }) {
20
+ return (_jsxs("dd", { ...props, ref: ref, "data-tone": tone, className: cx('surf-stat-status', className), children: [_jsx(StatusIcon, { tone: tone }), children] }));
21
+ };
22
+ /** Several values of one measure side by side, such as one per node: a shared label
23
+ * over Stat.Roots whose own labels name each. */
24
+ const GroupPart = function StatGroup({ label, labelHidden = false, className, children, ref, ...props }) {
25
+ return (_jsxs("div", { role: 'group', "aria-label": label, ...props, ref: ref, className: cx('surf-stat-group', className), children: [!labelHidden && (_jsx("span", { className: 'surf-stat-group-label', "aria-hidden": 'true', children: label })), _jsx("div", { className: 'surf-stat-group-items', children: children })] }));
26
+ };
15
27
  /** A key figure: what it measures, its value, how it moved and what it covers. Renders
16
28
  * a description list, so the label names the value for assistive technology. */
17
29
  export const Stat = {
@@ -19,7 +31,9 @@ export const Stat = {
19
31
  Label: /* @__PURE__ */ elementPart('dt', 'surf-stat-label'),
20
32
  Value: /* @__PURE__ */ elementPart('dd', 'surf-stat-value'),
21
33
  Change: ChangePart,
34
+ Status: StatusPart,
22
35
  Caption: /* @__PURE__ */ elementPart('dd', 'surf-stat-caption'),
36
+ Group: GroupPart,
23
37
  };
24
38
  // Flat names for React Server Components, which cannot dot into client exports.
25
- export const { Root: StatRoot, Label: StatLabel, Value: StatValue, Change: StatChange, Caption: StatCaption, } = Stat;
39
+ export const { Root: StatRoot, Label: StatLabel, Value: StatValue, Change: StatChange, Status: StatStatus, Caption: StatCaption, Group: StatGroup, } = Stat;
package/dist/table.d.ts CHANGED
@@ -15,7 +15,8 @@ declare const RootPart: ({ className, size, ref, ...rest }: Omit<Omit<React.Deta
15
15
  export type TableDensity = 'default' | 'compact';
16
16
  export type TableVariant = 'plain' | 'lined' | 'grid' | 'striped';
17
17
  declare const TableRoot: ({ density, variant, stickyHeader, ref, ...props }: React.ComponentPropsWithoutRef<typeof RootPart> & {
18
- /** compact tightens rows for scanning many records with a pointer. */
18
+ /** compact tightens rows for scanning many records with a pointer. Without it the
19
+ * table follows a SizeProvider density; default keeps it roomy inside a compact one. */
19
20
  density?: TableDensity;
20
21
  /**
21
22
  * plain separates rows by space; lined draws lines between rows, grid between
@@ -56,7 +57,10 @@ declare const TableSortHeader: ({ sort, onSort, disabled, children, className, r
56
57
  ref?: React.Ref<HTMLTableCellElement>;
57
58
  }) => React.JSX.Element;
58
59
  /** One-line text that ends in an ellipsis. String content also becomes the title. */
59
- declare const TableTruncate: ({ className, title, children, ref, ...props }: React.ComponentPropsWithoutRef<"span"> & {
60
+ declare const TableTruncate: ({ measure, className, title, style, children, ref, ...props }: React.ComponentPropsWithoutRef<"span"> & {
61
+ /** The widest it gets before the ellipsis, such as '44ch' for long image
62
+ * references; 32ch without it. */
63
+ measure?: string;
60
64
  ref?: React.Ref<HTMLSpanElement>;
61
65
  }) => React.JSX.Element;
62
66
  /** A full-width body row for loading, empty, error and permission messages. It has no hover. */
@@ -80,7 +84,8 @@ export declare const Table: {
80
84
  ref?: React.Ref<HTMLDivElement>;
81
85
  }) => React.JSX.Element;
82
86
  Root: ({ density, variant, stickyHeader, ref, ...props }: React.ComponentPropsWithoutRef<typeof RootPart> & {
83
- /** compact tightens rows for scanning many records with a pointer. */
87
+ /** compact tightens rows for scanning many records with a pointer. Without it the
88
+ * table follows a SizeProvider density; default keeps it roomy inside a compact one. */
84
89
  density?: TableDensity;
85
90
  /**
86
91
  * plain separates rows by space; lined draws lines between rows, grid between
@@ -126,7 +131,10 @@ export declare const Table: {
126
131
  Cell: ({ align, numeric, ref, ...props }: Omit<React.ComponentPropsWithoutRef<typeof CellPart>, "align"> & CellLayout & {
127
132
  ref?: React.Ref<HTMLTableCellElement>;
128
133
  }) => React.JSX.Element;
129
- Truncate: ({ className, title, children, ref, ...props }: React.ComponentPropsWithoutRef<"span"> & {
134
+ Truncate: ({ measure, className, title, style, children, ref, ...props }: React.ComponentPropsWithoutRef<"span"> & {
135
+ /** The widest it gets before the ellipsis, such as '44ch' for long image
136
+ * references; 32ch without it. */
137
+ measure?: string;
130
138
  ref?: React.Ref<HTMLSpanElement>;
131
139
  }) => React.JSX.Element;
132
140
  Message: ({ className, ref, ...props }: React.ComponentPropsWithoutRef<"td"> & {
package/dist/table.js CHANGED
@@ -9,7 +9,7 @@ const TableContainer = function TableContainer({ bleed, rootClassName, ref, ...p
9
9
  };
10
10
  const RootPart = /* @__PURE__ */ elementPart('table', 'surf-table');
11
11
  const TableRoot = function TableRoot({ density, variant, stickyHeader, ref, ...props }) {
12
- return (_jsx(RootPart, { ...props, ref: ref, "data-density": density === 'compact' ? 'compact' : undefined, "data-variant": variant && variant !== 'plain' ? variant : undefined, "data-sticky-header": stickyHeader || undefined }));
12
+ return (_jsx(RootPart, { ...props, ref: ref, "data-density": density, "data-variant": variant && variant !== 'plain' ? variant : undefined, "data-sticky-header": stickyHeader || undefined }));
13
13
  };
14
14
  const layout = ({ align, numeric }) => ({
15
15
  'data-align': numeric ? 'end' : align,
@@ -36,8 +36,13 @@ const TableSortHeader = function TableSortHeader({ sort, onSort, disabled, child
36
36
  return (_jsx(TableHeaderCell, { ...props, ref: ref, scope: 'col', "aria-sort": sort, className: cx('surf-table-sort-header', className), children: _jsxs(Button, { variant: 'ghost', className: 'surf-table-sort', "data-sort": sort, disabled: disabled, onClick: onSort, children: [_jsx("span", { children: children }), _jsx(SortIcon, { sort: sort })] }) }));
37
37
  };
38
38
  /** One-line text that ends in an ellipsis. String content also becomes the title. */
39
- const TableTruncate = function TableTruncate({ className, title, children, ref, ...props }) {
40
- return (_jsx("span", { ...props, ref: ref, title: title ?? (typeof children === 'string' ? children : undefined), className: cx('surf-table-truncate', className), children: children }));
39
+ const TableTruncate = function TableTruncate({ measure, className, title, style, children, ref, ...props }) {
40
+ return (_jsx("span", { ...props, ref: ref, title: title ?? (typeof children === 'string' ? children : undefined), style: measure
41
+ ? {
42
+ ...style,
43
+ '--surf-table-truncate-measure': measure,
44
+ }
45
+ : style, className: cx('surf-table-truncate', className), children: children }));
41
46
  };
42
47
  /** A full-width body row for loading, empty, error and permission messages. It has no hover. */
43
48
  const TableMessage = function TableMessage({ className, ref, ...props }) {
@@ -1,9 +1,12 @@
1
1
  import * as React from 'react';
2
- import { type ComponentSizeAlias } from './sizing.js';
2
+ import { type ComponentSize } from './sizing.js';
3
3
  /** A multi-line text field. Inside Field.Root it takes the label, description and
4
4
  * error from the field, as Input does. */
5
- export declare const Textarea: ({ className, size, rows, cols, wrap, ref, ...props }: React.ComponentPropsWithoutRef<"textarea"> & {
6
- size?: ComponentSizeAlias;
5
+ export declare const Textarea: ({ className, size, variant, rows, cols, wrap, ref, ...props }: React.ComponentPropsWithoutRef<"textarea"> & {
6
+ size?: ComponentSize;
7
+ /** `embedded` fills the pane it sits in, such as an editor beside a preview: no
8
+ * edge or corners of its own, and focus as a ring inside it. */
9
+ variant?: "default" | "embedded";
7
10
  } & {
8
11
  ref?: React.Ref<HTMLTextAreaElement>;
9
12
  }) => React.JSX.Element;
package/dist/textarea.js CHANGED
@@ -5,7 +5,7 @@ import { Field as BaseField } from '@base-ui/react/field';
5
5
  import { cx } from './parts.js';
6
6
  /** A multi-line text field. Inside Field.Root it takes the label, description and
7
7
  * error from the field, as Input does. */
8
- export const Textarea = function Textarea({ className, size, rows, cols, wrap, ref, ...props }) {
8
+ export const Textarea = function Textarea({ className, size, variant = 'default', rows, cols, wrap, ref, ...props }) {
9
9
  const scheme = useComponentSize(size);
10
- return (_jsx(BaseField.Control, { ...props, ref: ref, "data-ui-size": scheme, render: _jsx("textarea", { rows: rows, cols: cols, wrap: wrap }), className: cx('surf-input surf-textarea', className) }));
10
+ return (_jsx(BaseField.Control, { ...props, ref: ref, "data-ui-size": scheme, "data-variant": variant === 'embedded' ? variant : undefined, render: _jsx("textarea", { rows: rows, cols: cols, wrap: wrap }), className: cx('surf-input surf-textarea', className) }));
11
11
  };
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+ import type { ComponentSize } from './size.js';
3
+ /** A span of time back from now, or a refresh interval, in milliseconds. */
4
+ export type TimeRangeOption = {
5
+ value: number;
6
+ label: string;
7
+ };
8
+ export declare const timeRangeOptions: readonly TimeRangeOption[];
9
+ /** 0 is off. */
10
+ export declare const refreshIntervalOptions: readonly TimeRangeOption[];
11
+ export type TimeRangePickerLabels = {
12
+ /** The range select's name. */
13
+ range: string;
14
+ /** The refresh interval select's name. */
15
+ refresh: string;
16
+ /** The button that refreshes now. */
17
+ refreshNow: string;
18
+ };
19
+ export type TimeRangePickerProps = Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> & {
20
+ /** Names the group, such as "Dashboard time". */
21
+ label: string;
22
+ /** The span back from now, in ms; one of ranges. */
23
+ range: number;
24
+ onRangeChange: (range: number) => void;
25
+ /** The span options, such as timeRangeOptions with translated labels. */
26
+ ranges?: readonly TimeRangeOption[];
27
+ /** How often onRefresh runs, in ms; 0 is off. Leave it out for no interval select. */
28
+ refreshInterval?: number;
29
+ onRefreshIntervalChange?: (interval: number) => void;
30
+ refreshIntervals?: readonly TimeRangeOption[];
31
+ /** Runs when the button is pressed and on every interval while the page is shown.
32
+ * Leave it out for no refresh button. */
33
+ onRefresh?: () => void;
34
+ labels?: Partial<TimeRangePickerLabels>;
35
+ size?: ComponentSize;
36
+ };
37
+ /** The span a monitoring screen shows, back from now, with how often it refreshes and
38
+ * a button to refresh now. For a fixed span between two dates, put a DateRangePicker
39
+ * beside it. */
40
+ export declare const TimeRangePicker: ({ label, range, onRangeChange, ranges, refreshInterval, onRefreshIntervalChange, refreshIntervals: intervals, onRefresh, labels, size, className, ref, ...props }: TimeRangePickerProps & {
41
+ ref?: React.Ref<HTMLDivElement>;
42
+ }) => React.JSX.Element;