@ahrowe/ui 0.37.0 → 0.38.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 (95) hide show
  1. package/README.md +4 -1
  2. package/dist/cjs-types/common/commandPalette/commandPalette.d.ts +4 -0
  3. package/dist/cjs-types/common/commandPalette/commandPalette.types.d.ts +42 -0
  4. package/dist/cjs-types/common/commandPalette/filterCommands.d.ts +15 -0
  5. package/dist/cjs-types/common/commandPalette/index.d.ts +2 -0
  6. package/dist/cjs-types/common/configProvider/configProvider.types.d.ts +6 -0
  7. package/dist/cjs-types/common/copyButton/copyButton.d.ts +4 -0
  8. package/dist/cjs-types/common/copyButton/copyButton.types.d.ts +36 -0
  9. package/dist/cjs-types/common/copyButton/index.d.ts +2 -0
  10. package/dist/cjs-types/common/errorBoundary/errorBoundary.d.ts +0 -3
  11. package/dist/cjs-types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
  12. package/dist/cjs-types/common/hooks/index.d.ts +2 -0
  13. package/dist/cjs-types/common/hooks/useClipboard.d.ts +16 -0
  14. package/dist/cjs-types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
  15. package/dist/cjs-types/common/menu/menu.types.d.ts +2 -0
  16. package/dist/cjs-types/common/themeProvider/themeProvider.d.ts +8 -7
  17. package/dist/cjs-types/common/weatherIcon/index.d.ts +3 -0
  18. package/dist/cjs-types/common/weatherIcon/parts.d.ts +26 -0
  19. package/dist/cjs-types/common/weatherIcon/weatherIcon.d.ts +4 -0
  20. package/dist/cjs-types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
  21. package/dist/cjs-types/index.d.ts +6 -0
  22. package/dist/esm/common/buttonGroup/buttonGroup.module.mjs.map +1 -1
  23. package/dist/esm/common/commandPalette/commandPalette.mjs +2 -0
  24. package/dist/esm/common/commandPalette/commandPalette.mjs.map +1 -0
  25. package/dist/esm/common/commandPalette/commandPalette.module.mjs +2 -0
  26. package/dist/esm/common/commandPalette/commandPalette.module.mjs.map +1 -0
  27. package/dist/esm/common/commandPalette/filterCommands.mjs +2 -0
  28. package/dist/esm/common/commandPalette/filterCommands.mjs.map +1 -0
  29. package/dist/esm/common/copyButton/copyButton.mjs +2 -0
  30. package/dist/esm/common/copyButton/copyButton.mjs.map +1 -0
  31. package/dist/esm/common/copyButton/copyButton.module.mjs +2 -0
  32. package/dist/esm/common/copyButton/copyButton.module.mjs.map +1 -0
  33. package/dist/esm/common/dropdown/dropdown.mjs +1 -1
  34. package/dist/esm/common/dropdown/dropdown.module.mjs.map +1 -1
  35. package/dist/esm/common/errorBoundary/errorBoundary.mjs +2 -2
  36. package/dist/esm/common/errorBoundary/errorBoundary.mjs.map +1 -1
  37. package/dist/esm/common/errorBoundary/errorBoundary.module.mjs +1 -1
  38. package/dist/esm/common/errorBoundary/errorBoundary.module.mjs.map +1 -1
  39. package/dist/esm/common/hooks/useClipboard.mjs +2 -0
  40. package/dist/esm/common/hooks/useClipboard.mjs.map +1 -0
  41. package/dist/esm/common/inputDropdown/inputDropdown.mjs +1 -1
  42. package/dist/esm/common/inputDropdown/inputDropdown.mjs.map +1 -1
  43. package/dist/esm/common/inputDropdown/inputDropdown.module.mjs +1 -1
  44. package/dist/esm/common/inputDropdown/inputDropdown.module.mjs.map +1 -1
  45. package/dist/esm/common/menu/menu.mjs +1 -1
  46. package/dist/esm/common/menu/menu.mjs.map +1 -1
  47. package/dist/esm/common/menu/menu.types.mjs.map +1 -1
  48. package/dist/esm/common/themeProvider/themeProvider.mjs +1 -1
  49. package/dist/esm/common/themeProvider/themeProvider.mjs.map +1 -1
  50. package/dist/esm/common/timer/timer.mjs +1 -1
  51. package/dist/esm/common/timer/timer.mjs.map +1 -1
  52. package/dist/esm/common/weatherIcon/parts.mjs +2 -0
  53. package/dist/esm/common/weatherIcon/parts.mjs.map +1 -0
  54. package/dist/esm/common/weatherIcon/weatherIcon.mjs +2 -0
  55. package/dist/esm/common/weatherIcon/weatherIcon.mjs.map +1 -0
  56. package/dist/esm/common/weatherIcon/weatherIcon.module.mjs +2 -0
  57. package/dist/esm/common/weatherIcon/weatherIcon.module.mjs.map +1 -0
  58. package/dist/esm/common/weatherIcon/weatherIcon.types.mjs +2 -0
  59. package/dist/esm/common/weatherIcon/weatherIcon.types.mjs.map +1 -0
  60. package/dist/esm/index.mjs +1 -1
  61. package/dist/index.cjs +3 -3
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/style.css +1 -1
  64. package/dist/types/common/commandPalette/commandPalette.d.ts +4 -0
  65. package/dist/types/common/commandPalette/commandPalette.types.d.ts +42 -0
  66. package/dist/types/common/commandPalette/filterCommands.d.ts +15 -0
  67. package/dist/types/common/commandPalette/index.d.ts +2 -0
  68. package/dist/types/common/configProvider/configProvider.types.d.ts +6 -0
  69. package/dist/types/common/copyButton/copyButton.d.ts +4 -0
  70. package/dist/types/common/copyButton/copyButton.types.d.ts +36 -0
  71. package/dist/types/common/copyButton/index.d.ts +2 -0
  72. package/dist/types/common/errorBoundary/errorBoundary.d.ts +0 -3
  73. package/dist/types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
  74. package/dist/types/common/hooks/index.d.ts +2 -0
  75. package/dist/types/common/hooks/useClipboard.d.ts +16 -0
  76. package/dist/types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
  77. package/dist/types/common/menu/menu.types.d.ts +2 -0
  78. package/dist/types/common/themeProvider/themeProvider.d.ts +8 -7
  79. package/dist/types/common/weatherIcon/index.d.ts +3 -0
  80. package/dist/types/common/weatherIcon/parts.d.ts +26 -0
  81. package/dist/types/common/weatherIcon/weatherIcon.d.ts +4 -0
  82. package/dist/types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
  83. package/dist/types/index.d.ts +6 -0
  84. package/docs/ButtonGroup.md +2 -0
  85. package/docs/CLAUDE.md +4 -1
  86. package/docs/CommandPalette.md +84 -0
  87. package/docs/ConfigProvider.md +2 -2
  88. package/docs/CopyButton.md +45 -0
  89. package/docs/ErrorBoundary.md +4 -4
  90. package/docs/Fab.md +1 -2
  91. package/docs/Hooks.md +39 -3
  92. package/docs/InputDropdown.md +9 -4
  93. package/docs/Menu.md +5 -3
  94. package/docs/WeatherIcon.md +49 -0
  95. package/package.json +1 -2
package/README.md CHANGED
@@ -132,8 +132,10 @@ runs where `@ahrowe/ui` is installed.
132
132
  | `Checkbox` | Boolean toggle input — settings, multi-select lists, accept terms, feature flags |
133
133
  | `Chip`, `ChipContainer`, `ChipStyleType` | Compact tag, label, or filter token — selected items, applied filters, category badges, user avatars with remove |
134
134
  | `ColorPicker` | Hex colour selection — theme editors, annotation tools, customisation panels |
135
+ | `CommandPalette` | A search-first dialog for running any command or jumping anywhere in the app from the keyboard, usually opened with ⌘K / Ctrl+K |
135
136
  | `ConfigProvider` | Set global **default props** for library components once, near the root of your app, instead of repeating the same prop on every instance |
136
137
  | `ConfirmModal` | Confirmation dialog before destructive or irreversible actions — delete, archive, send, overwrite |
138
+ | `CopyButton` | Icon-only button that copies a value to the clipboard: an invoice number, an ID in a table row, an API key or share link inside an `Input` |
137
139
  | `DatePicker` | Date selection input — booking forms, birth date fields, date range pickers, deadline selectors |
138
140
  | `Divider`, `DividerTextPosition`, `DividerOrientation` | A rule that separates sections of content — horizontal by default, filling the width of its parent |
139
141
  | `Drawer` | Inline horizontal panel that collapses and expands by animating its width — side panels, navigation rails, detail/filter panes that slide open beside the main content |
@@ -146,7 +148,7 @@ runs where `@ahrowe/ui` is installed.
146
148
  | `emptyFloorPlan`, `addWall`, `derivePlan` | The shared data model behind `RoomDrawer` and `RoomViewer` |
147
149
  | `FormValidator`, `Validators`, `useFormValidator` | Track and validate a single form field's value, dirty/touched state, and errors |
148
150
  | `useFormValidatorGroup`, `FormValidatorGroup`, `FormValidator`, `Validators` | Manage a whole form — multiple fields, a single submit handler, validate-on-submit, dirty/touched tracking across the form, and mapping server-side errors back onto the right fields |
149
- | `useHotkeys`, `formatHotkey`, `useMediaQuery`, `useCoarsePointer`, `useVisualViewportSize`, `useScrollEdges`, `useSwipeDismiss`, `useOverlay`, `tabbablesIn` | Standalone React hooks the library uses internally and publishes for the same jobs in your own code: keyboard shortcuts, media queries, the mobile keyboard's viewport, scroll-edge detection, swipe-to-dismiss, and the Escape / focus-trap / scroll-lock behaviour behind a custom overlay |
151
+ | `useHotkeys`, `formatHotkey`, `useMediaQuery`, `useCoarsePointer`, `useVisualViewportSize`, `useScrollEdges`, `useSwipeDismiss`, `useOverlay`, `useClipboard`, `tabbablesIn` | Standalone React hooks the library uses internally and publishes for the same jobs in your own code: keyboard shortcuts, media queries, the mobile keyboard's viewport, scroll-edge detection, swipe-to-dismiss, copying to the clipboard, and the Escape / focus-trap / scroll-lock behaviour behind a custom overlay |
150
152
  | `IconPicker` | Pick one item from a visual icon/image grid — category selectors, emoji-style pickers, avatar selectors, anything where each option is best represented by an icon or image rather than text alone |
151
153
  | `IdleManager` | Show something only once the user has stopped interacting — a screen saver, a session-timeout warning, an "are you still there?" prompt, an idle dashboard view |
152
154
  | `Input`, `InputType` | Text input with floating label, validation state, and type variants |
@@ -192,6 +194,7 @@ runs where `@ahrowe/ui` is installed.
192
194
  | `Tooltip` | Contextual help, info icons, field explanations, error pointers |
193
195
  | `Tree`, `flattenTree` | A hierarchical list the user can expand, collapse and pick from: categories, nested locations, folder structures, org charts |
194
196
  | `VirtualList` | Efficiently render large lists or tables — thousands of rows rendered with a virtualised scroll window so only visible rows are in the DOM |
197
+ | `WeatherIcon`, `WeatherType` | An animated weather symbol for a forecast, a dashboard widget or a day selector, one per condition a forecast reports |
195
198
  | `Wizard` | Animated step container for multi-step forms and flows |
196
199
 
197
200
  <!-- components:end -->
