@surflane/ui 0.12.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 (126) hide show
  1. package/dist/alert-dialog.d.ts +25 -7
  2. package/dist/alert-dialog.js +9 -4
  3. package/dist/autocomplete.d.ts +2 -6
  4. package/dist/avatar.d.ts +7 -7
  5. package/dist/avatar.js +2 -2
  6. package/dist/bar-list.js +7 -1
  7. package/dist/button.d.ts +6 -11
  8. package/dist/button.js +8 -8
  9. package/dist/card.d.ts +15 -10
  10. package/dist/card.js +7 -2
  11. package/dist/chart.d.ts +6 -4
  12. package/dist/chart.js +83 -19
  13. package/dist/choice.d.ts +2 -2
  14. package/dist/class-names.d.ts +0 -4
  15. package/dist/class-names.js +0 -14
  16. package/dist/code.d.ts +19 -7
  17. package/dist/code.js +32 -38
  18. package/dist/combobox.d.ts +2 -8
  19. package/dist/combobox.js +2 -2
  20. package/dist/context-menu.d.ts +2 -8
  21. package/dist/context-menu.js +2 -2
  22. package/dist/css/rules/alert-dialog.css +4 -2
  23. package/dist/css/rules/app-shell.css +47 -10
  24. package/dist/css/rules/bar-list.css +2 -2
  25. package/dist/css/rules/bar.css +5 -12
  26. package/dist/css/rules/bottom-navigation.css +5 -2
  27. package/dist/css/rules/breadcrumbs.css +0 -6
  28. package/dist/css/rules/button.css +4 -12
  29. package/dist/css/rules/card.css +14 -0
  30. package/dist/css/rules/chart.css +13 -5
  31. package/dist/css/rules/choice.css +26 -2
  32. package/dist/css/rules/combobox.css +17 -16
  33. package/dist/css/rules/dashboard.css +53 -1
  34. package/dist/css/rules/date-picker.css +50 -27
  35. package/dist/css/rules/dialog.css +38 -9
  36. package/dist/css/rules/disclosure.css +17 -3
  37. package/dist/css/rules/field-control.css +9 -6
  38. package/dist/css/rules/file-upload.css +9 -5
  39. package/dist/css/rules/filter-bar.css +24 -1
  40. package/dist/css/rules/gauge.css +2 -2
  41. package/dist/css/rules/icon.css +0 -5
  42. package/dist/css/rules/input-group.css +23 -8
  43. package/dist/css/rules/link.css +0 -6
  44. package/dist/css/rules/menu.css +5 -2
  45. package/dist/css/rules/menubar.css +1 -1
  46. package/dist/css/rules/navigation-menu.css +31 -26
  47. package/dist/css/rules/number-field.css +8 -1
  48. package/dist/css/rules/otp-field.css +13 -3
  49. package/dist/css/rules/page-header.css +10 -0
  50. package/dist/css/rules/panel.css +51 -10
  51. package/dist/css/rules/popup.css +50 -0
  52. package/dist/css/rules/preview-card.css +0 -21
  53. package/dist/css/rules/scroll-area.css +20 -1
  54. package/dist/css/rules/select.css +4 -1
  55. package/dist/css/rules/sizing.css +15 -3
  56. package/dist/css/rules/skeleton.css +1 -1
  57. package/dist/css/rules/slider.css +16 -8
  58. package/dist/css/rules/spinner.css +1 -1
  59. package/dist/css/rules/stat.css +9 -0
  60. package/dist/css/rules/tabs.css +14 -6
  61. package/dist/css/rules/textarea.css +28 -0
  62. package/dist/css/rules/toast.css +3 -1
  63. package/dist/css/rules/toggle.css +10 -3
  64. package/dist/css/rules/toolbar.css +10 -3
  65. package/dist/css/rules/tree.css +29 -4
  66. package/dist/dashboard.d.ts +19 -7
  67. package/dist/dashboard.js +30 -5
  68. package/dist/date-picker.d.ts +32 -3
  69. package/dist/date-picker.js +43 -4
  70. package/dist/dialog.d.ts +25 -7
  71. package/dist/dialog.js +9 -4
  72. package/dist/drawer.d.ts +10 -2
  73. package/dist/drawer.js +6 -3
  74. package/dist/icon.d.ts +5 -5
  75. package/dist/icon.js +5 -3
  76. package/dist/index.d.ts +3 -3
  77. package/dist/index.js +2 -2
  78. package/dist/inline-message.d.ts +2 -2
  79. package/dist/inline-message.js +7 -2
  80. package/dist/input.d.ts +2 -2
  81. package/dist/layout.d.ts +1 -3
  82. package/dist/layout.js +2 -2
  83. package/dist/native-select.d.ts +2 -2
  84. package/dist/navigation-menu.d.ts +16 -0
  85. package/dist/navigation-menu.js +6 -1
  86. package/dist/page-header.d.ts +10 -2
  87. package/dist/page-header.js +3 -3
  88. package/dist/panel.d.ts +6 -3
  89. package/dist/panel.js +2 -2
  90. package/dist/parts.d.ts +7 -3
  91. package/dist/parts.js +12 -4
  92. package/dist/popover.d.ts +6 -6
  93. package/dist/popover.js +3 -2
  94. package/dist/preview-card.d.ts +2 -8
  95. package/dist/preview-card.js +2 -2
  96. package/dist/select.d.ts +7 -4
  97. package/dist/select.js +23 -8
  98. package/dist/sheet.d.ts +10 -2
  99. package/dist/sheet.js +6 -3
  100. package/dist/size.d.ts +8 -6
  101. package/dist/size.js +2 -7
  102. package/dist/sizing.d.ts +13 -6
  103. package/dist/sizing.js +13 -4
  104. package/dist/sparkline.d.ts +2 -2
  105. package/dist/sparkline.js +2 -2
  106. package/dist/spinner.d.ts +2 -2
  107. package/dist/spinner.js +2 -2
  108. package/dist/stat.d.ts +6 -2
  109. package/dist/stat.js +2 -2
  110. package/dist/table.d.ts +12 -4
  111. package/dist/table.js +8 -3
  112. package/dist/textarea.d.ts +6 -3
  113. package/dist/textarea.js +2 -2
  114. package/dist/time-range-picker.d.ts +2 -2
  115. package/dist/time-range-picker.js +2 -2
  116. package/dist/toggle.d.ts +2 -2
  117. package/dist/toggle.js +2 -2
  118. package/dist/toolbar.d.ts +11 -10
  119. package/dist/toolbar.js +23 -1
  120. package/dist/tooltip.d.ts +1 -3
  121. package/dist/tooltip.js +4 -2
  122. package/dist/tree.d.ts +1 -3
  123. package/dist/tree.js +2 -2
  124. package/dist/typography.d.ts +10 -3
  125. package/dist/typography.js +4 -3
  126. package/package.json +3 -3
@@ -14,7 +14,7 @@
14
14
  min-width: 0;
15
15
  max-width: 100%;
16
16
  padding: var(--surf-space-1);
17
- background: var(--surf-surface);
17
+ background: var(--surf-recessed);
18
18
  border-radius: var(--surf-radius-md);
19
19
  overflow: auto;
20
20
  scrollbar-width: thin;
@@ -38,15 +38,23 @@
38
38
  white-space: nowrap;
39
39
  outline: none;
40
40
  }
41
+ /* State layers paint over the tab's own fill, so hovering or pressing the
42
+ selected tab keeps it selected. */
41
43
  .surf-tabs-tab:hover:not(:disabled):not([data-disabled]) {
42
44
  color: var(--surf-ink);
43
- background: var(--surf-interaction-hover-bg);
45
+ background-image: linear-gradient(
46
+ var(--surf-interaction-hover-bg),
47
+ var(--surf-interaction-hover-bg)
48
+ );
44
49
  }
45
50
  .surf-tabs-tab:active:not(:disabled):not([data-disabled]) {
46
- background: var(--surf-interaction-active-bg);
51
+ background-image: linear-gradient(
52
+ var(--surf-interaction-active-bg),
53
+ var(--surf-interaction-active-bg)
54
+ );
47
55
  }
48
56
  .surf-tabs-tab[data-active] {
49
- background: var(--surf-canvas);
57
+ background-color: var(--surf-plane);
50
58
  color: var(--surf-ink);
51
59
  font-weight: var(--surf-weight-medium);
52
60
  }
@@ -77,12 +85,12 @@
77
85
  height: var(--active-tab-height);
78
86
  box-sizing: border-box;
79
87
  border: 0;
80
- background: var(--surf-canvas);
88
+ background: var(--surf-plane);
81
89
  border-radius: var(--surf-control-radius);
82
90
  pointer-events: none;
83
91
  }
84
92
  .surf-tabs-list:has(.surf-tabs-indicator) .surf-tabs-tab[data-active] {
85
- background: transparent;
93
+ background-color: transparent;
86
94
  border-color: transparent;
87
95
  }
88
96
  .surf-tabs[data-orientation='vertical'] {
@@ -13,6 +13,34 @@
13
13
  scrollbar-width: thin;
14
14
  scrollbar-color: var(--surf-scrollbar-thumb) var(--surf-scrollbar-track);
15
15
  }
16
+ /* Embedded: the pane it fills draws the boundary, so the editor keeps the pane's
17
+ plane and no edge of its own. An outside halo would fall on the pane's edge,
18
+ so focus is a ring inside it, and an invalid value an edge inside it. */
19
+ .surf-textarea[data-variant='embedded'] {
20
+ inline-size: 100%;
21
+ padding: var(--surf-space-4);
22
+ border: 0;
23
+ border-radius: 0;
24
+ background-color: transparent;
25
+ resize: none;
26
+ }
27
+ .surf-textarea[data-variant='embedded']:focus {
28
+ background-color: transparent;
29
+ box-shadow: inset 0 0 0 var(--surf-focus-ring-width)
30
+ var(--surf-control-focus);
31
+ }
32
+ .surf-textarea[data-variant='embedded'][aria-invalid='true'] {
33
+ box-shadow: inset 0 0 0 1px var(--surf-status-danger-fg);
34
+ }
35
+ .surf-textarea[data-variant='embedded'][aria-invalid='true']:focus {
36
+ box-shadow: inset 0 0 0 var(--surf-focus-ring-width)
37
+ var(--surf-status-danger-fg);
38
+ }
39
+ @media (forced-colors: active) {
40
+ .surf-textarea[data-variant='embedded']:focus {
41
+ outline-offset: var(--surf-focus-ring-inset);
42
+ }
43
+ }
16
44
  }
17
45
  @layer surflane.sizes {
18
46
  :where([data-ui-size], [data-ui-size] *).surf-textarea {
@@ -32,7 +32,9 @@
32
32
  box-sizing: border-box;
33
33
  width: 100%;
34
34
  padding: var(--surf-space-4);
35
- border: 0;
35
+ /* No backdrop sets a toast apart from the same-tone page, and its shadow fades
36
+ in the dark theme: it keeps the floating edge. */
37
+ border: 1px solid var(--surf-menu-border);
36
38
  border-radius: var(--surf-radius-lg);
37
39
  background: var(--surf-menu-background);
38
40
  color: var(--surf-ink);
@@ -6,10 +6,10 @@
6
6
  /* Command state uses the whole rounded surface, with no one-sided accent. */
7
7
  .surf-toggle {
8
8
  background: transparent;
9
- border-color: var(--surf-line);
9
+ border-color: var(--surf-control-border);
10
10
  }
11
11
  .surf-toggle[data-pressed] {
12
- background: var(--surf-selected);
12
+ background-color: var(--surf-selected);
13
13
  border-color: var(--surf-control-border);
14
14
  color: var(--surf-ink);
15
15
  }
@@ -17,11 +17,18 @@
17
17
  opacity: var(--surf-control-disabled-opacity);
18
18
  cursor: not-allowed;
19
19
  }
20
+ @media (forced-colors: active) {
21
+ .surf-toggle[data-pressed] {
22
+ background-color: Highlight;
23
+ color: HighlightText;
24
+ forced-color-adjust: none;
25
+ }
26
+ }
20
27
  }
21
28
  @layer surflane.sizes {
22
29
  :where([data-ui-size], [data-ui-size] *):is(.surf-toggle) {
23
30
  min-block-size: var(--surf-ui-row);
24
31
  font-size: var(--surf-ui-font);
25
- line-height: var(--surf-ui-leading);
32
+ line-height: var(--surf-ui-label-leading);
26
33
  }
27
34
  }