@@ -0,0 +1,4 @@
1
+ import { ReactElement } from 'react';
2
+ import { CommandPaletteProps } from './commandPalette.types.js';
3
+ declare function CommandPalette(props: CommandPaletteProps): ReactElement;
4
+ export default CommandPalette;
@@ -0,0 +1,42 @@
1
+ import { CSSProperties } from 'react';
2
+ import { MenuEntry } from '../menu/menu.types.js';
3
+ import { Presentation } from '../configProvider/presentation.types.js';
4
+ import { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types.js';
5
+ export type CommandPaletteSlots = 'root' | 'search' | 'input' | 'list' | 'item' | 'itemIcon' | 'itemLabel' | 'itemShortcut' | 'heading' | 'separator' | 'empty';
6
+ export interface CommandPaletteLabels {
7
+ /** Accessible name of the dialog and the search field. Default: "Search commands". */
8
+ label?: string;
9
+ /** Placeholder in the search field. Default: "Type a command or search…". */
10
+ placeholder?: string;
11
+ /** Shown when nothing matches. Default: "No results". */
12
+ empty?: string;
13
+ /** Shown while `isLoading` and nothing matches yet. Default: "Searching…". */
14
+ loading?: string;
15
+ /** Announced to screen readers as the matches change. */
16
+ results?: (count: number) => string;
17
+ }
18
+ export interface CommandPaletteProps extends HtmlProps<HTMLDivElement> {
19
+ /** The commands, in the same shape `Menu` takes. Headings group them; separators split groups. */
20
+ items: MenuEntry[];
21
+ isOpen: boolean;
22
+ onClose: () => void;
23
+ /** Called with the id of the command that was run, after its own `onClick`. */
24
+ onAction?: (id: string) => void;
25
+ /** Called on every keystroke, and with `''` each time the palette opens. */
26
+ onQueryChange?: (query: string) => void;
27
+ /**
28
+ * Filter `items` by the query. Default `true`. Set `false` when `items` are already the results,
29
+ * e.g. fetched from a server in `onQueryChange`.
30
+ */
31
+ filter?: boolean;
32
+ /** Shows the `loading` label instead of `empty` while nothing matches. */
33
+ isLoading?: boolean;
34
+ /** Centred dialog, bottom sheet, or a sheet only on a small touch screen. */
35
+ presentation?: Presentation;
36
+ /** Text this component renders of its own. */
37
+ labels?: CommandPaletteLabels;
38
+ className?: string;
39
+ style?: CSSProperties;
40
+ classNames?: SlotClassNames<CommandPaletteSlots>;
41
+ styles?: SlotStyles<CommandPaletteSlots>;
42
+ }
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from 'react';
2
+ import { MenuAction, MenuEntry } from '../menu/menu.types.js';
3
+ export interface CommandGroup {
4
+ heading?: ReactNode;
5
+ /** Whether a separator started this group, so the divider can be drawn above it. */
6
+ separated: boolean;
7
+ actions: MenuAction[];
8
+ }
9
+ /** Splits entries into groups at each heading and separator, keeping their order. */
10
+ export declare function groupCommands(items: MenuEntry[]): CommandGroup[];
11
+ /**
12
+ * The groups with only the actions matching `query`, best matches first within each group, and
13
+ * the group holding the best match first. A heading moves with its group, never away from it.
14
+ */
15
+ export declare function filterCommands(groups: CommandGroup[], query: string): CommandGroup[];
@@ -0,0 +1,2 @@
1
+ export { default } from './commandPalette.js';
2
+ export * from './commandPalette.types.js';
@@ -15,6 +15,9 @@ import { SearchInputLabels } from '../searchInput/searchInput.types.js';
15
15
  import { TooltipLabels } from '../tooltip/tooltip.types.js';
16
16
  import { VirtualListLabels } from '../virtualList/virtualList.types.js';
17
17
  import { ButtonProps } from '../button/button.types.js';
18
+ import { CopyButtonProps } from '../copyButton/copyButton.types.js';
19
+ import { CommandPaletteProps } from '../commandPalette/commandPalette.types.js';
20
+ import { WeatherIconProps } from '../weatherIcon/weatherIcon.types.js';
18
21
  import { ActionButtonsProps } from '../actionButtons/actionButtons.types.js';
19
22
  import { ConfirmModalProps } from '../confirmModal/confirmModal.types.js';
20
23
  import { BadgeProps } from '../badge/badge.types.js';
@@ -91,6 +94,7 @@ export interface ComponentDefaults {
91
94
  RadioGroup?: Partial<RadioGroupProps>;
92
95
  DatePicker?: Partial<DatePickerProps>;
93
96
  Button?: Partial<ButtonProps>;
97
+ CopyButton?: Partial<CopyButtonProps>;
94
98
  ActionButtons?: Partial<ActionButtonsProps>;
95
99
  ConfirmModal?: Partial<ConfirmModalProps>;
96
100
  Badge?: Partial<BadgeProps>;
@@ -118,6 +122,8 @@ export interface ComponentDefaults {
118
122
  RoomViewer?: Partial<RoomViewerProps>;
119
123
  Popover?: Partial<PopoverProps>;
120
124
  Modal?: Partial<ModalProps>;
125
+ CommandPalette?: Partial<CommandPaletteProps>;
126
+ WeatherIcon?: Partial<WeatherIconProps>;
121
127
  }
122
128
  export interface ConfigProviderProps {
123
129
  children: React.ReactNode;
@@ -0,0 +1,4 @@
1
+ import { ReactElement } from 'react';
2
+ import { CopyButtonProps } from './copyButton.types.js';
3
+ declare function CopyButton(props: CopyButtonProps): ReactElement;
4
+ export default CopyButton;
@@ -0,0 +1,36 @@
1
+ import { CSSProperties, MouseEvent, Ref } from 'react';
2
+ import { IconContent } from '../types/icon.types.js';
3
+ import { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types.js';
4
+ export type CopyButtonSlots = 'root' | 'icon';
5
+ export interface CopyButtonLabels {
6
+ /** Accessible name and tooltip before copying. Default: "Copy". */
7
+ copy?: string;
8
+ /** Accessible name and tooltip after a successful copy, also announced. Default: "Copied". */
9
+ copied?: string;
10
+ /** Announced when the copy fails. Default: "Could not copy". */
11
+ failed?: string;
12
+ }
13
+ export interface CopyButtonProps extends Omit<HtmlProps<HTMLDivElement>, 'onCopy' | 'onError'> {
14
+ /** Text written to the clipboard on click. */
15
+ value: string;
16
+ /** How long the copied state shows, in ms. Default: 2000. */
17
+ timeout?: number;
18
+ /** Icon before copying. Default: `faCopy`. */
19
+ icon?: IconContent;
20
+ /** Icon while the copied state shows. Default: `faCheck`. */
21
+ copiedIcon?: IconContent;
22
+ /** Called after a successful copy. */
23
+ onCopy?: (value: string) => void;
24
+ /** Called when the clipboard refuses the write, e.g. outside a secure context. */
25
+ onError?: () => void;
26
+ /** Called on every click, before the copy. */
27
+ onClick?: (event: MouseEvent<HTMLDivElement>) => void;
28
+ /** Text this component renders of its own. */
29
+ labels?: CopyButtonLabels;
30
+ /** Ref to the root element. */
31
+ ref?: Ref<HTMLDivElement>;
32
+ className?: string;
33
+ style?: CSSProperties;
34
+ classNames?: SlotClassNames<CopyButtonSlots>;
35
+ styles?: SlotStyles<CopyButtonSlots>;
36
+ }
@@ -0,0 +1,2 @@
1
+ export { default } from './copyButton.js';
2
+ export * from './copyButton.types.js';
@@ -2,13 +2,10 @@ import { default as React } from 'react';
2
2
  import { ErrorBoundaryProps, ErrorBoundaryState } from './errorBoundary.types.js';
3
3
  declare class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
4
4
  state: ErrorBoundaryState;
5
- private copyResetTimeout;
6
5
  static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
7
6
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
8
7
  componentDidUpdate(prevProps: ErrorBoundaryProps): void;
9
- componentWillUnmount(): void;
10
8
  private reset;
11
- private copyStackTrace;
12
9
  render(): React.ReactNode;
13
10
  }
14
11
  export default ErrorBoundary;
@@ -13,9 +13,9 @@ export interface ErrorBoundaryFallbackProps {
13
13
  export interface ErrorBoundaryLabels {
14
14
  /** Accordion header for the stack trace section. Default: "Stack trace". */
15
15
  stackTrace?: string;
16
- /** Copy button label before copying. Default: "Copy". */
16
+ /** Copy button's tooltip and accessible name before copying. Default: "Copy". */
17
17
  copy?: string;
18
- /** Copy button label right after a successful copy. Default: "Copied". */
18
+ /** Copy button's tooltip and accessible name right after a successful copy. Default: "Copied". */
19
19
  copied?: string;
20
20
  /** Reset button label. Default: "Try again". */
21
21
  reset?: string;
@@ -53,5 +53,4 @@ export interface ErrorBoundaryProps extends Omit<HtmlProps, 'title' | 'onError'>
53
53
  export interface ErrorBoundaryState {
54
54
  error: Error | null;
55
55
  errorInfo: ErrorInfo | null;
56
- copied: boolean;
57
56
  }
@@ -11,3 +11,5 @@ export type { UseSwipeDismissOptions, UseSwipeDismissResult } from './useSwipeDi
11
11
  export { useOverlay } from './useOverlay.js';
12
12
  export type { UseOverlayArgs } from './useOverlay.js';
13
13
  export { tabbablesIn } from './useFocusBoundary.js';
14
+ export { useClipboard } from './useClipboard.js';
15
+ export type { UseClipboardOptions, UseClipboardResult } from './useClipboard.js';
@@ -0,0 +1,16 @@
1
+ export interface UseClipboardOptions {
2
+ /** How long `copied` stays `true` after a successful copy, in ms. Default: 2000. */
3
+ timeout?: number;
4
+ }
5
+ export interface UseClipboardResult {
6
+ /** Writes `text` to the clipboard. Resolves `true` on success, `false` on failure. */
7
+ copy: (text: string) => Promise<boolean>;
8
+ /** `true` for `timeout` ms after a successful copy. */
9
+ copied: boolean;
10
+ /** Why the last copy failed, until the next attempt. */
11
+ error: Error | null;
12
+ /** Clears `copied` and `error` early. */
13
+ reset: () => void;
14
+ }
15
+ export declare function useClipboard({ timeout }?: UseClipboardOptions): UseClipboardResult;
16
+ export default useClipboard;
@@ -1,7 +1,11 @@
1
1
  import { default as React } from 'react';
2
2
  import { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types.js';
3
3
  import { FormValidator } from '../../services/formValidation/index.js';
4
- export type InputDropdownSlots = 'root' | 'dropdown' | 'item';
4
+ export type InputDropdownSlots = 'root' | 'dropdown' | 'item' | 'empty';
5
+ export interface InputDropdownLabels {
6
+ /** Shown when nothing matches the typed text and `allowCustomValue` is off (default `No matches`). */
7
+ empty?: string;
8
+ }
5
9
  export interface InputDropdownItem {
6
10
  label?: string;
7
11
  value: string;
@@ -29,6 +33,8 @@ export interface InputDropdownProps extends Omit<HtmlProps, 'onSelect'> {
29
33
  onChange?: (value: unknown) => void;
30
34
  placeholder?: string;
31
35
  readOnly?: boolean;
36
+ /** Overrides the text InputDropdown renders of its own. Merged per key. */
37
+ labels?: InputDropdownLabels;
32
38
  className?: string;
33
39
  style?: React.CSSProperties;
34
40
  classNames?: SlotClassNames<InputDropdownSlots>;
@@ -20,6 +20,8 @@ export interface MenuAction {
20
20
  * into a `useHotkeys` map, so the one spelling covers both ends.
21
21
  */
22
22
  shortcut?: string;
23
+ /** Extra search terms for `CommandPalette`, which also matches a string `label`. `Menu` ignores them. */
24
+ keywords?: string[];
23
25
  disabled?: boolean;
24
26
  /** Renders in the destructive colour. For deleting, discarding, revoking. */
25
27
  danger?: boolean;
@@ -9,6 +9,11 @@ interface State {
9
9
  resolvedVariables: Record<string, string>;
10
10
  colorFadeActive: boolean;
11
11
  currentThemeColor: string | null;
12
+ /** What `resolvedVariables` was resolved from, to tell a theme change from any other render. */
13
+ resolvedFrom: {
14
+ themes: Theme[] | undefined;
15
+ currentThemeId: string | undefined;
16
+ };
12
17
  }
13
18
  declare class ThemeProvider extends Component<Props, State> {
14
19
  state: State;
@@ -16,6 +21,7 @@ declare class ThemeProvider extends Component<Props, State> {
16
21
  private wrapperRef;
17
22
  private themeColorMeta;
18
23
  private createdThemeColorMeta;
24
+ static getDerivedStateFromProps(props: Props, state: State): Partial<State> | null;
19
25
  componentDidMount(): void;
20
26
  componentDidUpdate(prevProps: Props, prevState: State): void;
21
27
  componentWillUnmount(): void;
@@ -26,13 +32,8 @@ declare class ThemeProvider extends Component<Props, State> {
26
32
  * (and removes it again on unmount).
27
33
  */
28
34
  private syncThemeColorMeta;
29
- /**
30
- * Resolves the full variable set for a given theme ID by walking up the
31
- * baseThemeId chain. Returns variables ordered from deepest ancestor to
32
- * the requested theme so callers can merge in order (later entries win).
33
- */
34
- private resolveChain;
35
- private applyTheme;
35
+ /** `--primary-color` as the browser resolved it, which may be a `var()` of another variable. */
36
+ private readThemeColor;
36
37
  render(): React.JSX.Element;
37
38
  }
38
39
  export default ThemeProvider;
@@ -0,0 +1,3 @@
1
+ export { default } from './weatherIcon.js';
2
+ export { WeatherType } from './weatherIcon.types.js';
3
+ export type { WeatherIconLabels, WeatherIconProps } from './weatherIcon.types.js';
@@ -0,0 +1,26 @@
1
+ import { ReactElement } from 'react';
2
+ import { WeatherPartProps } from './weatherIcon.types.js';
3
+ /** Exported so a scene can clip what sits behind the cloud to this same silhouette. */
4
+ export declare const CLOUD_PATH = "M 26.3 62.9 C 15.4 65.0 15.0 48.5 27.8 49.8 C 23.9 43.9 34.2 34.0 43.0 39.6 C 46.0 26.2 73.9 32.0 68.9 46.3 C 85.0 44.3 84.6 64.2 72.4 62.5 Z";
5
+ export declare function Cloud({ className, delayed }: {
6
+ className?: string;
7
+ delayed?: boolean;
8
+ }): ReactElement;
9
+ export declare function Sun({ className }: {
10
+ className?: string;
11
+ }): ReactElement;
12
+ export declare function Moon({ className }: {
13
+ className?: string;
14
+ }): ReactElement;
15
+ export declare enum RainIntensity {
16
+ Light = "light",
17
+ Medium = "medium",
18
+ Heavy = "heavy"
19
+ }
20
+ export declare function Rain({ animated, intensity, }: WeatherPartProps & {
21
+ intensity?: RainIntensity;
22
+ }): ReactElement;
23
+ export declare function Snow({ animated }: WeatherPartProps): ReactElement;
24
+ export declare function Hail({ animated }: WeatherPartProps): ReactElement;
25
+ export declare function Fog(): ReactElement;
26
+ export declare function Lightning(): ReactElement;
@@ -0,0 +1,4 @@
1
+ import { ReactElement } from 'react';
2
+ import { WeatherIconProps } from './weatherIcon.types.js';
3
+ declare function WeatherIcon(props: WeatherIconProps): ReactElement;
4
+ export default WeatherIcon;
@@ -0,0 +1,44 @@
1
+ import { CSSProperties, SVGAttributes } from 'react';
2
+ import { DataProps } from '../types/slots.types.js';
3
+ export declare enum WeatherType {
4
+ Sunny = "sunny",
5
+ ClearNight = "clearNight",
6
+ Cloudy = "cloudy",
7
+ PartlyCloudy = "partlyCloudy",
8
+ PartlyCloudyNight = "partlyCloudyNight",
9
+ RainyLight = "rainyLight",
10
+ Rainy = "rainy",
11
+ RainyHeavy = "rainyHeavy",
12
+ Snowy = "snowy",
13
+ Hail = "hail",
14
+ Foggy = "foggy",
15
+ Thunderstorm = "thunderstorm"
16
+ }
17
+ /** The accessible name of each weather, read out by screen readers. */
18
+ export type WeatherIconLabels = Partial<Record<`${WeatherType}`, string>>;
19
+ /**
20
+ * The root is an `<svg>`, so the native attributes come from `SVGAttributes` rather than
21
+ * `HtmlProps`. Its `type` is the attribute of `<script>` and `<style>`, not of an `<svg>`.
22
+ */
23
+ export interface WeatherIconProps extends DataProps, Omit<SVGAttributes<SVGSVGElement>, 'type'> {
24
+ /**
25
+ * The weather to draw. Takes the enum or its string value, so an API's own enum with the same
26
+ * values passes as it is.
27
+ */
28
+ type: WeatherType | `${WeatherType}`;
29
+ /** Width and height, any CSS size. Default `64`. */
30
+ size?: number | string;
31
+ /**
32
+ * Default `true`. `false` draws a still frame, for small or repeated icons where the motion is
33
+ * too small to see but costs the same. Always still under `prefers-reduced-motion`.
34
+ */
35
+ animated?: boolean;
36
+ /** Text this component renders of its own. */
37
+ labels?: WeatherIconLabels;
38
+ className?: string;
39
+ style?: CSSProperties;
40
+ }
41
+ /** What a falling part needs to know to pick its still frame. */
42
+ export interface WeatherPartProps {
43
+ animated: boolean;
44
+ }
@@ -31,10 +31,14 @@ export { default as Chip } from './common/chip/index.js';
31
31
  export * from './common/chip/index.js';
32
32
  export { default as ColorPicker } from './common/colorPicker/index.js';
33
33
  export * from './common/colorPicker/index.js';
34
+ export { default as CommandPalette } from './common/commandPalette/index.js';
35
+ export * from './common/commandPalette/index.js';
34
36
  export { default as ConfigProvider } from './common/configProvider/index.js';
35
37
  export * from './common/configProvider/index.js';
36
38
  export { default as ConfirmModal } from './common/confirmModal/index.js';
37
39
  export * from './common/confirmModal/index.js';
40
+ export { default as CopyButton } from './common/copyButton/index.js';
41
+ export * from './common/copyButton/index.js';
38
42
  export { default as DatePicker } from './common/datePicker/index.js';
39
43
  export * from './common/datePicker/index.js';
40
44
  export { default as Divider } from './common/divider/index.js';
@@ -146,6 +150,8 @@ export { default as Tree } from './common/tree/index.js';
146
150
  export * from './common/tree/index.js';
147
151
  export { default as VirtualList } from './common/virtualList/index.js';
148
152
  export * from './common/virtualList/index.js';
153
+ export { default as WeatherIcon } from './common/weatherIcon/index.js';
154
+ export * from './common/weatherIcon/index.js';
149
155
  export { default as Wizard } from './common/wizard/index.js';
150
156
  export * from './common/wizard/index.js';
151
157
  export * from './common/hooks/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"buttonGroup.module.mjs","names":[],"sources":["../../../../package/common/buttonGroup/buttonGroup.module.pcss"],"sourcesContent":[".buttonGroup {\n display: inline-flex;\n align-items: stretch;\n\n /* Each rule below targets its position with a pseudo-class (:first-child, :last-child, or\n :not()), which contributes extra specificity beyond a plain class selector. That's what\n reliably beats a child component's own base border-radius rule (e.g. Button's), regardless\n of which CSS module happens to load first in the final bundle — a plain \".buttonGroup > *\"\n reset would tie on specificity with Button's own class and depend on load order.\n\n Alongside the direct border-radius reset (for a child whose visible border lives right on\n its own root, e.g. Button), each rule also sets --group-radius-* custom properties. Unlike\n border-radius itself, custom properties inherit through every descendant regardless of\n nesting depth, so a child whose actual border lives on a nested element (e.g. Input's\n `fieldset`, buried inside a `<label>`) can flatten correctly too, as long as that nested\n element's own border-radius reads `var(--group-radius-tl, var(--default-border-radius))`\n etc. instead of hardcoding `var(--default-border-radius)` — see input.module.pcss.\n\n A nested ButtonGroup (role=\"group\") is excluded throughout: it manages its own corners and\n spacing internally, so the outer group only arranges it as a self-contained unit rather than\n reaching into it. */\n > *:not(:first-child):not(:last-child):not([role='group']) {\n border-radius: 0;\n\n --group-radius-tl: 0;\n --group-radius-tr: 0;\n --group-radius-br: 0;\n --group-radius-bl: 0;\n }\n\n > *:hover,\n > *:focus-within {\n z-index: 1;\n }\n\n &:not(&-vertical) {\n /* A separator (e.g. a vertical Divider) is meant to stay visible between two buttons, not\n merge into one of them, so it's excluded from the border-collapsing overlap below — and\n so is whatever comes right after one, since that overlap is only meant to collapse two\n adjacent button borders, not eat into the separator itself. A nested ButtonGroup is\n excluded the same way, and given real spacing instead, rather than being squeezed 1px\n into whatever's next to it. */\n > *:not(:first-child):not([role='separator']):not([role='group']) {\n margin-left: -1px;\n }\n\n /* Matches the specificity of the collapse rule above (three :not()s deep, not two) so this\n reliably wins the override regardless of source order — the collapse rule's own three\n :not()s (:first-child, [role=separator], [role=group]) outweigh an adjacency selector with\n only one, so a fourth, redundant :not() is added here purely to close that gap. The\n trailing :not([role='group']) on both lines also keeps this from matching when the\n following element is itself a nested group — that case must fall through to the dedicated\n 8px group-spacing rule below, not get zeroed. */\n > [role='separator'] + *:not(:first-child):not([role='group']),\n > [role='group'] + *:not(:first-child):not([role='separator']):not([role='group']) {\n margin-left: 0;\n }\n\n > [role='group']:not(:first-child) {\n margin-left: 8px;\n }\n\n > *:first-child:not(:last-child):not([role='group']) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n\n --group-radius-tr: 0;\n --group-radius-br: 0;\n }\n\n > *:last-child:not(:first-child):not([role='group']) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n\n --group-radius-tl: 0;\n --group-radius-bl: 0;\n }\n }\n\n &-vertical {\n flex-direction: column;\n\n > *:not(:first-child):not([role='separator']):not([role='group']) {\n margin-top: -1px;\n }\n\n > [role='separator'] + *:not(:first-child):not([role='group']),\n > [role='group'] + *:not(:first-child):not([role='separator']):not([role='group']) {\n margin-top: 0;\n }\n\n > [role='group']:not(:first-child) {\n margin-top: 8px;\n }\n\n > *:first-child:not(:last-child):not([role='group']) {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n\n --group-radius-bl: 0;\n --group-radius-br: 0;\n }\n\n > *:last-child:not(:first-child):not([role='group']) {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n\n --group-radius-tl: 0;\n --group-radius-tr: 0;\n }\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"buttonGroup.module.mjs","names":[],"sources":["../../../../package/common/buttonGroup/buttonGroup.module.pcss"],"sourcesContent":[".buttonGroup {\n display: inline-flex;\n align-items: stretch;\n\n /* At a fractional scale two overlapping borders round to different device pixels, so each seam\n keeps one: the earlier child clips its shared 1px, or the next one does while the earlier has\n the focus (docs/ButtonGroup.md). Reset per group so a nested one starts clean. */\n --group-clip-start: initial;\n --group-clip-end: initial;\n\n /* Each rule below targets its position with a pseudo-class (:first-child, :last-child, or\n :not()), which contributes extra specificity beyond a plain class selector. That's what\n reliably beats a child component's own base border-radius rule (e.g. Button's), regardless\n of which CSS module happens to load first in the final bundle — a plain \".buttonGroup > *\"\n reset would tie on specificity with Button's own class and depend on load order.\n\n Alongside the direct border-radius reset (for a child whose visible border lives right on\n its own root, e.g. Button), each rule also sets --group-radius-* custom properties. Unlike\n border-radius itself, custom properties inherit through every descendant regardless of\n nesting depth, so a child whose actual border lives on a nested element (e.g. Input's\n `fieldset`, buried inside a `<label>`) can flatten correctly too, as long as that nested\n element's own border-radius reads `var(--group-radius-tl, var(--default-border-radius))`\n etc. instead of hardcoding `var(--default-border-radius)` — see input.module.pcss.\n\n A nested ButtonGroup (role=\"group\") is excluded throughout: it manages its own corners and\n spacing internally, so the outer group only arranges it as a self-contained unit rather than\n reaching into it. */\n > *:not(:first-child):not(:last-child):not([role='group']) {\n border-radius: 0;\n\n --group-radius-tl: 0;\n --group-radius-tr: 0;\n --group-radius-br: 0;\n --group-radius-bl: 0;\n }\n\n > *:hover,\n > *:focus-within {\n z-index: 1;\n }\n\n &:not(&-vertical) {\n /* A separator (e.g. a vertical Divider) is meant to stay visible between two buttons, not\n merge into one of them, so it's excluded from the border-collapsing overlap below — and\n so is whatever comes right after one, since that overlap is only meant to collapse two\n adjacent button borders, not eat into the separator itself. A nested ButtonGroup is\n excluded the same way, and given real spacing instead, rather than being squeezed 1px\n into whatever's next to it. */\n > *:not(:first-child):not([role='separator']):not([role='group']) {\n margin-left: -1px;\n }\n\n > *:not([role='separator']):not([role='group']):has(+ :not([role='separator']):not([role='group'])) {\n --group-clip-end: 1px;\n\n clip-path: inset(-100vmax var(--group-clip-end, -100vmax) -100vmax var(--group-clip-start, -100vmax));\n }\n\n > *:not([role='separator']):not([role='group']):has(+ :not([role='separator']):not([role='group'])):focus-within {\n --group-clip-end: initial;\n }\n\n > :focus-within:not([role='separator']):not([role='group']) + *:not([role='separator']):not([role='group']) {\n --group-clip-start: 1px;\n\n clip-path: inset(-100vmax var(--group-clip-end, -100vmax) -100vmax var(--group-clip-start, -100vmax));\n }\n\n /* Matches the specificity of the collapse rule above (three :not()s deep, not two) so this\n reliably wins the override regardless of source order — the collapse rule's own three\n :not()s (:first-child, [role=separator], [role=group]) outweigh an adjacency selector with\n only one, so a fourth, redundant :not() is added here purely to close that gap. The\n trailing :not([role='group']) on both lines also keeps this from matching when the\n following element is itself a nested group — that case must fall through to the dedicated\n 8px group-spacing rule below, not get zeroed. */\n > [role='separator'] + *:not(:first-child):not([role='group']),\n > [role='group'] + *:not(:first-child):not([role='separator']):not([role='group']) {\n margin-left: 0;\n }\n\n > [role='group']:not(:first-child) {\n margin-left: 8px;\n }\n\n > *:first-child:not(:last-child):not([role='group']) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n\n --group-radius-tr: 0;\n --group-radius-br: 0;\n }\n\n > *:last-child:not(:first-child):not([role='group']) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n\n --group-radius-tl: 0;\n --group-radius-bl: 0;\n }\n }\n\n &-vertical {\n flex-direction: column;\n\n > *:not(:first-child):not([role='separator']):not([role='group']) {\n margin-top: -1px;\n }\n\n > *:not([role='separator']):not([role='group']):has(+ :not([role='separator']):not([role='group'])) {\n --group-clip-end: 1px;\n\n clip-path: inset(var(--group-clip-start, -100vmax) -100vmax var(--group-clip-end, -100vmax) -100vmax);\n }\n\n > *:not([role='separator']):not([role='group']):has(+ :not([role='separator']):not([role='group'])):focus-within {\n --group-clip-end: initial;\n }\n\n > :focus-within:not([role='separator']):not([role='group']) + *:not([role='separator']):not([role='group']) {\n --group-clip-start: 1px;\n\n clip-path: inset(var(--group-clip-start, -100vmax) -100vmax var(--group-clip-end, -100vmax) -100vmax);\n }\n\n > [role='separator'] + *:not(:first-child):not([role='group']),\n > [role='group'] + *:not(:first-child):not([role='separator']):not([role='group']) {\n margin-top: 0;\n }\n\n > [role='group']:not(:first-child) {\n margin-top: 8px;\n }\n\n > *:first-child:not(:last-child):not([role='group']) {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n\n --group-radius-bl: 0;\n --group-radius-br: 0;\n }\n\n > *:last-child:not(:first-child):not([role='group']) {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n\n --group-radius-tl: 0;\n --group-radius-tr: 0;\n }\n }\n}\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{renderIcon as n}from"../utils/renderIcon.mjs";import r from"../modal/modal.mjs";import{formatHotkey as ee}from"../hooks/useHotkeys.mjs";import{edgeIndex as i,nextIndex as a,scrollOptionIntoView as o}from"../utils/listNavigation.mjs";import{filterCommands as s,groupCommands as c}from"./filterCommands.mjs";import l from"./commandPalette.module.mjs";import{useEffect as u,useId as te,useRef as d,useState as f}from"react";import p from"classnames";import{faMagnifyingGlass as m}from"@fortawesome/free-solid-svg-icons";import{jsx as h,jsxs as g}from"react/jsx-runtime";var _={label:`Search commands`,placeholder:`Type a command or search…`,empty:`No results`,loading:`Searching…`,results:e=>e===1?`1 result`:`${e} results`};function v(v){let{items:y,isOpen:b,onClose:x,onAction:S,onQueryChange:C,filter:w=!0,isLoading:T=!1,presentation:E,labels:D,className:O,style:k,classNames:A,styles:j,...ne}=e(`CommandPalette`,v),M=t(`CommandPalette`,D,_),N=te(),P=d(null),[F,I]=f(``),[L,R]=f(null),[z,B]=f(b);b!==z&&(B(b),b&&(I(``),R(null)));let V=d(C);u(()=>{V.current=C}),u(()=>{b&&V.current?.(``)},[b]);let H=w?s(c(y),F):c(y),U=H.flatMap(e=>e.actions),W=e=>!!U[e]?.disabled,G=U.findIndex(e=>e.id===L&&!e.disabled),K=G>=0?G:i(`first`,U.length,{isDisabled:W}),q=e=>`${N}-${encodeURIComponent(e.id)}`,J=K===null?void 0:q(U[K]);u(()=>{J&&o(P.current,document.getElementById(J))},[J]);function Y(e){e&&!e.disabled&&(e.onClick?.(),S?.(e.id),x())}function X(e){if(!e.nativeEvent.isComposing){if(e.key===`ArrowDown`||e.key===`ArrowUp`){let t=a(K,e.key===`ArrowDown`?1:-1,U.length,{isDisabled:W});R(t===null?null:U[t].id)}else if(e.key===`Enter`)Y(K===null?void 0:U[K]);else return;e.preventDefault()}}let Z=F.trim()!==``,Q=T?M.loading:M.empty,$=-1;return g(r,{...ne,"aria-label":M.label,isOpen:b,onClose:x,hideHeader:!0,presentation:E,className:p(l.commandPalette,O,A?.root),style:{...k,...j?.root},children:[g(`div`,{className:p(l.commandPaletteSearch,A?.search),style:j?.search,children:[n(m,{className:l.commandPaletteSearchIcon}),h(`input`,{type:`text`,role:`combobox`,"aria-label":M.label,"aria-expanded":`true`,"aria-controls":N,"aria-autocomplete":`list`,"aria-activedescendant":J,autoComplete:`off`,spellCheck:!1,placeholder:M.placeholder,value:F,onChange:e=>{I(e.target.value),R(null),C?.(e.target.value)},onKeyDown:X,className:p(l.commandPaletteInput,A?.input),style:j?.input})]}),h(`div`,{ref:P,id:N,role:`listbox`,"aria-label":M.label,className:p(l.commandPaletteList,A?.list),style:j?.list,children:H.map((e,t)=>{let r=`${N}-heading-${t}`;return g(`div`,{role:e.heading===void 0?`presentation`:`group`,"aria-labelledby":e.heading===void 0?void 0:r,children:[e.separated&&t>0&&h(`div`,{"aria-hidden":!0,className:p(l.commandPaletteSeparator,A?.separator),style:j?.separator}),e.heading!==void 0&&h(`div`,{id:r,role:`presentation`,className:p(l.commandPaletteHeading,A?.heading),style:j?.heading,children:e.heading}),e.actions.map(e=>{$+=1;let t=$;return g(`div`,{id:q(e),role:`option`,"aria-selected":t===K,"aria-disabled":e.disabled||void 0,className:p(l.commandPaletteItem,t===K&&l.commandPaletteItemActive,e.danger&&l.commandPaletteItemDanger,e.disabled&&l.commandPaletteItemDisabled,A?.item),style:j?.item,onMouseDown:e=>e.preventDefault(),onMouseMove:()=>!e.disabled&&R(e.id),onClick:()=>Y(e),children:[e.icon!=null&&n(e.icon,{className:p(l.commandPaletteItemIcon,A?.itemIcon),style:j?.itemIcon}),h(`span`,{className:p(l.commandPaletteItemLabel,A?.itemLabel),style:j?.itemLabel,children:e.label}),e.shortcut!=null&&h(`span`,{className:p(l.commandPaletteItemShortcut,A?.itemShortcut),style:j?.itemShortcut,children:ee(e.shortcut)})]},e.id)})]},t)})}),Z&&U.length===0&&h(`div`,{className:p(l.commandPaletteEmpty,A?.empty),style:j?.empty,children:Q}),h(`span`,{className:l.commandPaletteStatus,role:`status`,"aria-live":`polite`,children:Z?U.length>0?M.results(U.length):Q:``})]})}export{v as default};
2
+ //# sourceMappingURL=commandPalette.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandPalette.mjs","names":[],"sources":["../../../../package/common/commandPalette/commandPalette.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState, type KeyboardEvent, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';\n\nimport Modal from '../modal';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { formatHotkey } from '../hooks/useHotkeys';\nimport { edgeIndex, nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\nimport { renderIcon } from '../utils/renderIcon';\nimport type { MenuAction } from '../menu/menu.types';\nimport { filterCommands, groupCommands } from './filterCommands';\nimport type { CommandPaletteLabels, CommandPaletteProps } from './commandPalette.types';\nimport styles from './commandPalette.module.pcss';\n\nconst DEFAULT_LABELS: Required<CommandPaletteLabels> = {\n label: 'Search commands',\n placeholder: 'Type a command or search…',\n empty: 'No results',\n loading: 'Searching…',\n results: (count) => (count === 1 ? '1 result' : `${count} results`),\n};\n\nfunction CommandPalette(props: CommandPaletteProps): ReactElement {\n const {\n items,\n isOpen,\n onClose,\n onAction,\n onQueryChange,\n filter = true,\n isLoading = false,\n presentation,\n labels,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('CommandPalette', props);\n const text = useLabels('CommandPalette', labels, DEFAULT_LABELS);\n const listId = useId();\n const listRef = useRef<HTMLDivElement>(null);\n\n const [query, setQuery] = useState('');\n // An id rather than an index, so the highlight stays on the same command while server results\n // are replaced around it. Typing clears it instead, so Enter runs the best match.\n const [activeId, setActiveId] = useState<string | null>(null);\n const [wasOpen, setWasOpen] = useState(isOpen);\n if (isOpen !== wasOpen) {\n setWasOpen(isOpen);\n if (isOpen) {\n setQuery('');\n setActiveId(null);\n }\n }\n\n // Read through a ref so an inline handler does not re-fire the effect on every parent render.\n const onQueryChangeRef = useRef(onQueryChange);\n useEffect(() => {\n onQueryChangeRef.current = onQueryChange;\n });\n useEffect(() => {\n if (isOpen) onQueryChangeRef.current?.('');\n }, [isOpen]);\n\n const groups = filter ? filterCommands(groupCommands(items), query) : groupCommands(items);\n const actions = groups.flatMap((group) => group.actions);\n const isDisabled = (index: number) => !!actions[index]?.disabled;\n const found = actions.findIndex((action) => action.id === activeId && !action.disabled);\n const activeIndex = found >= 0 ? found : edgeIndex('first', actions.length, { isDisabled });\n // From the command's id, not its position: an id that stayed the same while the option under it\n // changed would not make a screen reader announce the new highlight.\n const optionId = (action: MenuAction) => `${listId}-${encodeURIComponent(action.id)}`;\n const activeOptionId = activeIndex === null ? undefined : optionId(actions[activeIndex]);\n\n useEffect(() => {\n if (!activeOptionId) return;\n scrollOptionIntoView(listRef.current, document.getElementById(activeOptionId));\n }, [activeOptionId]);\n\n function run(action: MenuAction | undefined) {\n if (!action || action.disabled) return;\n action.onClick?.();\n onAction?.(action.id);\n onClose();\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n // Enter confirms an IME composition before it means anything here.\n if (event.nativeEvent.isComposing) return;\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n const next = nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, actions.length, { isDisabled });\n setActiveId(next === null ? null : actions[next].id);\n } else if (event.key === 'Enter') {\n run(activeIndex === null ? undefined : actions[activeIndex]);\n } else {\n return;\n }\n event.preventDefault();\n }\n\n const hasQuery = query.trim() !== '';\n const emptyText = isLoading ? text.loading : text.empty;\n let index = -1;\n\n return (\n <Modal\n {...rest}\n aria-label={text.label}\n isOpen={isOpen}\n onClose={onClose}\n hideHeader\n presentation={presentation}\n className={cx(styles.commandPalette, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div className={cx(styles.commandPaletteSearch, classNames?.search)} style={slotStyles?.search}>\n {renderIcon(faMagnifyingGlass, { className: styles.commandPaletteSearchIcon })}\n <input\n type='text'\n role='combobox'\n aria-label={text.label}\n aria-expanded='true'\n aria-controls={listId}\n aria-autocomplete='list'\n aria-activedescendant={activeOptionId}\n autoComplete='off'\n spellCheck={false}\n placeholder={text.placeholder}\n value={query}\n onChange={(event) => {\n setQuery(event.target.value);\n setActiveId(null);\n onQueryChange?.(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n className={cx(styles.commandPaletteInput, classNames?.input)}\n style={slotStyles?.input}\n />\n </div>\n\n <div\n ref={listRef}\n id={listId}\n role='listbox'\n aria-label={text.label}\n className={cx(styles.commandPaletteList, classNames?.list)}\n style={slotStyles?.list}\n >\n {groups.map((group, groupIndex) => {\n const headingId = `${listId}-heading-${groupIndex}`;\n return (\n <div\n // Groups carry no id of their own; a new query re-renders the list wholesale anyway.\n key={groupIndex}\n role={group.heading === undefined ? 'presentation' : 'group'}\n aria-labelledby={group.heading === undefined ? undefined : headingId}\n >\n {group.separated && groupIndex > 0 && (\n <div\n aria-hidden\n className={cx(styles.commandPaletteSeparator, classNames?.separator)}\n style={slotStyles?.separator}\n />\n )}\n {group.heading !== undefined && (\n <div\n id={headingId}\n role='presentation'\n className={cx(styles.commandPaletteHeading, classNames?.heading)}\n style={slotStyles?.heading}\n >\n {group.heading}\n </div>\n )}\n {group.actions.map((action) => {\n index += 1;\n const own = index;\n return (\n <div\n key={action.id}\n id={optionId(action)}\n role='option'\n aria-selected={own === activeIndex}\n aria-disabled={action.disabled || undefined}\n className={cx(\n styles.commandPaletteItem,\n own === activeIndex && styles.commandPaletteItemActive,\n action.danger && styles.commandPaletteItemDanger,\n action.disabled && styles.commandPaletteItemDisabled,\n classNames?.item,\n )}\n style={slotStyles?.item}\n // Keeps the focus, and so the caret, in the search field.\n onMouseDown={(event) => event.preventDefault()}\n // Move, not enter: a list scrolling under a resting pointer must not steal the highlight.\n onMouseMove={() => !action.disabled && setActiveId(action.id)}\n onClick={() => run(action)}\n >\n {action.icon != null &&\n renderIcon(action.icon, {\n className: cx(styles.commandPaletteItemIcon, classNames?.itemIcon),\n style: slotStyles?.itemIcon,\n })}\n <span\n className={cx(styles.commandPaletteItemLabel, classNames?.itemLabel)}\n style={slotStyles?.itemLabel}\n >\n {action.label}\n </span>\n {action.shortcut != null && (\n <span\n className={cx(styles.commandPaletteItemShortcut, classNames?.itemShortcut)}\n style={slotStyles?.itemShortcut}\n >\n {formatHotkey(action.shortcut)}\n </span>\n )}\n </div>\n );\n })}\n </div>\n );\n })}\n </div>\n\n {hasQuery && actions.length === 0 && (\n <div className={cx(styles.commandPaletteEmpty, classNames?.empty)} style={slotStyles?.empty}>\n {emptyText}\n </div>\n )}\n <span className={styles.commandPaletteStatus} role='status' aria-live='polite'>\n {hasQuery ? (actions.length > 0 ? text.results(actions.length) : emptyText) : ''}\n </span>\n </Modal>\n );\n}\n\nexport default CommandPalette;\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use client";var e=`commandPalette_YtQdV`,t=`commandPalette-search_FII6k`,n=`commandPalette-search-icon_acfcm`,r=`commandPalette-input_Vb3da`,i=`commandPalette-list_93yGm`,a=`commandPalette-item_oa2PX`,o=`commandPalette-item-active_9KaV-`,s=`commandPalette-item-danger_VP4If`,c=`commandPalette-item-disabled_hiHzA`,l=`commandPalette-item-icon_N4L26`,u=`commandPalette-item-label_cy-ke`,d=`commandPalette-item-shortcut_G2KPh`,f=`commandPalette-heading_ONebI`,p=`commandPalette-separator_Yu8TJ`,m=`commandPalette-empty_LqVAt`,h=`commandPalette-status_rft-N`,g={commandPalette:e,"commandPalette-search":`commandPalette-search_FII6k`,commandPaletteSearch:t,"commandPalette-search-icon":`commandPalette-search-icon_acfcm`,commandPaletteSearchIcon:n,"commandPalette-input":`commandPalette-input_Vb3da`,commandPaletteInput:r,"commandPalette-list":`commandPalette-list_93yGm`,commandPaletteList:i,"commandPalette-item":`commandPalette-item_oa2PX`,commandPaletteItem:a,"commandPalette-item-active":`commandPalette-item-active_9KaV-`,commandPaletteItemActive:o,"commandPalette-item-danger":`commandPalette-item-danger_VP4If`,commandPaletteItemDanger:s,"commandPalette-item-disabled":`commandPalette-item-disabled_hiHzA`,commandPaletteItemDisabled:c,"commandPalette-item-icon":`commandPalette-item-icon_N4L26`,commandPaletteItemIcon:l,"commandPalette-item-label":`commandPalette-item-label_cy-ke`,commandPaletteItemLabel:u,"commandPalette-item-shortcut":`commandPalette-item-shortcut_G2KPh`,commandPaletteItemShortcut:d,"commandPalette-heading":`commandPalette-heading_ONebI`,commandPaletteHeading:f,"commandPalette-separator":`commandPalette-separator_Yu8TJ`,commandPaletteSeparator:p,"commandPalette-empty":`commandPalette-empty_LqVAt`,commandPaletteEmpty:m,"commandPalette-status":`commandPalette-status_rft-N`,commandPaletteStatus:h};export{e as commandPalette,m as commandPaletteEmpty,f as commandPaletteHeading,r as commandPaletteInput,a as commandPaletteItem,o as commandPaletteItemActive,s as commandPaletteItemDanger,c as commandPaletteItemDisabled,l as commandPaletteItemIcon,u as commandPaletteItemLabel,d as commandPaletteItemShortcut,i as commandPaletteList,t as commandPaletteSearch,n as commandPaletteSearchIcon,p as commandPaletteSeparator,h as commandPaletteStatus,g as default};
2
+ //# sourceMappingURL=commandPalette.module.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandPalette.module.mjs","names":[],"sources":["../../../../package/common/commandPalette/commandPalette.module.pcss"],"sourcesContent":[".commandPalette {\n --modal-padding: var(--spacing-s);\n --modal-body-bleed: 0;\n\n width: 640px;\n\n /* Near the top rather than centred, so the panel does not jump as the list shrinks while\n typing. A sheet overrides both with its own doubled selector. */\n align-self: flex-start;\n margin-top: 12vh;\n\n &-search {\n display: flex;\n align-items: center;\n gap: var(--spacing-s);\n padding: var(--spacing-s) var(--spacing-m);\n border-bottom: 1px solid var(--border-color);\n\n &-icon {\n flex: none;\n color: var(--text-light);\n }\n }\n\n &-input {\n flex: 1;\n min-width: 0;\n padding: 4px 0;\n border: none;\n outline: none;\n background: transparent;\n font: inherit;\n font-size: 1.1em;\n color: var(--text-color);\n\n &::placeholder {\n color: var(--text-light);\n }\n }\n\n &-list {\n max-height: min(24rem, 60vh);\n max-height: min(24rem, 60dvh);\n overflow-y: auto;\n padding: 4px 0;\n\n &:empty {\n display: none;\n }\n }\n\n &-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n border-radius: var(--default-border-radius);\n color: var(--text-color);\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* Same token as Menu's highlight: `--background-accent-light` matches the modal surface in\n the default theme. */\n &-active {\n background: var(--background-accent);\n }\n\n &-danger {\n color: var(--error-color);\n }\n\n &-disabled {\n opacity: 0.45;\n cursor: default;\n }\n\n &-icon {\n flex: none;\n width: 1em;\n text-align: center;\n }\n\n &-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &-shortcut {\n flex: none;\n margin-left: 16px;\n font-size: 0.85em;\n color: var(--text-light);\n }\n }\n\n &-heading {\n padding: 10px 12px 4px;\n font-size: 0.8em;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--text-light);\n }\n\n &-separator {\n height: 1px;\n margin: 4px 0;\n background: var(--border-color);\n }\n\n &-empty {\n padding: var(--spacing-l) var(--spacing-m);\n text-align: center;\n color: var(--text-light);\n }\n\n &-status {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use client";import{isMenuAction as e,isMenuHeading as t}from"../menu/menu.types.mjs";function n(e){return e.normalize(`NFD`).replace(/\p{Diacritic}/gu,``).toLowerCase()}function r(n){let r=[],i={separated:!1,actions:[]},a=()=>{(i.heading!==void 0||i.actions.length>0)&&r.push(i)};for(let r of n){if(e(r)){i.actions.push(r);continue}a(),i=t(r)?{heading:r.heading,separated:i.separated&&i.actions.length===0,actions:[]}:{separated:!0,actions:[]}}return a(),r.filter(e=>e.actions.length>0)}function i(e,t,r){let i=typeof e.label==`string`?n(e.label):``,a=(e.keywords??[]).map(n),o=[i,...a].join(` `);if(!t.every(e=>o.includes(e)))return null;if(i.startsWith(r))return 0;let s=e=>e.split(/\s+/).some(e=>e.startsWith(t[0]));return s(i)||a.some(s)?1:2}function a(e,t){let r=n(t.trim());if(!r)return e;let a=r.split(/\s+/);return e.map(e=>{let t=e.actions.map(e=>({action:e,rank:i(e,a,r)})).filter(e=>e.rank!==null).sort((e,t)=>e.rank-t.rank);return t.length===0?null:{group:{...e,separated:!1,actions:t.map(e=>e.action)},best:t[0].rank}}).filter(e=>e!==null).sort((e,t)=>e.best-t.best).map(e=>e.group)}export{a as filterCommands,r as groupCommands};
2
+ //# sourceMappingURL=filterCommands.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterCommands.mjs","names":[],"sources":["../../../../package/common/commandPalette/filterCommands.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { isMenuAction, isMenuHeading, type MenuAction, type MenuEntry } from '../menu/menu.types';\n\nexport interface CommandGroup {\n heading?: ReactNode;\n /** Whether a separator started this group, so the divider can be drawn above it. */\n separated: boolean;\n actions: MenuAction[];\n}\n\n// Lower-cased and stripped of accents, so \"uber\" finds \"Übersicht\".\nfunction normalize(text: string): string {\n return text\n .normalize('NFD')\n .replace(/\\p{Diacritic}/gu, '')\n .toLowerCase();\n}\n\n/** Splits entries into groups at each heading and separator, keeping their order. */\nexport function groupCommands(items: MenuEntry[]): CommandGroup[] {\n const groups: CommandGroup[] = [];\n let current: CommandGroup = { separated: false, actions: [] };\n const flush = () => {\n if (current.heading !== undefined || current.actions.length > 0) groups.push(current);\n };\n for (const entry of items) {\n if (isMenuAction(entry)) {\n current.actions.push(entry);\n continue;\n }\n flush();\n current = isMenuHeading(entry)\n ? { heading: entry.heading, separated: current.separated && current.actions.length === 0, actions: [] }\n : { separated: true, actions: [] };\n }\n flush();\n return groups.filter((group) => group.actions.length > 0);\n}\n\n/**\n * 0 when the label starts with the query, 1 when a word of the label or a keyword does, 2 for any\n * other match, `null` when a query word is missing from both the label and the keywords.\n *\n * A label that is not a string cannot be read, so such an action is found through its keywords only.\n */\nfunction rank(action: MenuAction, words: string[], query: string): number | null {\n const label = typeof action.label === 'string' ? normalize(action.label) : '';\n const keywords = (action.keywords ?? []).map(normalize);\n const haystack = [label, ...keywords].join(' ');\n if (!words.every((word) => haystack.includes(word))) return null;\n if (label.startsWith(query)) return 0;\n const hasWordStartingWith = (text: string) => text.split(/\\s+/).some((word) => word.startsWith(words[0]));\n if (hasWordStartingWith(label) || keywords.some(hasWordStartingWith)) return 1;\n return 2;\n}\n\ninterface RankedGroup {\n group: CommandGroup;\n best: number;\n}\n\n/**\n * The groups with only the actions matching `query`, best matches first within each group, and\n * the group holding the best match first. A heading moves with its group, never away from it.\n */\nexport function filterCommands(groups: CommandGroup[], query: string): CommandGroup[] {\n const normalized = normalize(query.trim());\n if (!normalized) return groups;\n const words = normalized.split(/\\s+/);\n\n // Both sorts rely on `Array.prototype.sort` being stable, so ties keep the order they were given in.\n return groups\n .map((group): RankedGroup | null => {\n const matches = group.actions\n .map((action) => ({ action, rank: rank(action, words, normalized) }))\n .filter((match): match is { action: MenuAction; rank: number } => match.rank !== null)\n .sort((a, b) => a.rank - b.rank);\n if (matches.length === 0) return null;\n return {\n // Separators are dropped while filtering: the headings still say where one group ends.\n group: { ...group, separated: false, actions: matches.map((match) => match.action) },\n best: matches[0].rank,\n };\n })\n .filter((ranked): ranked is RankedGroup => ranked !== null)\n .sort((a, b) => a.best - b.best)\n .map((ranked) => ranked.group);\n}\n"],"mappings":"sFAWA,SAAS,EAAU,EAAsB,CACvC,OAAO,EACJ,UAAU,KAAK,CAAC,CAChB,QAAQ,kBAAmB,EAAE,CAAC,CAC9B,YAAY,CACjB,CAGA,SAAgB,EAAc,EAAoC,CAChE,IAAM,EAAyB,CAAC,EAC5B,EAAwB,CAAE,UAAW,GAAO,QAAS,CAAC,CAAE,EACtD,MAAc,EACd,EAAQ,UAAY,IAAA,IAAa,EAAQ,QAAQ,OAAS,IAAG,EAAO,KAAK,CAAO,CACtF,EACA,IAAK,IAAM,KAAS,EAAO,CACzB,GAAI,EAAa,CAAK,EAAG,CACvB,EAAQ,QAAQ,KAAK,CAAK,EAC1B,QACF,CACA,EAAM,EACN,EAAU,EAAc,CAAK,EACzB,CAAE,QAAS,EAAM,QAAS,UAAW,EAAQ,WAAa,EAAQ,QAAQ,SAAW,EAAG,QAAS,CAAC,CAAE,EACpG,CAAE,UAAW,GAAM,QAAS,CAAC,CAAE,CACrC,CAEA,OADA,EAAM,EACC,EAAO,OAAQ,GAAU,EAAM,QAAQ,OAAS,CAAC,CAC1D,CAQA,SAAS,EAAK,EAAoB,EAAiB,EAA8B,CAC/E,IAAM,EAAQ,OAAO,EAAO,OAAU,SAAW,EAAU,EAAO,KAAK,EAAI,GACrE,GAAY,EAAO,UAAY,CAAC,EAAA,CAAG,IAAI,CAAS,EAChD,EAAW,CAAC,EAAO,GAAG,CAAQ,CAAC,CAAC,KAAK,GAAG,EAC9C,GAAI,CAAC,EAAM,MAAO,GAAS,EAAS,SAAS,CAAI,CAAC,EAAG,OAAO,KAC5D,GAAI,EAAM,WAAW,CAAK,EAAG,MAAO,GACpC,IAAM,EAAuB,GAAiB,EAAK,MAAM,KAAK,CAAC,CAAC,KAAM,GAAS,EAAK,WAAW,EAAM,EAAE,CAAC,EAExG,OADI,EAAoB,CAAK,GAAK,EAAS,KAAK,CAAmB,EAAU,EACtE,CACT,CAWA,SAAgB,EAAe,EAAwB,EAA+B,CACpF,IAAM,EAAa,EAAU,EAAM,KAAK,CAAC,EACzC,GAAI,CAAC,EAAY,OAAO,EACxB,IAAM,EAAQ,EAAW,MAAM,KAAK,EAGpC,OAAO,EACJ,IAAK,GAA8B,CAClC,IAAM,EAAU,EAAM,QACnB,IAAK,IAAY,CAAE,SAAQ,KAAM,EAAK,EAAQ,EAAO,CAAU,CAAE,EAAE,CAAC,CACpE,OAAQ,GAAyD,EAAM,OAAS,IAAI,CAAC,CACrF,MAAM,EAAG,IAAM,EAAE,KAAO,EAAE,IAAI,EAEjC,OADI,EAAQ,SAAW,EAAU,KAC1B,CAEL,MAAO,CAAE,GAAG,EAAO,UAAW,GAAO,QAAS,EAAQ,IAAK,GAAU,EAAM,MAAM,CAAE,EACnF,KAAM,EAAQ,EAAE,CAAC,IACnB,CACF,CAAC,CAAC,CACD,OAAQ,GAAkC,IAAW,IAAI,CAAC,CAC1D,MAAM,EAAG,IAAM,EAAE,KAAO,EAAE,IAAI,CAAC,CAC/B,IAAK,GAAW,EAAO,KAAK,CACjC"}
@@ -0,0 +1,2 @@
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{isIconDefinition as n,renderIcon as r}from"../utils/renderIcon.mjs";import i from"../actionIcon/actionIcon.mjs";import a from"../animatedIcon/animatedIcon.mjs";import o from"../hooks/useClipboard.mjs";import s from"./copyButton.module.mjs";import c from"classnames";import{faCheck as l,faCopy as u}from"@fortawesome/free-solid-svg-icons";import{Fragment as d,jsx as f,jsxs as p}from"react/jsx-runtime";var m={copy:`Copy`,copied:`Copied`,failed:`Could not copy`};function h(h){let{value:g,timeout:_=2e3,icon:v=u,copiedIcon:y=l,onCopy:b,onError:x,onClick:S,labels:C,className:w,style:T,classNames:E,styles:D,...O}=e(`CopyButton`,h),k=t(`CopyButton`,C,m),{copy:A,copied:j,error:M}=o({timeout:_}),N=j?k.copied:k.copy,P=j?y:v,F=n(v)&&n(y)?f(a,{icon:j?y:v,classNames:{icon:E?.icon},styles:{icon:D?.icon}}):r(P,{className:E?.icon,style:D?.icon});return p(d,{children:[f(i,{...O,icon:f(d,{children:F}),title:N,"aria-label":N,className:c(s.copyButton,j&&s.copyButtonCopied,w),style:T,classNames:{root:E?.root},styles:{root:D?.root},onClick:async e=>{S?.(e),await A(g)?b?.(g):x?.()}}),f(`span`,{className:s.copyButtonStatus,role:`status`,"aria-live":`polite`,children:j?k.copied:M?k.failed:``})]})}export{h as default};
2
+ //# sourceMappingURL=copyButton.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copyButton.mjs","names":[],"sources":["../../../../package/common/copyButton/copyButton.tsx"],"sourcesContent":["import type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faCheck, faCopy } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport AnimatedIcon from '../animatedIcon';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useClipboard } from '../hooks/useClipboard';\nimport { isIconDefinition, renderIcon } from '../utils/renderIcon';\nimport type { CopyButtonLabels, CopyButtonProps } from './copyButton.types';\nimport styles from './copyButton.module.pcss';\n\nconst DEFAULT_LABELS: Required<CopyButtonLabels> = {\n copy: 'Copy',\n copied: 'Copied',\n failed: 'Could not copy',\n};\n\nfunction CopyButton(props: CopyButtonProps): ReactElement {\n const {\n value,\n timeout = 2000,\n icon = faCopy,\n copiedIcon = faCheck,\n onCopy,\n onError,\n onClick,\n labels,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('CopyButton', props);\n const text = useLabels('CopyButton', labels, DEFAULT_LABELS);\n const { copy, copied, error } = useClipboard({ timeout });\n\n const label = copied ? text.copied : text.copy;\n const current = copied ? copiedIcon : icon;\n const iconElement =\n isIconDefinition(icon) && isIconDefinition(copiedIcon) ? (\n <AnimatedIcon\n icon={copied ? copiedIcon : icon}\n classNames={{ icon: classNames?.icon }}\n styles={{ icon: slotStyles?.icon }}\n />\n ) : (\n renderIcon(current, { className: classNames?.icon, style: slotStyles?.icon })\n );\n\n return (\n <>\n <ActionIcon\n {...rest}\n icon={<>{iconElement}</>}\n title={label}\n aria-label={label}\n className={cx(styles.copyButton, copied && styles.copyButtonCopied, className)}\n style={style}\n classNames={{ root: classNames?.root }}\n styles={{ root: slotStyles?.root }}\n onClick={async (event) => {\n onClick?.(event);\n if (await copy(value)) onCopy?.(value);\n else onError?.();\n }}\n />\n {/* Beside the button, not in it: a live region inside role=\"button\" is not reliably announced. */}\n <span className={styles.copyButtonStatus} role='status' aria-live='polite'>\n {copied ? text.copied : error ? text.failed : ''}\n </span>\n </>\n );\n}\n\nexport default CopyButton;\n"],"mappings":"ojBAYA,IAAM,EAA6C,CACjD,KAAM,OACN,OAAQ,SACR,OAAQ,gBACV,EAEA,SAAS,EAAW,EAAsC,CACxD,GAAM,CACJ,QACA,UAAU,IACV,OAAO,EACP,aAAa,EACb,SACA,UACA,UACA,SACA,YACA,QACA,aACA,OAAQ,EACR,GAAG,GACD,EAAqB,aAAc,CAAK,EACtC,EAAO,EAAU,aAAc,EAAQ,CAAc,EACrD,CAAE,OAAM,SAAQ,SAAU,EAAa,CAAE,SAAQ,CAAC,EAElD,EAAQ,EAAS,EAAK,OAAS,EAAK,KACpC,EAAU,EAAS,EAAa,EAChC,EACJ,EAAiB,CAAI,GAAK,EAAiB,CAAU,EACnD,EAAC,EAAD,CACE,KAAM,EAAS,EAAa,EAC5B,WAAY,CAAE,KAAM,GAAY,IAAK,EACrC,OAAQ,CAAE,KAAM,GAAY,IAAK,CAClC,CAAA,EAED,EAAW,EAAS,CAAE,UAAW,GAAY,KAAM,MAAO,GAAY,IAAK,CAAC,EAGhF,OACE,EAAA,EAAA,CAAA,SAAA,CACE,EAAC,EAAD,CACE,GAAI,EACJ,KAAM,EAAA,EAAA,CAAA,SAAG,CAAc,CAAA,EACvB,MAAO,EACP,aAAY,EACZ,UAAW,EAAG,EAAO,WAAY,GAAU,EAAO,iBAAkB,CAAS,EACtE,QACP,WAAY,CAAE,KAAM,GAAY,IAAK,EACrC,OAAQ,CAAE,KAAM,GAAY,IAAK,EACjC,QAAS,KAAO,IAAU,CACxB,IAAU,CAAK,EACX,MAAM,EAAK,CAAK,EAAG,IAAS,CAAK,EAChC,IAAU,CACjB,CACD,CAAA,EAED,EAAC,OAAD,CAAM,UAAW,EAAO,iBAAkB,KAAK,SAAS,YAAU,SAC/D,SAAA,EAAS,EAAK,OAAS,EAAQ,EAAK,OAAS,EAC1C,CAAA,CACN,CAAA,CAAA,CAEN"}
@@ -0,0 +1,2 @@
1
+ "use client";var e=`copyButton_HEd1K`,t=`copyButton-copied_HL1zN`,n=`copyButton-status_QdwYf`,r={copyButton:e,"copyButton-copied":`copyButton-copied_HL1zN`,copyButtonCopied:t,"copyButton-status":`copyButton-status_QdwYf`,copyButtonStatus:n};export{e as copyButton,t as copyButtonCopied,n as copyButtonStatus,r as default};
2
+ //# sourceMappingURL=copyButton.module.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copyButton.module.mjs","names":[],"sources":["../../../../package/common/copyButton/copyButton.module.pcss"],"sourcesContent":[".copyButton {\n /* As a block, AnimatedIcon's inline-block box sits on a text baseline and rides high. */\n display: inline-flex;\n\n &-copied {\n color: var(--success-color);\n }\n\n &-status {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n }\n}\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{usePresentationDefault as t}from"../configProvider/usePresentationDefault.mjs";import{Presentation as n}from"../configProvider/presentation.types.mjs";import r from"../loading/spinnerLoading/index.mjs";import i from"../styles/sheet.module.mjs";import{PopoverAlign as ee,PopoverPlacement as te}from"../popover/popover.types.mjs";import ne from"../hooks/useSheet.mjs";import re from"../popover/popover.mjs";import ie from"../tooltip/tooltip.mjs";import{useInfiniteScroll as ae}from"../infiniteBlock/useInfiniteScroll.mjs";import{edgeIndex as oe,nextIndex as se,scrollOptionIntoView as ce}from"../utils/listNavigation.mjs";import a from"./dropdown.module.mjs";import{useEffect as o,useId as le,useLayoutEffect as ue,useRef as s,useState as c}from"react";import l from"classnames";import{FontAwesomeIcon as de}from"@fortawesome/react-fontawesome";import{faChevronDown as fe}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var pe=500;function f(f){let{errorMessage:p=``,className:me=``,style:he,label:m=``,value:ge=``,isValid:_e=!0,tabIndex:ve=0,isRequired:ye=!1,customProps:h={},isRow:be=!1,disabled:g=!1,readOnly:_=!1,onScroll:xe=()=>Promise.resolve(),onClick:v,onChange:y,onFocus:Se=null,onBlur:Ce=null,onMouseEnter:we=null,onMouseLeave:Te=null,dropdownLabel:Ee,isDropdownListWider:b=!1,formValidator:x=null,classNames:S,styles:C,items:w=[],useMatLabelStyle:T=!0,alwaysFloatLabel:E=!1,placeholder:D=``,plainStyle:O=!1,scrollToSelected:De=!1,presentation:Oe,reactRef:ke,...Ae}=e(`Dropdown`,f),[je,Me]=c(!1),[Ne,Pe]=c(!1),[,Fe]=c(0),k=s(x),A=s(null),j=s(null),M=s(null),[Ie,Le]=c(!1),[N,P]=c(null),F=s({buffer:``,at:0}),I=le(),L=`${I}-listbox`,R=`${I}-label`,z=e=>`${I}-option-${e}`,B=!g&&!_,V=Ie&&B,{isLoading:Re,sentinelRef:ze}=ae(M,xe,!1),Be=t(),H=Oe??Be??n.Default,{isSheet:U}=ne(H);ue(()=>{let e=A.current,t=j.current;V&&!U&&e&&t&&(t.style.fontSize=window.getComputedStyle(e).fontSize)},[V,U]),o(()=>{if(k.current=x,!k.current)return;k.current.label=m??``,k.current.validate();let e=()=>Fe(e=>e+1);return k.current.registerOnUpdateListener(e),()=>k.current?.removeOnUpdateListener(e)},[x,m]);let W=x?x.value:ge,G=w.findIndex(e=>e.key===W);o(()=>{V&&N!==null&&ce(M.current,document.getElementById(z(N)))},[V,N]);let Ve=e=>{Pe(!0),we?.(e)},He=e=>{Pe(!1),Te?.(e)},Ue=e=>{Me(!0),Se?.(e)},We=e=>{k.current&&(k.current.touched=!0),Me(!1),Ce?.(e)},Ge=()=>x?x.touched&&x.hasError():!_e||!!p,K=()=>!!(ye||x?.validators.find(e=>e.validatorId===`required`)),Ke=e=>{x?(x.set(e),y?.(x.value,h)):y?.(e,h)};function q(e){(!e||B)&&(Le(e),e?P(De&&G>=0?G:null):F.current={buffer:``,at:0})}function J(e){e.disabled||(Ke(e.key),q(!1))}let qe={isDisabled:e=>!!w[e].disabled};function Je(e){let t=Date.now(),n=t-F.current.at>pe?e:F.current.buffer+e;F.current={buffer:n,at:t};let r=n.toLowerCase(),i=w.findIndex(e=>!e.disabled&&typeof e.label==`string`&&e.label.toLowerCase().startsWith(r));i!==-1&&(V?P(i):J(w[i]))}function Ye(e){if(B){if(!V){e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`||e.key===` `?(e.preventDefault(),q(!0)):e.key.length===1&&Je(e.key);return}if(e.key===`ArrowDown`||e.key===`ArrowUp`)e.preventDefault(),P(se(N,e.key===`ArrowDown`?1:-1,w.length,qe));else if(e.key===`Home`||e.key===`End`)e.preventDefault(),P(oe(e.key===`Home`?`first`:`last`,w.length,qe));else if(e.key===`Enter`||e.key===` `){e.preventDefault();let t=N===null?void 0:w[N];t?J(t):q(!1)}else e.key===`Escape`||e.key===`Tab`?q(!1):e.key.length===1&&Je(e.key)}}let Y=Ee,X=Ge();w.forEach(e=>{e.key===W&&(Y=e.label)});let Z=Y||W,Q=Z==null||typeof Z==`string`&&Z.trim()===``,Xe=K()?u(`span`,{className:a.requiredMark,children:`*`}):null,Ze=m&&K()?`${m} *`:m,$=je&&!_;return d(`div`,{...Ae,className:l(me,S?.root,a.dropdownLabel,a.dropdown,{[a.dropdownLabelColumn]:!be}),style:{...he,...C?.root},onMouseEnter:Ve,onMouseLeave:He,children:[u(`div`,{children:u(re,{isOpen:V,onOpenChange:q,disabled:!B,placement:te.Bottom,align:ee.Start,offset:0,withArrow:!1,constrainWidth:!1,width:`trigger`,focusOnOpen:!1,presentation:H,classNames:{panel:a.dropdownPanel},content:u(`div`,{ref:j,className:l(!U&&a.dropdownInputListAnim,{[a.dropdownInputListAnimWider]:b&&!U}),children:d(`div`,{ref:M,id:L,role:`listbox`,"aria-label":m||D||void 0,className:l(a.dropdownInputList,U&&i.sheetScroll,S?.list),style:C?.list,children:[w.map((e,t)=>u(`div`,{id:z(t),role:`option`,"aria-selected":e.key===W,"aria-disabled":e.disabled||void 0,style:C?.option,className:l(a.dropdownInputListItem,S?.option,{[a.dropdownInputListItemActive]:N===t,[a.dropdownInputListItemSelected]:e.key===W,[a.dropdownInputListItemDisabled]:!!e.disabled}),onMouseDown:e=>e.preventDefault(),onMouseEnter:()=>P(t),onClick:()=>J(e),children:e.label},e.key)),u(`div`,{ref:ze,className:a.dropdownSentinel,"aria-hidden":`true`}),Re&&u(`div`,{className:a.dropdownLoadMore,"aria-hidden":`true`,children:u(r,{className:a.dropdownLoadMoreSpinner})})]})}),children:d(`div`,{ref:A,onFocus:Ue,onBlur:We,className:l(a.dropdownInput,{[a.dropdownInputPlain]:O,[a.dropdownInputReadOnly]:_&&!g},S?.field),style:C?.field,children:[u(`button`,{tabIndex:ve,type:`button`,disabled:g,role:`combobox`,"aria-haspopup":`listbox`,"aria-expanded":V,"aria-controls":L,"aria-activedescendant":V&&N!==null?z(N):void 0,"aria-labelledby":m?R:void 0,"aria-label":m?void 0:D||void 0,"aria-readonly":_||void 0,className:l(a.dropdownInputButton,{[a.dropdownInputButtonDisabled]:g,[a.dropdownInputButtonReadOnly]:_&&!g},S?.control),style:{...b?{padding:`6px`,border:`none`}:{},...C?.control},onKeyDown:Ye,onClick:v,ref:ke,children:Q?`\xA0`:Z}),u(`button`,{type:`button`,tabIndex:-1,disabled:g,"aria-hidden":`true`,className:l(a.dropdownInputButtonIcon,S?.icon),style:C?.icon,onMouseDown:e=>e.preventDefault(),onClick:v,children:!g&&!_&&u(de,{icon:fe,className:l(a.dropdownInputButtonIconArrow,{[a.dropdownInputButtonIconArrowOpen]:V})})}),u(`fieldset`,{className:l(a.dropdownFieldset,{[a.dropdownBorderPlain]:O,[a.dropdownBorderFocused]:$,[a.dropdownBorderInvalid]:X,[a.dropdownBorderError]:X}),children:u(`legend`,{className:l(a.dropdownFieldsetLegend,{[a.dropdownFieldsetLegendActive]:T&&(E||$||!Q)}),children:u(`span`,{children:Ze})})}),T&&m&&d(`div`,{id:R,className:l(a.dropdownMatLabel,{[a.dropdownMatLabelFloating]:E||$||!Q},S?.label),style:C?.label,children:[m,Xe]}),T&&D&&u(`div`,{className:l(a.dropdownPlaceholder,$&&!Y&&a.dropdownPlaceholderShown),children:D})]})})}),m&&!T&&d(`div`,{id:R,className:l(a.dropdownLabelText,S?.label),style:C?.label,children:[m,Xe]}),u(ie,{variant:`error`,message:x?x.getCurrentErrorMessage():p||``,isVisible:X&&(je||Ne)})]})}export{f as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{usePresentationDefault as t}from"../configProvider/usePresentationDefault.mjs";import{Presentation as n}from"../configProvider/presentation.types.mjs";import r from"../loading/spinnerLoading/index.mjs";import i from"../styles/sheet.module.mjs";import{PopoverAlign as ee,PopoverPlacement as te}from"../popover/popover.types.mjs";import ne from"../hooks/useSheet.mjs";import re from"../popover/popover.mjs";import ie from"../tooltip/tooltip.mjs";import{edgeIndex as ae,nextIndex as oe,scrollOptionIntoView as se}from"../utils/listNavigation.mjs";import{useInfiniteScroll as ce}from"../infiniteBlock/useInfiniteScroll.mjs";import a from"./dropdown.module.mjs";import{useEffect as o,useId as le,useLayoutEffect as ue,useRef as s,useState as c}from"react";import l from"classnames";import{FontAwesomeIcon as de}from"@fortawesome/react-fontawesome";import{faChevronDown as fe}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var pe=500;function f(f){let{errorMessage:p=``,className:me=``,style:he,label:m=``,value:ge=``,isValid:_e=!0,tabIndex:ve=0,isRequired:ye=!1,customProps:h={},isRow:be=!1,disabled:g=!1,readOnly:_=!1,onScroll:xe=()=>Promise.resolve(),onClick:v,onChange:y,onFocus:Se=null,onBlur:Ce=null,onMouseEnter:we=null,onMouseLeave:Te=null,dropdownLabel:Ee,isDropdownListWider:b=!1,formValidator:x=null,classNames:S,styles:C,items:w=[],useMatLabelStyle:T=!0,alwaysFloatLabel:E=!1,placeholder:D=``,plainStyle:O=!1,scrollToSelected:De=!1,presentation:Oe,reactRef:ke,...Ae}=e(`Dropdown`,f),[je,Me]=c(!1),[Ne,Pe]=c(!1),[,Fe]=c(0),k=s(x),A=s(null),j=s(null),M=s(null),[Ie,Le]=c(!1),[N,P]=c(null),F=s({buffer:``,at:0}),I=le(),L=`${I}-listbox`,R=`${I}-label`,z=e=>`${I}-option-${e}`,B=!g&&!_,V=Ie&&B,{isLoading:Re,sentinelRef:ze}=ce(M,xe,!1),Be=t(),H=Oe??Be??n.Default,{isSheet:U}=ne(H);ue(()=>{let e=A.current,t=j.current;V&&!U&&e&&t&&(t.style.fontSize=window.getComputedStyle(e).fontSize)},[V,U]),o(()=>{if(k.current=x,!k.current)return;k.current.label=m??``,k.current.validate();let e=()=>Fe(e=>e+1);return k.current.registerOnUpdateListener(e),()=>k.current?.removeOnUpdateListener(e)},[x,m]);let W=x?x.value:ge,G=w.findIndex(e=>e.key===W);o(()=>{V&&N!==null&&se(M.current,document.getElementById(z(N)))},[V,N]);let Ve=e=>{Pe(!0),we?.(e)},He=e=>{Pe(!1),Te?.(e)},Ue=e=>{Me(!0),Se?.(e)},We=e=>{k.current&&(k.current.touched=!0),Me(!1),Ce?.(e)},Ge=()=>x?x.touched&&x.hasError():!_e||!!p,K=()=>!!(ye||x?.validators.find(e=>e.validatorId===`required`)),Ke=e=>{x?(x.set(e),y?.(x.value,h)):y?.(e,h)};function q(e){(!e||B)&&(Le(e),e?P(De&&G>=0?G:null):F.current={buffer:``,at:0})}function J(e){e.disabled||(Ke(e.key),q(!1))}let qe={isDisabled:e=>!!w[e].disabled};function Je(e){let t=Date.now(),n=t-F.current.at>pe?e:F.current.buffer+e;F.current={buffer:n,at:t};let r=n.toLowerCase(),i=w.findIndex(e=>!e.disabled&&typeof e.label==`string`&&e.label.toLowerCase().startsWith(r));i!==-1&&(V?P(i):J(w[i]))}function Ye(e){if(B){if(!V){e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`||e.key===` `?(e.preventDefault(),q(!0)):e.key.length===1&&Je(e.key);return}if(e.key===`ArrowDown`||e.key===`ArrowUp`)e.preventDefault(),P(oe(N,e.key===`ArrowDown`?1:-1,w.length,qe));else if(e.key===`Home`||e.key===`End`)e.preventDefault(),P(ae(e.key===`Home`?`first`:`last`,w.length,qe));else if(e.key===`Enter`||e.key===` `){e.preventDefault();let t=N===null?void 0:w[N];t?J(t):q(!1)}else e.key===`Escape`||e.key===`Tab`?q(!1):e.key.length===1&&Je(e.key)}}let Y=Ee,X=Ge();w.forEach(e=>{e.key===W&&(Y=e.label)});let Z=Y||W,Q=Z==null||typeof Z==`string`&&Z.trim()===``,Xe=K()?u(`span`,{className:a.requiredMark,children:`*`}):null,Ze=m&&K()?`${m} *`:m,$=je&&!_;return d(`div`,{...Ae,className:l(me,S?.root,a.dropdownLabel,a.dropdown,{[a.dropdownLabelColumn]:!be}),style:{...he,...C?.root},onMouseEnter:Ve,onMouseLeave:He,children:[u(`div`,{children:u(re,{isOpen:V,onOpenChange:q,disabled:!B,placement:te.Bottom,align:ee.Start,offset:0,withArrow:!1,constrainWidth:!1,width:`trigger`,focusOnOpen:!1,presentation:H,classNames:{panel:a.dropdownPanel},content:u(`div`,{ref:j,className:l(!U&&a.dropdownInputListAnim,{[a.dropdownInputListAnimWider]:b&&!U}),children:d(`div`,{ref:M,id:L,role:`listbox`,"aria-label":m||D||void 0,className:l(a.dropdownInputList,U&&i.sheetScroll,S?.list),style:C?.list,children:[w.map((e,t)=>u(`div`,{id:z(t),role:`option`,"aria-selected":e.key===W,"aria-disabled":e.disabled||void 0,style:C?.option,className:l(a.dropdownInputListItem,S?.option,{[a.dropdownInputListItemActive]:N===t,[a.dropdownInputListItemSelected]:e.key===W,[a.dropdownInputListItemDisabled]:!!e.disabled}),onMouseDown:e=>e.preventDefault(),onMouseEnter:()=>P(t),onClick:()=>J(e),children:e.label},e.key)),u(`div`,{ref:ze,className:a.dropdownSentinel,"aria-hidden":`true`}),Re&&u(`div`,{className:a.dropdownLoadMore,"aria-hidden":`true`,children:u(r,{className:a.dropdownLoadMoreSpinner})})]})}),children:d(`div`,{ref:A,onFocus:Ue,onBlur:We,className:l(a.dropdownInput,{[a.dropdownInputPlain]:O,[a.dropdownInputReadOnly]:_&&!g},S?.field),style:C?.field,children:[u(`button`,{tabIndex:ve,type:`button`,disabled:g,role:`combobox`,"aria-haspopup":`listbox`,"aria-expanded":V,"aria-controls":L,"aria-activedescendant":V&&N!==null?z(N):void 0,"aria-labelledby":m?R:void 0,"aria-label":m?void 0:D||void 0,"aria-readonly":_||void 0,className:l(a.dropdownInputButton,{[a.dropdownInputButtonDisabled]:g,[a.dropdownInputButtonReadOnly]:_&&!g},S?.control),style:{...b?{padding:`6px`,border:`none`}:{},...C?.control},onKeyDown:Ye,onClick:v,ref:ke,children:Q?`\xA0`:Z}),u(`button`,{type:`button`,tabIndex:-1,disabled:g,"aria-hidden":`true`,className:l(a.dropdownInputButtonIcon,S?.icon),style:C?.icon,onMouseDown:e=>e.preventDefault(),onClick:v,children:!g&&!_&&u(de,{icon:fe,className:l(a.dropdownInputButtonIconArrow,{[a.dropdownInputButtonIconArrowOpen]:V})})}),u(`fieldset`,{className:l(a.dropdownFieldset,{[a.dropdownBorderPlain]:O,[a.dropdownBorderFocused]:$,[a.dropdownBorderInvalid]:X,[a.dropdownBorderError]:X}),children:u(`legend`,{className:l(a.dropdownFieldsetLegend,{[a.dropdownFieldsetLegendActive]:T&&(E||$||!Q)}),children:u(`span`,{children:Ze})})}),T&&m&&d(`div`,{id:R,className:l(a.dropdownMatLabel,{[a.dropdownMatLabelFloating]:E||$||!Q},S?.label),style:C?.label,children:[m,Xe]}),T&&D&&u(`div`,{className:l(a.dropdownPlaceholder,$&&!Y&&a.dropdownPlaceholderShown),children:D})]})})}),m&&!T&&d(`div`,{id:R,className:l(a.dropdownLabelText,S?.label),style:C?.label,children:[m,Xe]}),u(ie,{variant:`error`,message:x?x.getCurrentErrorMessage():p||``,isVisible:X&&(je||Ne)})]})}export{f as default};
2
2
  //# sourceMappingURL=dropdown.mjs.map