@@ -4,10 +4,17 @@
4
4
 
5
5
  @layer surflane.components {
6
6
  .surf-toolbar-button[aria-pressed='true'] {
7
- background: var(--surf-selected);
7
+ background-color: var(--surf-selected);
8
8
  border-color: var(--surf-control-border);
9
9
  color: var(--surf-ink);
10
10
  }
11
+ @media (forced-colors: active) {
12
+ .surf-toolbar-button[aria-pressed='true'] {
13
+ background-color: Highlight;
14
+ color: HighlightText;
15
+ forced-color-adjust: none;
16
+ }
17
+ }
11
18
  .surf-toolbar-button[data-disabled],
12
19
  .surf-toolbar-input[data-disabled] {
13
20
  opacity: var(--surf-control-disabled-opacity);
@@ -16,7 +23,7 @@
16
23
  .surf-toolbar {
17
24
  padding: var(--surf-space-1);
18
25
  border-radius: var(--surf-radius-md);
19
- background: var(--surf-surface);
26
+ background: var(--surf-recessed);
20
27
  gap: var(--surf-space-2);
21
28
  }
22
29
  .surf-toolbar-group {
@@ -38,7 +45,7 @@
38
45
  max-width: 100%;
39
46
  border: 0;
40
47
  border-radius: max(0px, calc(var(--surf-radius-md) - var(--surf-space-1)));
41
- background: var(--surf-canvas);
48
+ background: var(--surf-plane);
42
49
  }
43
50
  .surf-toolbar-separator {
44
51
  flex: none;
@@ -25,10 +25,13 @@
25
25
  transition: background-color var(--surf-duration-fast) var(--surf-ease-out);
26
26
  }
27
27
  .surf-tree-item:hover {
28
- background: var(--surf-interaction-hover-bg);
28
+ background-image: linear-gradient(
29
+ var(--surf-interaction-hover-bg),
30
+ var(--surf-interaction-hover-bg)
31
+ );
29
32
  }
30
33
  .surf-tree-item[aria-selected='true'] {
31
- background: var(--surf-selected);
34
+ background-color: var(--surf-selected);
32
35
  }
33
36
  .surf-tree-item:focus-visible {
34
37
  outline: var(--surf-focus-ring-width) solid var(--surf-interaction-focus);
@@ -52,10 +55,16 @@
52
55
  color: inherit;
53
56
  }
54
57
  .surf-tree-toggle:hover:not(:disabled) {
55
- background: var(--surf-interaction-hover-bg);
58
+ background-image: linear-gradient(
59
+ var(--surf-interaction-hover-bg),
60
+ var(--surf-interaction-hover-bg)
61
+ );
56
62
  }
57
63
  .surf-tree-toggle:active:not(:disabled) {
58
- background: var(--surf-interaction-active-bg);
64
+ background-image: linear-gradient(
65
+ var(--surf-interaction-active-bg),
66
+ var(--surf-interaction-active-bg)
67
+ );
59
68
  }
60
69
  .surf-tree-toggle svg[data-expanded] {
61
70
  rotate: 90deg;
@@ -73,6 +82,21 @@
73
82
  padding: var(--surf-space-4);
74
83
  color: var(--surf-ink-muted);
75
84
  }
85
+ /* On touch the disclosure keeps its size and reaches the row's
86
+ --surf-control-target-size around it. */
87
+ @media (max-width: 760px), (pointer: coarse) {
88
+ .surf-tree-toggle {
89
+ position: relative;
90
+ }
91
+ .surf-tree-toggle::after {
92
+ content: '';
93
+ position: absolute;
94
+ inset: 50% auto auto 50%;
95
+ inline-size: var(--surf-control-target-size);
96
+ block-size: var(--surf-control-target-size);
97
+ translate: -50% -50%;
98
+ }
99
+ }
76
100
  @media (prefers-reduced-motion: reduce) {
77
101
  .surf-tree-item {
78
102
  transition: none;
@@ -82,6 +106,7 @@
82
106
  .surf-tree-item[aria-selected='true'] {
83
107
  background: Highlight;
84
108
  color: HighlightText;
109
+ forced-color-adjust: none;
85
110
  }
86
111
  .surf-tree-item[aria-disabled='true'] {
87
112
  color: GrayText;
@@ -1,11 +1,19 @@
1
1
  import * as React from 'react';
2
- export type DashboardRootProps = React.ComponentPropsWithoutRef<'div'>;
2
+ export type DashboardRootProps = React.ComponentPropsWithoutRef<'div'> & {
3
+ /** Names the dashboard as a region of the page, such as "Cluster overview". */
4
+ label?: string;
5
+ /** The most columns a wide screen shows, so a row planned for that many keeps its
6
+ * proportions and a span keeps its meaning. Narrower screens still fit fewer. */
7
+ columns?: number;
8
+ };
3
9
  export type DashboardPanelLabels = {
4
10
  /** The button that shows the description, given the title. */
5
11
  about: (title: string) => string;
6
12
  loading: string;
7
13
  refreshing: string;
8
14
  error: string;
15
+ /** Above the last values when a refresh failed. */
16
+ stale: string;
9
17
  retry: string;
10
18
  empty: string;
11
19
  };
@@ -16,8 +24,12 @@ export type DashboardPanelProps = Omit<React.ComponentPropsWithoutRef<'div'>, 't
16
24
  /** The heading level of the title. */
17
25
  level?: 2 | 3 | 4 | 5 | 6;
18
26
  /** loading shows a placeholder until the first data arrives; error and empty
19
- * replace the content with a message. */
20
- state?: 'ready' | 'loading' | 'error' | 'empty';
27
+ * replace the content with a message; stale keeps the last content under a caution
28
+ * that a refresh failed, with retry. */
29
+ state?: 'ready' | 'loading' | 'error' | 'empty' | 'stale';
30
+ /** The loading placeholder's height, as tall as the content will be, so the
31
+ * panels below don't move when the data arrives. 96px without it. */
32
+ placeholderHeight?: number | string;
21
33
  /** New data is on its way: the content stays, a spinner shows by the title and the
22
34
  * panel is busy for assistive technology. */
23
35
  refreshing?: boolean;
@@ -32,15 +44,15 @@ export type DashboardPanelProps = Omit<React.ComponentPropsWithoutRef<'div'>, 't
32
44
  labels?: Partial<DashboardPanelLabels>;
33
45
  };
34
46
  export declare const Dashboard: {
35
- Root: ({ className, ref, ...props }: DashboardRootProps & {
47
+ Root: ({ label, columns, className, style, ref, ...props }: DashboardRootProps & {
36
48
  ref?: React.Ref<HTMLDivElement>;
37
49
  }) => React.JSX.Element;
38
- Panel: ({ title, description, level, state, refreshing, onRetry, message, actions, span, labels, className, children, ref, ...props }: DashboardPanelProps & {
50
+ Panel: ({ title, description, level, state, refreshing, onRetry, message, actions, span, placeholderHeight, labels, className, children, ref, ...props }: DashboardPanelProps & {
39
51
  ref?: React.Ref<HTMLDivElement>;
40
52
  }) => React.JSX.Element;
41
53
  };
42
- export declare const DashboardRoot: ({ className, ref, ...props }: DashboardRootProps & {
54
+ export declare const DashboardRoot: ({ label, columns, className, style, ref, ...props }: DashboardRootProps & {
43
55
  ref?: React.Ref<HTMLDivElement>;
44
- }) => React.JSX.Element, DashboardPanel: ({ title, description, level, state, refreshing, onRetry, message, actions, span, labels, className, children, ref, ...props }: DashboardPanelProps & {
56
+ }) => React.JSX.Element, DashboardPanel: ({ title, description, level, state, refreshing, onRetry, message, actions, span, placeholderHeight, labels, className, children, ref, ...props }: DashboardPanelProps & {
45
57
  ref?: React.Ref<HTMLDivElement>;
46
58
  }) => React.JSX.Element;
package/dist/dashboard.js CHANGED
@@ -10,25 +10,50 @@ import { Skeleton } from './skeleton.js';
10
10
  import { Spinner } from './spinner.js';
11
11
  import { cx } from './class-names.js';
12
12
  /** The panels of a dashboard on a responsive grid: as many columns as fit panels of
13
- * --surf-dashboard-panel-min-width (20rem), one column on a phone. */
14
- const Root = function DashboardRoot({ className, ref, ...props }) {
15
- return (_jsx("div", { ...props, ref: ref, className: cx('surf-dashboard', className) }));
13
+ * --surf-dashboard-panel-min-width (20rem), up to columns, one column on a phone. */
14
+ const Root = function DashboardRoot({ label, columns, className, style, ref, ...props }) {
15
+ return (_jsx("div", { role: label ? 'region' : undefined, "aria-label": label, ...props, ref: ref, "data-columns": columns, style: columns
16
+ ? {
17
+ ...style,
18
+ '--surf-dashboard-columns': columns,
19
+ }
20
+ : style, className: cx('surf-dashboard', className) }));
16
21
  };
17
22
  const panelLabels = {
18
23
  about: (title) => `About ${title}`,
19
24
  loading: 'Loading',
20
25
  refreshing: 'Refreshing',
21
26
  error: 'This panel could not load.',
27
+ stale: 'These are the last values: the panel could not refresh.',
22
28
  retry: 'Retry',
23
29
  empty: 'No data in this range.',
24
30
  };
25
31
  /** One panel of a dashboard: a card with its title, an optional description, the
26
32
  * states of its data and the content. The content takes the room the row gives it. */
27
- const Panel = function DashboardPanel({ title, description, level = 3, state = 'ready', refreshing = false, onRetry, message, actions, span, labels, className, children, ref, ...props }) {
33
+ const Panel = function DashboardPanel({ title, description, level = 3, state = 'ready', refreshing = false, onRetry, message, actions, span, placeholderHeight, labels, className, children, ref, ...props }) {
28
34
  const text = { ...panelLabels, ...labels };
29
35
  const id = React.useId();
30
36
  const busy = state === 'loading' || refreshing;
31
- return (_jsxs(Card.Root, { role: 'group', "aria-labelledby": `${id}-title`, "aria-busy": busy || undefined, ...props, ref: ref, "data-span": span, "data-state": state, className: cx('surf-dashboard-panel', className), children: [_jsxs("div", { className: 'surf-dashboard-panel-header', children: [_jsx(Card.Title, { level: level, id: `${id}-title`, children: title }), description && (_jsxs(Popover.Root, { children: [_jsx(Popover.Trigger, { render: _jsx(Button, { variant: 'ghost', size: 'icon-small', "aria-label": text.about(title) }), children: _jsx(Icon, { name: 'info' }) }), _jsx(Popover.Content, { size: 'small', children: _jsx(Popover.Description, { children: description }) })] })), refreshing && state !== 'loading' && (_jsx(Spinner, { size: 'small', label: text.refreshing })), actions && (_jsx("div", { className: 'surf-dashboard-panel-actions', children: actions }))] }), _jsx("div", { className: 'surf-dashboard-panel-body', children: state === 'loading' ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { shape: 'block' }), _jsx("span", { className: 'surf-feedback-sr', children: text.loading })] })) : state === 'error' ? (_jsxs(InlineMessage.Root, { tone: 'danger', children: [_jsx(InlineMessage.Icon, { tone: 'danger' }), _jsxs(InlineMessage.Content, { children: [_jsx(InlineMessage.Description, { children: message ?? text.error }), onRetry && (_jsx(InlineMessage.Actions, { children: _jsx(Button, { variant: 'outline', size: 'small', onClick: onRetry, children: text.retry }) }))] })] })) : state === 'empty' ? (_jsx("p", { className: 'surf-dashboard-panel-empty', children: message ?? text.empty })) : (children) })] }));
37
+ const titleRef = React.useRef(null);
38
+ const retried = React.useRef(false);
39
+ // Retry takes its own button away when the panel leaves the error, so focus
40
+ // moves to the panel's title instead of falling to the page.
41
+ React.useEffect(() => {
42
+ if (!retried.current)
43
+ return;
44
+ retried.current = false;
45
+ const active = document.activeElement;
46
+ if (!active || active === document.body)
47
+ titleRef.current?.focus();
48
+ }, [state]);
49
+ const retry = onRetry &&
50
+ (() => {
51
+ retried.current = true;
52
+ onRetry();
53
+ });
54
+ return (_jsxs(Card.Root, { role: 'group', "aria-labelledby": `${id}-title`, "aria-busy": busy || undefined, ...props, ref: ref, "data-span": span, "data-state": state, className: cx('surf-dashboard-panel', className), children: [_jsxs("div", { className: 'surf-dashboard-panel-header', children: [_jsx(Card.Title, { level: level, id: `${id}-title`, title: title, ref: titleRef, tabIndex: -1, children: title }), description && (_jsxs(Popover.Root, { children: [_jsx(Popover.Trigger, { render: _jsx(Button, { variant: 'ghost', size: 'icon-small', "aria-label": text.about(title) }), children: _jsx(Icon, { name: 'info' }) }), _jsx(Popover.Content, { size: 'small', children: _jsx(Popover.Description, { children: description }) })] })), refreshing && state !== 'loading' && (_jsx(Spinner, { size: 'small', label: text.refreshing })), actions && (_jsx("div", { className: 'surf-dashboard-panel-actions', children: actions }))] }), _jsx("div", { className: 'surf-dashboard-panel-body', children: state === 'loading' ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { shape: 'block', style: placeholderHeight === undefined
55
+ ? undefined
56
+ : { blockSize: placeholderHeight } }), _jsx("span", { className: 'surf-feedback-sr', children: text.loading })] })) : state === 'stale' ? (_jsxs(_Fragment, { children: [_jsxs(InlineMessage.Root, { tone: 'caution', children: [_jsx(InlineMessage.Icon, {}), _jsxs(InlineMessage.Content, { children: [_jsx(InlineMessage.Description, { children: message ?? text.stale }), onRetry && (_jsx(InlineMessage.Actions, { children: _jsx(Button, { variant: 'outline', size: 'small', onClick: retry, children: text.retry }) }))] })] }), children] })) : state === 'error' ? (_jsxs(InlineMessage.Root, { tone: 'danger', children: [_jsx(InlineMessage.Icon, {}), _jsxs(InlineMessage.Content, { children: [_jsx(InlineMessage.Description, { children: message ?? text.error }), onRetry && (_jsx(InlineMessage.Actions, { children: _jsx(Button, { variant: 'outline', size: 'small', onClick: retry, children: text.retry }) }))] })] })) : state === 'empty' ? (_jsx("p", { className: 'surf-dashboard-panel-empty', children: message ?? text.empty })) : (children) })] }));
32
57
  };
33
58
  export const Dashboard = { Root, Panel };
34
59
  // Flat names for React Server Components, which cannot dot into client exports.
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { type ComponentSize } from './sizing.js';
2
3
  import { type DatePickerProps as AriaDatePickerProps, type DateValue, type ValidationResult } from 'react-aria-components/DatePicker';
3
4
  import { type DateRangePickerProps as AriaDateRangePickerProps } from 'react-aria-components/DateRangePicker';
4
5
  import { type TimeFieldProps as AriaTimeFieldProps, type TimeValue } from 'react-aria-components/TimeField';
@@ -13,9 +14,37 @@ type FieldContent = {
13
14
  type CalendarContent = {
14
15
  calendarLabel?: string;
15
16
  };
16
- export type DatePickerProps<T extends DateValue> = Omit<AriaDatePickerProps<T>, 'children'> & FieldContent & CalendarContent;
17
- export type DateRangePickerProps<T extends DateValue> = Omit<AriaDateRangePickerProps<T>, 'children'> & FieldContent & CalendarContent;
18
- export type TimeFieldProps<T extends TimeValue> = Omit<AriaTimeFieldProps<T>, 'children'> & FieldContent;
17
+ /** The names every other Surflane field takes. react-aria's spellings keep working. */
18
+ type FieldStates<Change> = {
19
+ /** The field's and its calendar's size; without it, the surrounding SizeProvider's. */
20
+ size?: ComponentSize;
21
+ disabled?: boolean;
22
+ readOnly?: boolean;
23
+ required?: boolean;
24
+ /** Marks the value invalid, as aria-invalid does on other fields. */
25
+ invalid?: boolean;
26
+ onValueChange?: Change;
27
+ /** @deprecated Use `disabled`. Removed in a later minor. */
28
+ isDisabled?: boolean;
29
+ /** @deprecated Use `readOnly`. Removed in a later minor. */
30
+ isReadOnly?: boolean;
31
+ /** @deprecated Use `required`. Removed in a later minor. */
32
+ isRequired?: boolean;
33
+ /** @deprecated Use `invalid`. Removed in a later minor. */
34
+ isInvalid?: boolean;
35
+ /** @deprecated Use `onValueChange`. Removed in a later minor. */
36
+ onChange?: Change;
37
+ };
38
+ type PopupStates = {
39
+ /** Whether the calendar is open, controlled. */
40
+ open?: boolean;
41
+ /** @deprecated Use `open`. Removed in a later minor. */
42
+ isOpen?: boolean;
43
+ };
44
+ type AriaNames = 'children' | 'isDisabled' | 'isReadOnly' | 'isRequired' | 'isInvalid' | 'isOpen' | 'onChange';
45
+ export type DatePickerProps<T extends DateValue> = Omit<AriaDatePickerProps<T>, AriaNames> & FieldContent & CalendarContent & FieldStates<AriaDatePickerProps<T>['onChange']> & PopupStates;
46
+ export type DateRangePickerProps<T extends DateValue> = Omit<AriaDateRangePickerProps<T>, AriaNames> & FieldContent & CalendarContent & FieldStates<AriaDateRangePickerProps<T>['onChange']> & PopupStates;
47
+ export type TimeFieldProps<T extends TimeValue> = Omit<AriaTimeFieldProps<T>, AriaNames> & FieldContent & FieldStates<AriaTimeFieldProps<T>['onChange']>;
19
48
  /** Locale-aware segmented entry with a keyboard and touch calendar. DateValue
20
49
  * retains calendar/time-zone semantics; conversion to an instant is caller-owned. */
21
50
  export declare function DatePicker<T extends DateValue>({ label, description, errorMessage, calendarLabel, className, ...props }: DatePickerProps<T>): React.JSX.Element;
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Icon } from './icon.js';
4
- import { useComponentSize } from './sizing.js';
4
+ import { ComponentSizeContext, useComponentSize, } from './sizing.js';
5
5
  import { DatePicker as AriaDatePicker, DateInput, DateSegment, Group, Label, Text, FieldError, Calendar, CalendarGrid, CalendarGridHeader, CalendarGridBody, CalendarHeaderCell, CalendarCell, Button, Popover, } from 'react-aria-components/DatePicker';
6
6
  import { DateRangePicker as AriaDateRangePicker, RangeCalendar, } from 'react-aria-components/DateRangePicker';
7
7
  import { TimeField as AriaTimeField, } from 'react-aria-components/TimeField';
@@ -10,6 +10,45 @@ import { Dialog } from 'react-aria-components/Dialog';
10
10
  import { skin } from './parts.js';
11
11
  import { useLocale } from 'react-aria-components/I18nProvider';
12
12
  export { I18nProvider as DateLocaleProvider } from 'react-aria-components/I18nProvider';
13
+ /** Surflane's field names to react-aria's, the new spelling first. */
14
+ function ariaStates({ size: _size, disabled, readOnly, required, invalid, onValueChange, isDisabled, isReadOnly, isRequired, isInvalid, onChange, open, isOpen, }) {
15
+ const opened = open ?? isOpen;
16
+ return {
17
+ isDisabled: disabled ?? isDisabled,
18
+ isReadOnly: readOnly ?? isReadOnly,
19
+ isRequired: required ?? isRequired,
20
+ isInvalid: invalid ?? isInvalid,
21
+ onChange: onValueChange ?? onChange,
22
+ ...(opened === undefined ? {} : { isOpen: opened }),
23
+ };
24
+ }
25
+ const stateNames = [
26
+ 'size',
27
+ 'disabled',
28
+ 'readOnly',
29
+ 'required',
30
+ 'invalid',
31
+ 'onValueChange',
32
+ 'isDisabled',
33
+ 'isReadOnly',
34
+ 'isRequired',
35
+ 'isInvalid',
36
+ 'onChange',
37
+ 'open',
38
+ 'isOpen',
39
+ ];
40
+ /** The props a react-aria field passes on, without the names ariaStates maps. */
41
+ function rest(props) {
42
+ const copy = { ...props };
43
+ for (const name of stateNames)
44
+ delete copy[name];
45
+ return copy;
46
+ }
47
+ /** An explicit size scopes the field and the calendar it opens. */
48
+ function SizedField({ size, children, }) {
49
+ const scheme = useComponentSize(size);
50
+ return (_jsx(ComponentSizeContext.Provider, { value: scheme, children: children(size ? scheme : undefined) }));
51
+ }
13
52
  function Segments({ slot }) {
14
53
  return (_jsx(DateInput, { slot: slot, className: 'surf-date-input', children: (segment) => (_jsx(DateSegment, { segment: segment, className: 'surf-date-segment' })) }));
15
54
  }
@@ -50,11 +89,11 @@ function CalendarPopup({ label, range }) {
50
89
  /** Locale-aware segmented entry with a keyboard and touch calendar. DateValue
51
90
  * retains calendar/time-zone semantics; conversion to an instant is caller-owned. */
52
91
  export function DatePicker({ label, description, errorMessage, calendarLabel, className, ...props }) {
53
- return (_jsxs(AriaDatePicker, { ...props, className: skin('surf-date-picker', className), children: [_jsx(Label, { className: 'surf-field-label', children: label }), _jsxs(Group, { className: 'surf-date-group', children: [_jsx(Segments, {}), _jsx(CalendarButton, {})] }), _jsx(FieldHelp, { description: description, errorMessage: errorMessage }), _jsx(CalendarPopup, { label: calendarLabel ?? label })] }));
92
+ return (_jsx(SizedField, { size: props.size, children: (scheme) => (_jsxs(AriaDatePicker, { ...rest(props), ...ariaStates(props), "data-ui-size": scheme, className: skin('surf-date-picker', className), children: [_jsx(Label, { className: 'surf-field-label', children: label }), _jsxs(Group, { className: 'surf-date-group', children: [_jsx(Segments, {}), _jsx(CalendarButton, {})] }), _jsx(FieldHelp, { description: description, errorMessage: errorMessage }), _jsx(CalendarPopup, { label: calendarLabel ?? label })] })) }));
54
93
  }
55
94
  export function DateRangePicker({ label, description, errorMessage, calendarLabel, className, ...props }) {
56
- return (_jsxs(AriaDateRangePicker, { ...props, className: skin('surf-date-picker surf-date-range-picker', className), children: [_jsx(Label, { className: 'surf-field-label', children: label }), _jsxs(Group, { className: 'surf-date-group', children: [_jsxs("div", { className: 'surf-date-range-inputs', children: [_jsx(Segments, { slot: 'start' }), _jsx("span", { "aria-hidden": 'true', className: 'surf-date-range-separator', children: "\u2013" }), _jsx(Segments, { slot: 'end' })] }), _jsx(CalendarButton, {})] }), _jsx(FieldHelp, { description: description, errorMessage: errorMessage }), _jsx(CalendarPopup, { label: calendarLabel ?? label, range: true })] }));
95
+ return (_jsx(SizedField, { size: props.size, children: (scheme) => (_jsxs(AriaDateRangePicker, { ...rest(props), ...ariaStates(props), "data-ui-size": scheme, className: skin('surf-date-picker surf-date-range-picker', className), children: [_jsx(Label, { className: 'surf-field-label', children: label }), _jsxs(Group, { className: 'surf-date-group', children: [_jsxs("div", { className: 'surf-date-range-inputs', children: [_jsx(Segments, { slot: 'start' }), _jsx("span", { "aria-hidden": 'true', className: 'surf-date-range-separator', children: "\u2013" }), _jsx(Segments, { slot: 'end' })] }), _jsx(CalendarButton, {})] }), _jsx(FieldHelp, { description: description, errorMessage: errorMessage }), _jsx(CalendarPopup, { label: calendarLabel ?? label, range: true })] })) }));
57
96
  }
58
97
  export function TimeField({ label, description, errorMessage, className, ...props }) {
59
- return (_jsxs(AriaTimeField, { ...props, className: skin('surf-time-field', className), children: [_jsx(Label, { className: 'surf-field-label', children: label }), _jsx("div", { className: 'surf-date-group', children: _jsx(Segments, {}) }), _jsx(FieldHelp, { description: description, errorMessage: errorMessage })] }));
98
+ return (_jsx(SizedField, { size: props.size, children: (scheme) => (_jsxs(AriaTimeField, { ...rest(props), ...ariaStates(props), "data-ui-size": scheme, className: skin('surf-time-field', className), children: [_jsx(Label, { className: 'surf-field-label', children: label }), _jsx("div", { className: 'surf-date-group', children: _jsx(Segments, {}) }), _jsx(FieldHelp, { description: description, errorMessage: errorMessage })] })) }));
60
99
  }
package/dist/dialog.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type ComponentSizeAlias } from './sizing.js';
2
+ import { type ComponentSize } from './sizing.js';
3
3
  import { Dialog as BaseDialog } from '@base-ui/react/dialog';
4
4
  /** Visible icon close action; inherits Dialog focus and dismissal behavior. */
5
5
  declare const DialogCloseButton: ({ className, "aria-label": label, ref, ...props }: Omit<React.ComponentPropsWithoutRef<typeof BaseDialog.Close>, "children"> & {
@@ -7,7 +7,7 @@ declare const DialogCloseButton: ({ className, "aria-label": label, ref, ...prop
7
7
  }) => React.JSX.Element;
8
8
  declare const DialogContent: ({ size, className, children, closeLabel, closeDisabled, focusBoundaryLabel, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDialog.Popup> & {
9
9
  /** Width and density: small 420px, default 640px, large 840px. */
10
- size?: ComponentSizeAlias;
10
+ size?: ComponentSize;
11
11
  closeLabel?: string;
12
12
  closeDisabled?: boolean;
13
13
  focusBoundaryLabel?: string;
@@ -19,10 +19,20 @@ declare const DialogBody: ({ className, ref, ...props }: React.ComponentProps<"d
19
19
  ref?: React.Ref<HTMLDivElement>;
20
20
  }) => React.JSX.Element;
21
21
  declare function DialogFooter({ className, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
22
+ declare const DialogTitle: ({ ref, ...props }: Omit<Omit<Omit<import("@base-ui/react/dialog").DialogTitleProps, "ref"> & React.RefAttributes<HTMLHeadingElement>, "ref">, "size"> & {
23
+ size?: ComponentSize;
24
+ } & {
25
+ ref?: React.Ref<HTMLHeadingElement> | undefined;
26
+ }) => React.JSX.Element;
27
+ declare const DialogDescription: ({ ref, ...props }: Omit<Omit<Omit<import("@base-ui/react/dialog").DialogDescriptionProps, "ref"> & React.RefAttributes<HTMLParagraphElement>, "ref">, "size"> & {
28
+ size?: ComponentSize;
29
+ } & {
30
+ ref?: React.Ref<HTMLParagraphElement> | undefined;
31
+ }) => React.JSX.Element;
22
32
  export declare const Dialog: {
23
33
  Content: ({ size, className, children, closeLabel, closeDisabled, focusBoundaryLabel, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDialog.Popup> & {
24
34
  /** Width and density: small 420px, default 640px, large 840px. */
25
- size?: ComponentSizeAlias;
35
+ size?: ComponentSize;
26
36
  closeLabel?: string;
27
37
  closeDisabled?: boolean;
28
38
  focusBoundaryLabel?: string;
@@ -37,17 +47,25 @@ export declare const Dialog: {
37
47
  CloseButton: ({ className, "aria-label": label, ref, ...props }: Omit<React.ComponentPropsWithoutRef<typeof BaseDialog.Close>, "children"> & {
38
48
  ref?: React.Ref<HTMLButtonElement>;
39
49
  }) => React.JSX.Element;
50
+ Title: ({ ref, ...props }: Omit<Omit<Omit<import("@base-ui/react/dialog").DialogTitleProps, "ref"> & React.RefAttributes<HTMLHeadingElement>, "ref">, "size"> & {
51
+ size?: ComponentSize;
52
+ } & {
53
+ ref?: React.Ref<HTMLHeadingElement> | undefined;
54
+ }) => React.JSX.Element;
55
+ Description: ({ ref, ...props }: Omit<Omit<Omit<import("@base-ui/react/dialog").DialogDescriptionProps, "ref"> & React.RefAttributes<HTMLParagraphElement>, "ref">, "size"> & {
56
+ size?: ComponentSize;
57
+ } & {
58
+ ref?: React.Ref<HTMLParagraphElement> | undefined;
59
+ }) => React.JSX.Element;
40
60
  Backdrop: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogBackdropProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
41
61
  Close: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogCloseProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
42
- Description: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogDescriptionProps, "ref"> & React.RefAttributes<HTMLParagraphElement>>;
43
62
  Popup: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogPopupProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
44
63
  Portal: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogPortalProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
45
64
  Root: <Payload>(props: BaseDialog.Root.Props<Payload>) => React.JSX.Element;
46
65
  Viewport: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogViewportProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
47
- Title: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogTitleProps, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
48
66
  Trigger: BaseDialog.Trigger;
49
67
  createHandle: typeof BaseDialog.createHandle;
50
68
  Handle: typeof BaseDialog.Handle;
51
69
  };
52
- export { DialogContent, DialogHeader, DialogBody, DialogFooter, DialogCloseButton, };
53
- export declare const DialogBackdrop: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogBackdropProps, "ref"> & React.RefAttributes<HTMLDivElement>>, DialogClose: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogCloseProps, "ref"> & React.RefAttributes<HTMLButtonElement>>, DialogDescription: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogDescriptionProps, "ref"> & React.RefAttributes<HTMLParagraphElement>>, DialogHandle: typeof BaseDialog.Handle, DialogPopup: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogPopupProps, "ref"> & React.RefAttributes<HTMLDivElement>>, DialogPortal: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogPortalProps, "ref"> & React.RefAttributes<HTMLDivElement>>, DialogRoot: <Payload>(props: BaseDialog.Root.Props<Payload>) => React.JSX.Element, DialogTitle: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogTitleProps, "ref"> & React.RefAttributes<HTMLHeadingElement>>, DialogTrigger: BaseDialog.Trigger, DialogViewport: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogViewportProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
70
+ export { DialogContent, DialogHeader, DialogBody, DialogFooter, DialogCloseButton, DialogTitle, DialogDescription, };
71
+ export declare const DialogBackdrop: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogBackdropProps, "ref"> & React.RefAttributes<HTMLDivElement>>, DialogClose: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogCloseProps, "ref"> & React.RefAttributes<HTMLButtonElement>>, DialogHandle: typeof BaseDialog.Handle, DialogPopup: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogPopupProps, "ref"> & React.RefAttributes<HTMLDivElement>>, DialogPortal: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogPortalProps, "ref"> & React.RefAttributes<HTMLDivElement>>, DialogRoot: <Payload>(props: BaseDialog.Root.Props<Payload>) => React.JSX.Element, DialogTrigger: BaseDialog.Trigger, DialogViewport: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/dialog").DialogViewportProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
package/dist/dialog.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Icon } from './icon.js';
4
- import { sizeName, skin, cx } from './parts.js';
4
+ import { part, skin, cx, usePortalDirection } from './parts.js';
5
5
  import { useDialogFocusBoundary } from './dialog-focus.js';
6
6
  import { useComponentSize } from './sizing.js';
7
7
  import { Dialog as BaseDialog } from '@base-ui/react/dialog';
@@ -12,7 +12,8 @@ const DialogCloseButton = function DialogCloseButton({ className, 'aria-label':
12
12
  const DialogContent = function DialogContent({ size, className, children, closeLabel = 'Close dialog', closeDisabled, focusBoundaryLabel = 'Continue dialog navigation', ref, ...props }) {
13
13
  const scheme = useComponentSize(size);
14
14
  const portalRef = useDialogFocusBoundary(focusBoundaryLabel);
15
- return (_jsxs(BaseDialog.Portal, { ref: portalRef, children: [_jsx(BaseDialog.Backdrop, { className: 'surf-dialog-backdrop' }), _jsxs(BaseDialog.Popup, { "data-ui-size": scheme, "data-size": sizeName(size), ...props, ref: ref, className: skin('surf-dialog-popup surf-dialog-content', className), children: [_jsx(DialogCloseButton, { "aria-label": closeLabel, disabled: closeDisabled }), children] })] }));
15
+ const dir = usePortalDirection();
16
+ return (_jsxs(BaseDialog.Portal, { ref: portalRef, children: [_jsx(BaseDialog.Backdrop, { className: 'surf-dialog-backdrop' }), _jsxs(BaseDialog.Popup, { dir: dir, "data-ui-size": scheme, "data-size": size, ...props, ref: ref, className: skin('surf-dialog-popup surf-dialog-content', className), children: [_jsx(DialogCloseButton, { "aria-label": closeLabel, disabled: closeDisabled }), children] })] }));
16
17
  };
17
18
  function DialogHeader({ className, ...props }) {
18
19
  return _jsx("div", { ...props, className: cx('surf-dialog-header', className) });
@@ -23,6 +24,8 @@ const DialogBody = function DialogBody({ className, ref, ...props }) {
23
24
  function DialogFooter({ className, ...props }) {
24
25
  return _jsx("div", { ...props, className: cx('surf-dialog-footer', className) });
25
26
  }
27
+ const DialogTitle = /* @__PURE__ */ part(BaseDialog.Title, 'surf-dialog-title');
28
+ const DialogDescription = /* @__PURE__ */ part(BaseDialog.Description, 'surf-dialog-description');
26
29
  export const Dialog = {
27
30
  ...BaseDialog,
28
31
  Content: DialogContent,
@@ -30,7 +33,9 @@ export const Dialog = {
30
33
  Body: DialogBody,
31
34
  Footer: DialogFooter,
32
35
  CloseButton: DialogCloseButton,
36
+ Title: DialogTitle,
37
+ Description: DialogDescription,
33
38
  };
34
39
  // Flat names for React Server Components, which cannot dot into client exports.
35
- export { DialogContent, DialogHeader, DialogBody, DialogFooter, DialogCloseButton, };
36
- export const { Backdrop: DialogBackdrop, Close: DialogClose, Description: DialogDescription, Handle: DialogHandle, Popup: DialogPopup, Portal: DialogPortal, Root: DialogRoot, Title: DialogTitle, Trigger: DialogTrigger, Viewport: DialogViewport, } = Dialog;
40
+ export { DialogContent, DialogHeader, DialogBody, DialogFooter, DialogCloseButton, DialogTitle, DialogDescription, };
41
+ export const { Backdrop: DialogBackdrop, Close: DialogClose, Handle: DialogHandle, Popup: DialogPopup, Portal: DialogPortal, Root: DialogRoot, Trigger: DialogTrigger, Viewport: DialogViewport, } = Dialog;
package/dist/drawer.d.ts CHANGED
@@ -4,14 +4,22 @@ import { Footer, Header, type PanelOptions, type PanelSide } from './panel.js';
4
4
  declare function DrawerRoot({ side, modal, ...props }: Omit<React.ComponentProps<typeof BaseDrawer.Root>, 'swipeDirection'> & {
5
5
  side?: PanelSide;
6
6
  }): React.JSX.Element;
7
- declare const DrawerContent: ({ size, flush, closeLabel, className, children, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDrawer.Popup> & PanelOptions & {
7
+ declare const DrawerContent: ({ size, flush, closeLabel, focusBoundaryLabel, className, children, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDrawer.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 DrawerTitle({ className, ...props }: React.ComponentProps<typeof BaseDrawer.Title>): React.JSX.Element;
11
15
  declare function DrawerDescription({ className, ...props }: React.ComponentProps<typeof BaseDrawer.Description>): React.JSX.Element;
12
16
  export declare const Drawer: {
13
17
  Root: typeof DrawerRoot;
14
- Content: ({ size, flush, closeLabel, className, children, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDrawer.Popup> & PanelOptions & {
18
+ Content: ({ size, flush, closeLabel, focusBoundaryLabel, className, children, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseDrawer.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
  PrimitiveContent: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/drawer").DrawerContentProps, "ref"> & React.RefAttributes<HTMLDivElement>>;