@pearpages/pulp-react 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,6 +12,17 @@ npm install @pearpages/pulp-react @pearpages/pulp-css @pearpages/pulp-tokens
12
12
 
13
13
  Requires React 19 and `react-dom`. `@pearpages/pulp-tokens` is a peer dependency.
14
14
 
15
+ With pnpm 11, a release less than 24 hours old will not install: `minimumReleaseAge` defaults to
16
+ 1440 minutes, as a guard against a hijacked package reaching you at once. To take a pulp release
17
+ on the day it ships, exempt the scope in `pnpm-workspace.yaml`:
18
+
19
+ ```yaml
20
+ minimumReleaseAgeExclude:
21
+ - '@pearpages/*'
22
+ ```
23
+
24
+ npm and yarn have no such delay.
25
+
15
26
  ## Use
16
27
 
17
28
  Load the styles once:
@@ -45,16 +56,36 @@ theme everything inside the element they're on.
45
56
  - **Forms**: `Field`, `TextField`, `Textarea`, `Select`, `Checkbox`, `Switch`, `RadioGroup`,
46
57
  `Radio`, `Combobox`, `Listbox`, `Picker`, `Calendar`, `DatePicker`, `Slider`
47
58
  - **Navigation**: `Tabs`, `Accordion`, `Pagination`
48
- - **Overlays**: `Dialog`, `DialogSystem`, `Menu`, `Popover`, `Tooltip`
59
+ - **Overlays**: `Dialog`, `DialogSystem`, `Sheet`, `Menu`, `Popover`, `Tooltip`
49
60
  - **Feedback**: `Alert`, `Badge`, `ToastProvider` and `useToast`, `Progress`, `Skeleton`, `Spinner`
50
61
  - **Data**: `Table`
51
62
  - **Utilities**: `Icon`, `VisuallyHidden`
52
63
 
53
- `Combobox`, `Listbox`, `Picker`, `Calendar`, `DatePicker`, `Slider`, `Table` and `Pagination` are
54
- **experimental**: their API may still change in a minor release. All but `Pagination` build on
64
+ `Combobox`, `Listbox`, `Picker`, `Calendar`, `DatePicker`, `Slider`, `Table`, `Pagination` and
65
+ `Sheet` are **experimental**: their API may still change in a minor release. All but `Pagination` build on
55
66
  React Aria Components, installed with this package and tree-shaken per entry. Their props are
56
67
  pulp's, and dates cross the API as `YYYY-MM-DD` strings.
57
68
 
69
+ ## Dialogs need one more stylesheet
70
+
71
+ `Dialog`, `DialogSystem` and `Sheet` (a dialog docked to an edge) build on
72
+ [`@pearpages/modals`](https://www.npmjs.com/package/@pearpages/modals): the focus trap, the inert
73
+ page behind the dialog, stacking, Escape and backdrop dismissal are its work, and it is installed
74
+ with this package. pulp maps its own tokens onto the vendor's `--modal-*` variables, so a dialog
75
+ follows `data-brand` and `data-scheme` like everything else.
76
+
77
+ The vendor's stylesheet is the one you import yourself. Put it in the `vendor` layer, which
78
+ `@pearpages/pulp-css` declares below `components`, so pulp's styles win:
79
+
80
+ ```css
81
+ @import "@pearpages/pulp-css";
82
+ @import "@pearpages/modals/styles.css" layer(vendor);
83
+ @import "@pearpages/pulp-react/styles.css";
84
+ ```
85
+
86
+ Then mount `DialogSystem` once near the root. Without the stylesheet a dialog opens unstyled; if
87
+ you never render a `Dialog`, skip it.
88
+
58
89
  ## For tooling and coding agents
59
90
 
60
91
  `@pearpages/pulp-react/component-manifest.json` lists every component with its status, category,
@@ -1,5 +1,5 @@
1
- import { VisuallyHidden } from './chunk-Z4PN6B2C.js';
2
1
  import { selectionToIds } from './chunk-CZBCGLEM.js';
2
+ import { VisuallyHidden } from './chunk-Z4PN6B2C.js';
3
3
  import { classes } from './chunk-MI4QUKQS.js';
4
4
  import { createContext, useRef, useCallback, useState, useContext, useEffect } from 'react';
5
5
  import { Table as Table$1, useTableOptions, TableHeader as TableHeader$1, Column, TableBody as TableBody$1, Row, Cell, Checkbox } from 'react-aria-components';
@@ -112,5 +112,5 @@ Table.Row = TableRow;
112
112
  Table.Cell = TableCell;
113
113
 
114
114
  export { Table };
115
- //# sourceMappingURL=chunk-F3OFZGGE.js.map
116
- //# sourceMappingURL=chunk-F3OFZGGE.js.map
115
+ //# sourceMappingURL=chunk-7RQQASKS.js.map
116
+ //# sourceMappingURL=chunk-7RQQASKS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/table/Table.module.css","../src/table/Table.tsx"],"names":["container","table","header","column","row","cell","body","selectColumn","selectCell","selectBox","select","empty","AriaTable","AriaTableHeader","AriaColumn","AriaTableBody","AriaRow","AriaCell","AriaCheckbox"],"mappings":"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","file":"chunk-F3OFZGGE.js","sourcesContent":["/*\n * Every value is a token (--table-*). Density resolves the row padding into a\n * private custom property on the table. React Aria writes the state:\n * data-hovered, data-selected, data-disabled, data-focus-visible on rows and\n * cells; data-allows-sorting and data-sort-direction on columns;\n * data-selected and data-indeterminate on the selection checkbox.\n * Rules are ordered from general to specific so no later selector has a lower\n * specificity than an earlier one for the same element.\n */\n\n@layer components {\n .container {\n overflow: auto;\n border: var(--size-hairline) solid var(--table-border);\n border-radius: var(--table-radius);\n background-color: var(--table-bg);\n color: var(--table-fg);\n font-family: var(--table-font-family);\n font-size: var(--table-font-size);\n }\n\n .table {\n --_padding-block: var(--table-cell-padding-block-default);\n\n inline-size: 100%;\n border-collapse: collapse;\n border-spacing: 0;\n outline: none;\n\n &[data-density=\"compact\"] {\n --_padding-block: var(--table-cell-padding-block-compact);\n }\n\n &[data-focus-visible] {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: calc(-1 * var(--table-focus-ring-width));\n }\n }\n\n .header {\n background-color: var(--table-header-bg);\n }\n\n .container[data-sticky-header] .header {\n position: sticky;\n inset-block-start: 0;\n }\n\n .column {\n padding-block: var(--_padding-block);\n padding-inline: var(--table-cell-padding-inline);\n border-block-end: var(--size-hairline) solid var(--table-border);\n color: var(--table-header-fg);\n font-size: var(--table-header-font-size);\n font-weight: var(--table-header-font-weight);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n outline: none;\n\n &[data-align=\"end\"] {\n text-align: end;\n }\n\n &[data-allows-sorting] {\n cursor: pointer;\n }\n\n &[data-allows-sorting]::after {\n content: \"\";\n display: inline-block;\n inline-size: var(--table-header-sort-indicator-size);\n block-size: var(--table-header-sort-indicator-size);\n margin-inline-start: var(--table-header-sort-indicator-size);\n border-inline-end: var(--table-header-sort-indicator-width) solid currentcolor;\n border-block-end: var(--table-header-sort-indicator-width) solid currentcolor;\n rotate: -135deg;\n translate: 0 25%;\n opacity: 0;\n transition: opacity var(--table-motion-duration) var(--motion-easing-standard);\n }\n\n &[data-hovered]::after {\n opacity: 0.5;\n }\n\n &[data-sort-direction] {\n color: var(--table-header-fg-active);\n }\n\n &[data-sort-direction]::after {\n color: var(--table-header-sort-indicator);\n opacity: 1;\n }\n\n &[data-sort-direction=\"descending\"]::after {\n rotate: 45deg;\n translate: 0 -25%;\n }\n\n &[data-focus-visible] {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: calc(-1 * var(--table-focus-ring-width));\n }\n }\n\n .row {\n outline: none;\n transition: background-color var(--table-motion-duration) var(--motion-easing-standard);\n\n &[data-hovered] {\n background-color: var(--table-row-bg-hover);\n }\n\n &[data-selected] {\n background-color: var(--table-row-bg-selected);\n }\n\n &[data-disabled] {\n color: var(--table-row-fg-disabled);\n }\n\n &[data-focus-visible] {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: calc(-1 * var(--table-focus-ring-width));\n }\n }\n\n .cell {\n padding-block: var(--_padding-block);\n padding-inline: var(--table-cell-padding-inline);\n outline: none;\n\n &[data-align=\"end\"] {\n text-align: end;\n font-variant-numeric: tabular-nums;\n }\n\n &[data-focus-visible] {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: calc(-1 * var(--table-focus-ring-width));\n }\n }\n\n .body .row:not(:last-child) .cell {\n border-block-end: var(--size-hairline) solid var(--table-border);\n }\n\n .selectColumn,\n .selectCell {\n inline-size: var(--table-select-size);\n padding-inline-end: 0;\n }\n\n .selectBox {\n display: grid;\n place-content: center;\n inline-size: var(--table-select-size);\n block-size: var(--table-select-size);\n border: var(--size-hairline) solid var(--table-select-border);\n border-radius: var(--table-select-radius);\n background-color: var(--table-select-bg);\n transition:\n background-color var(--table-motion-duration) var(--motion-easing-standard),\n border-color var(--table-motion-duration) var(--motion-easing-standard);\n\n &::after {\n content: \"\";\n inline-size: calc(var(--table-select-size) * 0.3);\n block-size: calc(var(--table-select-size) * 0.55);\n border-style: solid;\n border-color: var(--table-select-check);\n border-width: 0 var(--table-select-check-width) var(--table-select-check-width) 0;\n rotate: 45deg;\n translate: 0 -10%;\n opacity: 0;\n }\n }\n\n .select {\n display: inline-grid;\n place-items: center;\n inline-size: var(--table-select-size);\n block-size: var(--table-select-size);\n cursor: pointer;\n\n &[data-focus-visible] .selectBox {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: var(--table-focus-ring-offset);\n }\n\n &[data-selected] .selectBox,\n &[data-indeterminate] .selectBox {\n border-color: var(--table-select-bg-checked);\n background-color: var(--table-select-bg-checked);\n\n &::after {\n opacity: 1;\n }\n }\n\n &[data-indeterminate] .selectBox::after {\n inline-size: calc(var(--table-select-size) * 0.55);\n block-size: 0;\n border-width: 0 0 var(--table-select-check-width);\n rotate: 0deg;\n translate: 0 0;\n }\n }\n\n .empty {\n padding: var(--table-empty-padding);\n color: var(--table-empty-fg);\n text-align: center;\n }\n}\n","import { createContext, useCallback, useContext, useEffect, useRef, useState, type ReactElement, type ReactNode, type Ref } from 'react';\nimport {\n Cell as AriaCell,\n Checkbox as AriaCheckbox,\n Column as AriaColumn,\n Row as AriaRow,\n Table as AriaTable,\n TableBody as AriaTableBody,\n TableHeader as AriaTableHeader,\n useTableOptions,\n type Key,\n type SortDescriptor,\n} from 'react-aria-components';\nimport { classes } from '../internal/classes';\nimport { VisuallyHidden } from '../visually-hidden';\nimport { selectionToIds } from '../internal/selection';\nimport styles from './Table.module.css';\n\nexport type TableSelectionMode = 'none' | 'single' | 'multiple';\nexport type TableDensity = 'default' | 'compact';\nexport type TableSortDirection = 'ascending' | 'descending';\nexport interface TableSort {\n column: string;\n direction: TableSortDirection;\n}\n\n/** Rows register their ids so `'all'` (Ctrl/Cmd+A) resolves to an array without a second source of truth. */\nconst RowIdsContext = createContext<{ add: (id: Key) => () => void; all: () => Key[] } | null>(null);\n\nexport interface TableProps {\n /** The table's accessible name. Pass `aria-labelledby` instead when a visible heading names it. */\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n /** @default 'none' */\n selectionMode?: TableSelectionMode;\n /** Selected row ids (controlled). */\n selected?: readonly string[];\n /** @default [] */\n defaultSelected?: readonly string[];\n onSelectedChange?: (ids: string[]) => void;\n /** Rows that cannot be selected or activated. */\n disabledIds?: readonly string[];\n /** Current sort (controlled). Sorting the data is the caller's job: this is the header state. */\n sort?: TableSort | null;\n defaultSort?: TableSort;\n onSortChange?: (sort: TableSort) => void;\n /** Row padding steps down one token. The brand's `space.unit` sets the base. @default 'default' */\n density?: TableDensity;\n /** The header stays visible while the container scrolls. Give the container a height through `className`. @default false */\n stickyHeader?: boolean;\n /**\n * Called with the row id on Enter, on click, and on double click. Once\n * rows are selectable and a selection exists, Enter and click toggle the\n * selection instead (the vendor's rule, so touch users select without a\n * modifier key); double click still runs the action.\n */\n onRowAction?: (id: string) => void;\n /** Applied to the scroll container around the table. */\n className?: string;\n /** The `<table>` element. */\n ref?: Ref<HTMLTableElement>;\n /** `Table.Header` then `Table.Body`. */\n children: ReactNode;\n}\n\n/**\n * A data table on React Aria's grid (decision record 001): one Tab stop,\n * arrows move between rows and cells, Space toggles selection, Enter runs\n * the row action (see `onRowAction` for the selection rule), sortable\n * headers carry `aria-sort` and are pressed to change direction. Selection and sort are the caller's state; the table\n * reports changes and renders them. Compose with `Table.Header`,\n * `Table.Column`, `Table.Body`, `Table.Row` and `Table.Cell`; the selection\n * column appears by itself when rows are selectable.\n *\n * @status experimental\n * @category Data\n * @accessibility `role=\"grid\"` named by `aria-label` or `aria-labelledby`; one column is the row header (`isRowHeader`), sortable columns carry `aria-sort`, selectable rows `aria-selected`, and the selection column holds real checkboxes (\"Select All\" in the header). One tab stop: arrows move between rows and cells, Space toggles selection, Enter runs the row action, the header cells are pressed to sort.\n * @do Mark exactly one column `isRowHeader`.\n * Give the table a name that says what the rows are.\n * @dont Sort the data inside the table; sort your data from `onSortChange` and pass it back.\n * Use it for layout.\n */\nexport function Table({\n selectionMode = 'none',\n selected,\n defaultSelected = [],\n onSelectedChange,\n disabledIds,\n sort,\n defaultSort,\n onSortChange,\n density = 'default',\n stickyHeader = false,\n onRowAction,\n className,\n ref,\n children,\n ...rest\n}: TableProps) {\n const ids = useRef(new Set<Key>());\n const add = useCallback((id: Key) => {\n ids.current.add(id);\n return () => {\n ids.current.delete(id);\n };\n }, []);\n const all = useCallback(() => Array.from(ids.current), []);\n // The vendor has no default for the sort descriptor; hold it here when uncontrolled.\n const [uncontrolledSort, setUncontrolledSort] = useState<TableSort | undefined>(defaultSort);\n const currentSort = sort === undefined ? uncontrolledSort : (sort ?? undefined);\n\n return (\n <RowIdsContext.Provider value={{ add, all }}>\n <div className={classes(styles.container, className)} data-sticky-header={stickyHeader ? '' : undefined}>\n <AriaTable\n {...rest}\n ref={ref as Ref<HTMLDivElement | HTMLTableElement>}\n className={styles.table}\n data-density={density}\n selectionMode={selectionMode}\n selectedKeys={selected}\n defaultSelectedKeys={defaultSelected}\n onSelectionChange={(selection) => onSelectedChange?.(selectionToIds(selection, all))}\n disabledKeys={disabledIds}\n sortDescriptor={currentSort as SortDescriptor | undefined}\n onSortChange={(descriptor) => {\n const next = { column: String(descriptor.column), direction: descriptor.direction };\n if (sort === undefined) setUncontrolledSort(next);\n onSortChange?.(next);\n }}\n onRowAction={onRowAction && ((key) => onRowAction(String(key)))}\n >\n {children}\n </AriaTable>\n </div>\n </RowIdsContext.Provider>\n );\n}\n\ninterface TableHeaderProps {\n className?: string;\n /** `Table.Column` elements. */\n children: ReactNode;\n}\n\nfunction TableHeader({ className, children }: TableHeaderProps) {\n const { selectionMode, selectionBehavior } = useTableOptions();\n return (\n <AriaTableHeader className={classes(styles.header, className)}>\n {selectionMode !== 'none' && selectionBehavior === 'toggle' && (\n <AriaColumn className={classes(styles.column, styles.selectColumn)}>\n {selectionMode === 'multiple' ? <SelectionCheckbox /> : <VisuallyHidden>Selection</VisuallyHidden>}\n </AriaColumn>\n )}\n {children}\n </AriaTableHeader>\n );\n}\n\nexport type TableAlign = 'start' | 'end';\n\ninterface TableColumnProps {\n /** The column id `sort.column` refers to. Defaults to the column's text. */\n id?: string;\n /** Pressing the header sorts by it; the table reports the change through `onSortChange`. @default false */\n allowsSorting?: boolean;\n /** This column names the row for assistive technology. Exactly one column per table. @default false */\n isRowHeader?: boolean;\n /** @default 'start' */\n align?: TableAlign;\n className?: string;\n children: ReactNode;\n}\n\nfunction TableColumn({ id, allowsSorting = false, isRowHeader = false, align = 'start', className, children }: TableColumnProps) {\n return (\n <AriaColumn id={id} allowsSorting={allowsSorting} isRowHeader={isRowHeader} className={classes(styles.column, className)} data-align={align}>\n {children}\n </AriaColumn>\n );\n}\n\ninterface TableBodyProps<T extends { id: string }> {\n /** Rows from data: each item renders through the `children` function, keyed by `item.id`. */\n items?: Iterable<T>;\n /** Shown when there are no rows. @default 'No rows' */\n emptyMessage?: ReactNode;\n className?: string;\n /** `Table.Row` elements, or a function from an item to one when `items` is given. */\n children: ReactNode | ((item: T) => ReactElement);\n}\n\nfunction TableBody<T extends { id: string }>({ items, emptyMessage = 'No rows', className, children }: TableBodyProps<T>) {\n return (\n <AriaTableBody items={items} className={classes(styles.body, className)} renderEmptyState={() => <div className={styles.empty}>{emptyMessage}</div>}>\n {children}\n </AriaTableBody>\n );\n}\n\ninterface TableRowProps {\n /** The row id used for selection, sorting callbacks and `onRowAction`. Required unless the row comes from `items`. */\n id?: string;\n className?: string;\n /** `Table.Cell` elements. */\n children: ReactNode;\n}\n\nfunction TableRow({ id, className, children }: TableRowProps) {\n const { selectionBehavior } = useTableOptions();\n const registry = useContext(RowIdsContext);\n useEffect(() => (id !== undefined && registry ? registry.add(id) : undefined), [id, registry]);\n return (\n <AriaRow id={id} className={classes(styles.row, className)}>\n {selectionBehavior === 'toggle' && (\n <AriaCell className={classes(styles.cell, styles.selectCell)}>\n <SelectionCheckbox />\n </AriaCell>\n )}\n {children}\n </AriaRow>\n );\n}\n\ninterface TableCellProps {\n /** @default 'start' */\n align?: TableAlign;\n className?: string;\n children: ReactNode;\n}\n\nfunction TableCell({ align = 'start', className, children }: TableCellProps) {\n return (\n <AriaCell className={classes(styles.cell, className)} data-align={align}>\n {children}\n </AriaCell>\n );\n}\n\n/** React Aria's checkbox wired to the row (or all rows) through `slot=\"selection\"`; drawn from the table's tokens. */\nfunction SelectionCheckbox() {\n return (\n <AriaCheckbox slot=\"selection\" className={styles.select}>\n <span className={styles.selectBox} aria-hidden=\"true\" />\n </AriaCheckbox>\n );\n}\n\nTable.displayName = 'Table';\nTableHeader.displayName = 'Table.Header';\nTableColumn.displayName = 'Table.Column';\nTableBody.displayName = 'Table.Body';\nTableRow.displayName = 'Table.Row';\nTableCell.displayName = 'Table.Cell';\n\nTable.Header = TableHeader;\nTable.Column = TableColumn;\nTable.Body = TableBody;\nTable.Row = TableRow;\nTable.Cell = TableCell;\n"]}
1
+ {"version":3,"sources":["../src/table/Table.module.css","../src/table/Table.tsx"],"names":["container","table","header","column","row","cell","body","selectColumn","selectCell","selectBox","select","empty","AriaTable","AriaTableHeader","AriaColumn","AriaTableBody","AriaRow","AriaCell","AriaCheckbox"],"mappings":"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","file":"chunk-7RQQASKS.js","sourcesContent":["/*\n * Every value is a token (--table-*). Density resolves the row padding into a\n * private custom property on the table. React Aria writes the state:\n * data-hovered, data-selected, data-disabled, data-focus-visible on rows and\n * cells; data-allows-sorting and data-sort-direction on columns;\n * data-selected and data-indeterminate on the selection checkbox.\n * Rules are ordered from general to specific so no later selector has a lower\n * specificity than an earlier one for the same element.\n */\n\n@layer components {\n .container {\n overflow: auto;\n border: var(--size-hairline) solid var(--table-border);\n border-radius: var(--table-radius);\n background-color: var(--table-bg);\n color: var(--table-fg);\n font-family: var(--table-font-family);\n font-size: var(--table-font-size);\n }\n\n .table {\n --_padding-block: var(--table-cell-padding-block-default);\n\n inline-size: 100%;\n border-collapse: collapse;\n border-spacing: 0;\n outline: none;\n\n &[data-density=\"compact\"] {\n --_padding-block: var(--table-cell-padding-block-compact);\n }\n\n &[data-focus-visible] {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: calc(-1 * var(--table-focus-ring-width));\n }\n }\n\n .header {\n background-color: var(--table-header-bg);\n }\n\n .container[data-sticky-header] .header {\n position: sticky;\n inset-block-start: 0;\n }\n\n .column {\n padding-block: var(--_padding-block);\n padding-inline: var(--table-cell-padding-inline);\n border-block-end: var(--size-hairline) solid var(--table-border);\n color: var(--table-header-fg);\n font-size: var(--table-header-font-size);\n font-weight: var(--table-header-font-weight);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n outline: none;\n\n &[data-align=\"end\"] {\n text-align: end;\n }\n\n &[data-allows-sorting] {\n cursor: pointer;\n }\n\n &[data-allows-sorting]::after {\n content: \"\";\n display: inline-block;\n inline-size: var(--table-header-sort-indicator-size);\n block-size: var(--table-header-sort-indicator-size);\n margin-inline-start: var(--table-header-sort-indicator-size);\n border-inline-end: var(--table-header-sort-indicator-width) solid currentcolor;\n border-block-end: var(--table-header-sort-indicator-width) solid currentcolor;\n rotate: -135deg;\n translate: 0 25%;\n opacity: 0;\n transition: opacity var(--table-motion-duration) var(--motion-easing-standard);\n }\n\n &[data-hovered]::after {\n opacity: 0.5;\n }\n\n &[data-sort-direction] {\n color: var(--table-header-fg-active);\n }\n\n &[data-sort-direction]::after {\n color: var(--table-header-sort-indicator);\n opacity: 1;\n }\n\n &[data-sort-direction=\"descending\"]::after {\n rotate: 45deg;\n translate: 0 -25%;\n }\n\n &[data-focus-visible] {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: calc(-1 * var(--table-focus-ring-width));\n }\n }\n\n .row {\n outline: none;\n transition: background-color var(--table-motion-duration) var(--motion-easing-standard);\n\n &[data-hovered] {\n background-color: var(--table-row-bg-hover);\n }\n\n &[data-selected] {\n background-color: var(--table-row-bg-selected);\n }\n\n &[data-disabled] {\n color: var(--table-row-fg-disabled);\n }\n\n &[data-focus-visible] {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: calc(-1 * var(--table-focus-ring-width));\n }\n }\n\n .cell {\n padding-block: var(--_padding-block);\n padding-inline: var(--table-cell-padding-inline);\n outline: none;\n\n &[data-align=\"end\"] {\n text-align: end;\n font-variant-numeric: tabular-nums;\n }\n\n &[data-focus-visible] {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: calc(-1 * var(--table-focus-ring-width));\n }\n }\n\n .body .row:not(:last-child) .cell {\n border-block-end: var(--size-hairline) solid var(--table-border);\n }\n\n .selectColumn,\n .selectCell {\n inline-size: var(--table-select-size);\n padding-inline-end: 0;\n }\n\n .selectBox {\n display: grid;\n place-content: center;\n inline-size: var(--table-select-size);\n block-size: var(--table-select-size);\n border: var(--size-hairline) solid var(--table-select-border);\n border-radius: var(--table-select-radius);\n background-color: var(--table-select-bg);\n transition:\n background-color var(--table-motion-duration) var(--motion-easing-standard),\n border-color var(--table-motion-duration) var(--motion-easing-standard);\n\n &::after {\n content: \"\";\n inline-size: calc(var(--table-select-size) * 0.3);\n block-size: calc(var(--table-select-size) * 0.55);\n border-style: solid;\n border-color: var(--table-select-check);\n border-width: 0 var(--table-select-check-width) var(--table-select-check-width) 0;\n rotate: 45deg;\n translate: 0 -10%;\n opacity: 0;\n }\n }\n\n .select {\n display: inline-grid;\n place-items: center;\n inline-size: var(--table-select-size);\n block-size: var(--table-select-size);\n cursor: pointer;\n\n &[data-focus-visible] .selectBox {\n outline: var(--table-focus-ring-width) solid var(--table-focus-ring);\n outline-offset: var(--table-focus-ring-offset);\n }\n\n &[data-selected] .selectBox,\n &[data-indeterminate] .selectBox {\n border-color: var(--table-select-bg-checked);\n background-color: var(--table-select-bg-checked);\n\n &::after {\n opacity: 1;\n }\n }\n\n &[data-indeterminate] .selectBox::after {\n inline-size: calc(var(--table-select-size) * 0.55);\n block-size: 0;\n border-width: 0 0 var(--table-select-check-width);\n rotate: 0deg;\n translate: 0 0;\n }\n }\n\n .empty {\n padding: var(--table-empty-padding);\n color: var(--table-empty-fg);\n text-align: center;\n }\n}\n","import { createContext, useCallback, useContext, useEffect, useRef, useState, type ReactElement, type ReactNode, type Ref } from 'react';\nimport {\n Cell as AriaCell,\n Checkbox as AriaCheckbox,\n Column as AriaColumn,\n Row as AriaRow,\n Table as AriaTable,\n TableBody as AriaTableBody,\n TableHeader as AriaTableHeader,\n useTableOptions,\n type Key,\n type SortDescriptor,\n} from 'react-aria-components';\nimport { classes } from '../internal/classes';\nimport { VisuallyHidden } from '../visually-hidden';\nimport { selectionToIds } from '../internal/selection';\nimport styles from './Table.module.css';\n\nexport type TableSelectionMode = 'none' | 'single' | 'multiple';\nexport type TableDensity = 'default' | 'compact';\nexport type TableSortDirection = 'ascending' | 'descending';\nexport interface TableSort {\n column: string;\n direction: TableSortDirection;\n}\n\n/** Rows register their ids so `'all'` (Ctrl/Cmd+A) resolves to an array without a second source of truth. */\nconst RowIdsContext = createContext<{ add: (id: Key) => () => void; all: () => Key[] } | null>(null);\n\nexport interface TableProps {\n /** The table's accessible name. Pass `aria-labelledby` instead when a visible heading names it. */\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n /** @default 'none' */\n selectionMode?: TableSelectionMode;\n /** Selected row ids (controlled). */\n selected?: readonly string[];\n /** @default [] */\n defaultSelected?: readonly string[];\n onSelectedChange?: (ids: string[]) => void;\n /** Rows that cannot be selected or activated. */\n disabledIds?: readonly string[];\n /** Current sort (controlled). Sorting the data is the caller's job: this is the header state. */\n sort?: TableSort | null;\n defaultSort?: TableSort;\n onSortChange?: (sort: TableSort) => void;\n /** Row padding steps down one token. The brand's `space.unit` sets the base. @default 'default' */\n density?: TableDensity;\n /** The header stays visible while the container scrolls. Give the container a height through `className`. @default false */\n stickyHeader?: boolean;\n /**\n * Called with the row id on Enter, on click, and on double click. Once\n * rows are selectable and a selection exists, Enter and click toggle the\n * selection instead (the vendor's rule, so touch users select without a\n * modifier key); double click still runs the action.\n */\n onRowAction?: (id: string) => void;\n /** Applied to the scroll container around the table. */\n className?: string;\n /** The `<table>` element. */\n ref?: Ref<HTMLTableElement>;\n /** `Table.Header` then `Table.Body`. */\n children: ReactNode;\n}\n\n/**\n * A data table on React Aria's grid (decision record 001): one Tab stop,\n * arrows move between rows and cells, Space toggles selection, Enter runs\n * the row action (see `onRowAction` for the selection rule), sortable\n * headers carry `aria-sort` and are pressed to change direction. Selection and sort are the caller's state; the table\n * reports changes and renders them. Compose with `Table.Header`,\n * `Table.Column`, `Table.Body`, `Table.Row` and `Table.Cell`; the selection\n * column appears by itself when rows are selectable.\n *\n * @status experimental\n * @category Data\n * @accessibility `role=\"grid\"` named by `aria-label` or `aria-labelledby`; one column is the row header (`isRowHeader`), sortable columns carry `aria-sort`, selectable rows `aria-selected`, and the selection column holds real checkboxes (\"Select All\" in the header). One tab stop: arrows move between rows and cells, Space toggles selection, Enter runs the row action, the header cells are pressed to sort.\n * @do Mark exactly one column `isRowHeader`.\n * Give the table a name that says what the rows are.\n * @dont Sort the data inside the table; sort your data from `onSortChange` and pass it back.\n * Use it for layout.\n */\nexport function Table({\n selectionMode = 'none',\n selected,\n defaultSelected = [],\n onSelectedChange,\n disabledIds,\n sort,\n defaultSort,\n onSortChange,\n density = 'default',\n stickyHeader = false,\n onRowAction,\n className,\n ref,\n children,\n ...rest\n}: TableProps) {\n const ids = useRef(new Set<Key>());\n const add = useCallback((id: Key) => {\n ids.current.add(id);\n return () => {\n ids.current.delete(id);\n };\n }, []);\n const all = useCallback(() => Array.from(ids.current), []);\n // The vendor has no default for the sort descriptor; hold it here when uncontrolled.\n const [uncontrolledSort, setUncontrolledSort] = useState<TableSort | undefined>(defaultSort);\n const currentSort = sort === undefined ? uncontrolledSort : (sort ?? undefined);\n\n return (\n <RowIdsContext.Provider value={{ add, all }}>\n <div className={classes(styles.container, className)} data-sticky-header={stickyHeader ? '' : undefined}>\n <AriaTable\n {...rest}\n ref={ref as Ref<HTMLDivElement | HTMLTableElement>}\n className={styles.table}\n data-density={density}\n selectionMode={selectionMode}\n selectedKeys={selected}\n defaultSelectedKeys={defaultSelected}\n onSelectionChange={(selection) => onSelectedChange?.(selectionToIds(selection, all))}\n disabledKeys={disabledIds}\n sortDescriptor={currentSort as SortDescriptor | undefined}\n onSortChange={(descriptor) => {\n const next = { column: String(descriptor.column), direction: descriptor.direction };\n if (sort === undefined) setUncontrolledSort(next);\n onSortChange?.(next);\n }}\n onRowAction={onRowAction && ((key) => onRowAction(String(key)))}\n >\n {children}\n </AriaTable>\n </div>\n </RowIdsContext.Provider>\n );\n}\n\ninterface TableHeaderProps {\n className?: string;\n /** `Table.Column` elements. */\n children: ReactNode;\n}\n\nfunction TableHeader({ className, children }: TableHeaderProps) {\n const { selectionMode, selectionBehavior } = useTableOptions();\n return (\n <AriaTableHeader className={classes(styles.header, className)}>\n {selectionMode !== 'none' && selectionBehavior === 'toggle' && (\n <AriaColumn className={classes(styles.column, styles.selectColumn)}>\n {selectionMode === 'multiple' ? <SelectionCheckbox /> : <VisuallyHidden>Selection</VisuallyHidden>}\n </AriaColumn>\n )}\n {children}\n </AriaTableHeader>\n );\n}\n\nexport type TableAlign = 'start' | 'end';\n\ninterface TableColumnProps {\n /** The column id `sort.column` refers to. Defaults to the column's text. */\n id?: string;\n /** Pressing the header sorts by it; the table reports the change through `onSortChange`. @default false */\n allowsSorting?: boolean;\n /** This column names the row for assistive technology. Exactly one column per table. @default false */\n isRowHeader?: boolean;\n /** @default 'start' */\n align?: TableAlign;\n className?: string;\n children: ReactNode;\n}\n\nfunction TableColumn({ id, allowsSorting = false, isRowHeader = false, align = 'start', className, children }: TableColumnProps) {\n return (\n <AriaColumn id={id} allowsSorting={allowsSorting} isRowHeader={isRowHeader} className={classes(styles.column, className)} data-align={align}>\n {children}\n </AriaColumn>\n );\n}\n\ninterface TableBodyProps<T extends { id: string }> {\n /** Rows from data: each item renders through the `children` function, keyed by `item.id`. */\n items?: Iterable<T>;\n /** Shown when there are no rows. @default 'No rows' */\n emptyMessage?: ReactNode;\n className?: string;\n /** `Table.Row` elements, or a function from an item to one when `items` is given. */\n children: ReactNode | ((item: T) => ReactElement);\n}\n\nfunction TableBody<T extends { id: string }>({ items, emptyMessage = 'No rows', className, children }: TableBodyProps<T>) {\n return (\n <AriaTableBody items={items} className={classes(styles.body, className)} renderEmptyState={() => <div className={styles.empty}>{emptyMessage}</div>}>\n {children}\n </AriaTableBody>\n );\n}\n\ninterface TableRowProps {\n /** The row id used for selection, sorting callbacks and `onRowAction`. Required unless the row comes from `items`. */\n id?: string;\n className?: string;\n /** `Table.Cell` elements. */\n children: ReactNode;\n}\n\nfunction TableRow({ id, className, children }: TableRowProps) {\n const { selectionBehavior } = useTableOptions();\n const registry = useContext(RowIdsContext);\n useEffect(() => (id !== undefined && registry ? registry.add(id) : undefined), [id, registry]);\n return (\n <AriaRow id={id} className={classes(styles.row, className)}>\n {selectionBehavior === 'toggle' && (\n <AriaCell className={classes(styles.cell, styles.selectCell)}>\n <SelectionCheckbox />\n </AriaCell>\n )}\n {children}\n </AriaRow>\n );\n}\n\ninterface TableCellProps {\n /** @default 'start' */\n align?: TableAlign;\n className?: string;\n children: ReactNode;\n}\n\nfunction TableCell({ align = 'start', className, children }: TableCellProps) {\n return (\n <AriaCell className={classes(styles.cell, className)} data-align={align}>\n {children}\n </AriaCell>\n );\n}\n\n/** React Aria's checkbox wired to the row (or all rows) through `slot=\"selection\"`; drawn from the table's tokens. */\nfunction SelectionCheckbox() {\n return (\n <AriaCheckbox slot=\"selection\" className={styles.select}>\n <span className={styles.selectBox} aria-hidden=\"true\" />\n </AriaCheckbox>\n );\n}\n\nTable.displayName = 'Table';\nTableHeader.displayName = 'Table.Header';\nTableColumn.displayName = 'Table.Column';\nTableBody.displayName = 'Table.Body';\nTableRow.displayName = 'Table.Row';\nTableCell.displayName = 'Table.Cell';\n\nTable.Header = TableHeader;\nTable.Column = TableColumn;\nTable.Body = TableBody;\nTable.Row = TableRow;\nTable.Cell = TableCell;\n"]}
@@ -1,6 +1,6 @@
1
1
  import { Spinner } from './chunk-I4KC4V6A.js';
2
- import { isPresent, AriaFieldLabel, AriaFieldDescription, AriaFieldError, ariaFieldClass } from './chunk-4HRNRTXI.js';
3
2
  import { renderOption, renderEmpty, listClass } from './chunk-T76YSNTZ.js';
3
+ import { isPresent, AriaFieldLabel, AriaFieldDescription, AriaFieldError, ariaFieldClass } from './chunk-4HRNRTXI.js';
4
4
  import { Icon } from './chunk-ASNTNTJW.js';
5
5
  import { useFilter, ComboBox, Input, Button, Popover, ListBox } from 'react-aria-components';
6
6
  import { ChevronDown } from '@pearpages/pulp-icons';
@@ -82,5 +82,5 @@ function Combobox({
82
82
  Combobox.displayName = "Combobox";
83
83
 
84
84
  export { Combobox };
85
- //# sourceMappingURL=chunk-Q6J6BMSB.js.map
86
- //# sourceMappingURL=chunk-Q6J6BMSB.js.map
85
+ //# sourceMappingURL=chunk-GUZMLYST.js.map
86
+ //# sourceMappingURL=chunk-GUZMLYST.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/combobox/Combobox.module.css","../src/combobox/Combobox.tsx"],"names":["root","control","input","button","popover","list","AriaComboBox","AriaInput","AriaButton","AriaPopover","AriaListBox"],"mappings":"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","file":"chunk-Q6J6BMSB.js","sourcesContent":["/*\n * Every value is a token (--combobox-*); the options read --listbox-*. Size\n * and invalid state resolve into private custom properties on the root. The\n * popover is positioned by React Aria; the gap to the control is its own\n * padding, so no offset number lives in code, and --trigger-width (set by\n * the vendor) makes it as wide as the control.\n */\n\n@layer components {\n .root {\n --_height: var(--combobox-height-md);\n --_font-size: var(--combobox-font-size-md);\n --_border: var(--combobox-border-default);\n\n font-family: var(--combobox-font-family);\n\n &[data-size=\"sm\"] {\n --_height: var(--combobox-height-sm);\n --_font-size: var(--combobox-font-size-sm);\n }\n\n &[data-size=\"lg\"] {\n --_height: var(--combobox-height-lg);\n --_font-size: var(--combobox-font-size-lg);\n }\n\n &[data-invalid] { --_border: var(--combobox-border-invalid); }\n }\n\n .control {\n display: grid;\n align-items: center;\n\n & > * {\n grid-area: 1 / 1;\n }\n }\n\n .input {\n inline-size: 100%;\n min-block-size: var(--_height);\n padding-block: 0;\n padding-inline: var(--combobox-padding-inline) var(--combobox-icon-size);\n border: var(--size-hairline) solid var(--_border);\n border-radius: var(--combobox-radius);\n background-color: var(--combobox-bg);\n color: var(--combobox-fg);\n font-size: var(--_font-size);\n transition: border-color var(--combobox-motion-duration) var(--motion-easing-standard);\n\n &::placeholder {\n color: var(--combobox-placeholder);\n }\n\n &[data-focused] {\n border-color: var(--combobox-border-focus);\n outline: var(--combobox-focus-ring-width) solid var(--combobox-focus-ring);\n outline-offset: var(--combobox-focus-ring-offset);\n }\n\n &[data-disabled] {\n cursor: not-allowed;\n opacity: 0.55;\n }\n\n &[data-hovered]:not([data-focused], [data-disabled]) {\n border-color: var(--combobox-border-hover);\n }\n }\n\n .button {\n display: grid;\n place-items: center;\n inline-size: var(--combobox-icon-size);\n block-size: var(--_height);\n padding: 0;\n border: none;\n border-radius: var(--combobox-radius);\n background-color: transparent;\n color: var(--combobox-icon);\n justify-self: end;\n cursor: pointer;\n\n &[data-focus-visible] {\n outline: var(--combobox-focus-ring-width) solid var(--combobox-focus-ring);\n outline-offset: calc(-1 * var(--combobox-focus-ring-width));\n }\n\n &[data-disabled] {\n cursor: not-allowed;\n }\n }\n\n .popover {\n inline-size: var(--trigger-width);\n padding-block-start: var(--combobox-gap);\n animation: fade var(--combobox-popover-duration) var(--motion-easing-standard);\n\n &[data-placement=\"top\"] {\n padding-block: 0 var(--combobox-gap);\n }\n }\n\n .list {\n box-shadow: var(--combobox-popover-shadow);\n }\n\n @keyframes fade {\n from {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .popover {\n animation: none;\n }\n }\n}\n","import type { ReactNode, Ref } from 'react';\nimport {\n Button as AriaButton,\n ComboBox as AriaComboBox,\n Input as AriaInput,\n ListBox as AriaListBox,\n Popover as AriaPopover,\n useFilter,\n} from 'react-aria-components';\nimport { ChevronDown } from '@pearpages/pulp-icons';\nimport { Icon } from '../icon';\nimport { AriaFieldDescription, AriaFieldError, AriaFieldLabel, ariaFieldClass, isPresent } from '../internal/AriaField';\nimport { listClass, renderEmpty, renderOption, type ListboxItem } from '../listbox/ListboxOptions';\nimport { Spinner } from '../spinner';\nimport styles from './Combobox.module.css';\n\nexport type ComboboxItem = ListboxItem;\nexport type ComboboxSize = 'sm' | 'md' | 'lg';\n/** `contains` and `startsWith` filter `items` as the user types; `none` shows `items` as given (you filtered them, typically after a fetch). */\nexport type ComboboxFilter = 'contains' | 'startsWith' | 'none';\n\nexport interface ComboboxProps {\n /** Visible label. Always rendered. */\n label: ReactNode;\n description?: ReactNode;\n /** Presence marks the control invalid and announces the message. */\n error?: ReactNode;\n /** The options. With `filter=\"none\"` they are shown as given. */\n items: readonly ComboboxItem[];\n /** Selected id (controlled). `null` is \"nothing selected\". */\n value?: string | null;\n defaultValue?: string | null;\n onChange?: (value: string | null) => void;\n /** The text in the input (controlled). Pair with `onInputChange` to fetch options as the user types. */\n inputValue?: string;\n defaultInputValue?: string;\n onInputChange?: (text: string) => void;\n /** @default 'contains' */\n filter?: ComboboxFilter;\n /** Keeps the list open with a spinner in the control while options load. @default false */\n loading?: boolean;\n /** Shown in the open list when no option matches. @default 'No results' */\n emptyMessage?: ReactNode;\n /** Announced while `loading`. @default 'Loading…' */\n loadingMessage?: ReactNode;\n /** Let the typed text stand as the value even when it matches no option. @default false */\n allowsCustomValue?: boolean;\n /** When the list opens: on focus, on typing, or only from the button and arrow keys. @default 'input' */\n menuTrigger?: 'focus' | 'input' | 'manual';\n placeholder?: string;\n /** @default 'md' */\n size?: ComboboxSize;\n /** @default false */\n required?: boolean;\n /** @default false */\n disabled?: boolean;\n /** @default false */\n readOnly?: boolean;\n /** Submitted with a form: the selected id (or the text with `allowsCustomValue`). */\n name?: string;\n autoFocus?: boolean;\n /** Applied to the field wrapper, not the input. */\n className?: string;\n /** The `<input>` element. */\n ref?: Ref<HTMLInputElement>;\n}\n\n/**\n * A text input that suggests options as the user types, on React Aria's\n * combobox (decision record 001): the list is announced through\n * `aria-activedescendant`, arrows move, Enter selects, Escape closes and\n * then clears. Static lists filter themselves; for async options, control\n * `inputValue`, fetch, and pass the results with `filter=\"none\"` and\n * `loading` while they arrive. Options look the same as Listbox's.\n *\n * @status experimental\n * @category Forms\n * @accessibility A `combobox` input with `aria-autocomplete=\"list\"`; the open list is a `listbox` announced through `aria-activedescendant`, so focus stays in the input. Arrows move, Enter selects, Escape closes and then clears. Loading and empty states are announced from inside the list.\n * @do Use `filter=\"none\"` with controlled `inputValue` for server-side search.\n * Keep `loading` true until the results for the current text arrive.\n * @dont Use it for fewer than five fixed options; use Picker or the native Select.\n * Set `menuTrigger=\"focus\"` on a long list; opening on every focus is noisy.\n */\nexport function Combobox({\n label,\n description,\n error,\n items,\n value,\n defaultValue,\n onChange,\n inputValue,\n defaultInputValue,\n onInputChange,\n filter = 'contains',\n loading = false,\n emptyMessage = 'No results',\n loadingMessage = 'Loading…',\n allowsCustomValue = false,\n menuTrigger = 'input',\n placeholder,\n size = 'md',\n required = false,\n disabled = false,\n readOnly = false,\n name,\n autoFocus,\n className,\n ref,\n}: ComboboxProps) {\n const { contains, startsWith } = useFilter({ sensitivity: 'base' });\n const invalid = isPresent(error);\n // `items` means \"already filtered\"; `defaultItems` lets React Aria filter.\n const collection = filter === 'none' ? { items } : { defaultItems: items, defaultFilter: filter === 'startsWith' ? startsWith : contains };\n\n return (\n <AriaComboBox\n {...collection}\n className={ariaFieldClass(styles.root, className)}\n data-size={size}\n data-loading={loading ? '' : undefined}\n selectedKey={value}\n defaultSelectedKey={defaultValue}\n onSelectionChange={(key) => onChange?.(key === null ? null : String(key))}\n inputValue={inputValue}\n defaultInputValue={defaultInputValue}\n onInputChange={onInputChange}\n allowsCustomValue={allowsCustomValue}\n allowsEmptyCollection\n menuTrigger={menuTrigger}\n isRequired={required}\n isDisabled={disabled}\n isReadOnly={readOnly}\n isInvalid={invalid}\n name={name}\n >\n <AriaFieldLabel required={required}>{label}</AriaFieldLabel>\n <div className={styles.control}>\n <AriaInput ref={ref} className={styles.input} placeholder={placeholder} autoFocus={autoFocus} />\n <AriaButton className={styles.button}>\n {loading ? (\n <Spinner size=\"inherit\" tone=\"inherit\" decorative />\n ) : (\n <Icon size=\"inherit\">\n <ChevronDown />\n </Icon>\n )}\n </AriaButton>\n </div>\n <AriaFieldDescription>{description}</AriaFieldDescription>\n <AriaFieldError>{error}</AriaFieldError>\n <AriaPopover className={styles.popover} placement=\"bottom start\">\n <AriaListBox className={listClass(styles.list)} renderEmptyState={() => renderEmpty(loading ? loadingMessage : emptyMessage)}>\n {renderOption}\n </AriaListBox>\n </AriaPopover>\n </AriaComboBox>\n );\n}\n\nCombobox.displayName = 'Combobox';\n"]}
1
+ {"version":3,"sources":["../src/combobox/Combobox.module.css","../src/combobox/Combobox.tsx"],"names":["root","control","input","button","popover","list","AriaComboBox","AriaInput","AriaButton","AriaPopover","AriaListBox"],"mappings":"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","file":"chunk-GUZMLYST.js","sourcesContent":["/*\n * Every value is a token (--combobox-*); the options read --listbox-*. Size\n * and invalid state resolve into private custom properties on the root. The\n * popover is positioned by React Aria; the gap to the control is its own\n * padding, so no offset number lives in code, and --trigger-width (set by\n * the vendor) makes it as wide as the control.\n */\n\n@layer components {\n .root {\n --_height: var(--combobox-height-md);\n --_font-size: var(--combobox-font-size-md);\n --_border: var(--combobox-border-default);\n\n font-family: var(--combobox-font-family);\n\n &[data-size=\"sm\"] {\n --_height: var(--combobox-height-sm);\n --_font-size: var(--combobox-font-size-sm);\n }\n\n &[data-size=\"lg\"] {\n --_height: var(--combobox-height-lg);\n --_font-size: var(--combobox-font-size-lg);\n }\n\n &[data-invalid] { --_border: var(--combobox-border-invalid); }\n }\n\n .control {\n display: grid;\n align-items: center;\n\n & > * {\n grid-area: 1 / 1;\n }\n }\n\n .input {\n inline-size: 100%;\n min-block-size: var(--_height);\n padding-block: 0;\n padding-inline: var(--combobox-padding-inline) var(--combobox-icon-size);\n border: var(--size-hairline) solid var(--_border);\n border-radius: var(--combobox-radius);\n background-color: var(--combobox-bg);\n color: var(--combobox-fg);\n font-size: var(--_font-size);\n transition: border-color var(--combobox-motion-duration) var(--motion-easing-standard);\n\n &::placeholder {\n color: var(--combobox-placeholder);\n }\n\n &[data-focused] {\n border-color: var(--combobox-border-focus);\n outline: var(--combobox-focus-ring-width) solid var(--combobox-focus-ring);\n outline-offset: var(--combobox-focus-ring-offset);\n }\n\n &[data-disabled] {\n cursor: not-allowed;\n opacity: 0.55;\n }\n\n &[data-hovered]:not([data-focused], [data-disabled]) {\n border-color: var(--combobox-border-hover);\n }\n }\n\n .button {\n display: grid;\n place-items: center;\n inline-size: var(--combobox-icon-size);\n block-size: var(--_height);\n padding: 0;\n border: none;\n border-radius: var(--combobox-radius);\n background-color: transparent;\n color: var(--combobox-icon);\n justify-self: end;\n cursor: pointer;\n\n &[data-focus-visible] {\n outline: var(--combobox-focus-ring-width) solid var(--combobox-focus-ring);\n outline-offset: calc(-1 * var(--combobox-focus-ring-width));\n }\n\n &[data-disabled] {\n cursor: not-allowed;\n }\n }\n\n .popover {\n inline-size: var(--trigger-width);\n padding-block-start: var(--combobox-gap);\n animation: fade var(--combobox-popover-duration) var(--motion-easing-standard);\n\n &[data-placement=\"top\"] {\n padding-block: 0 var(--combobox-gap);\n }\n }\n\n .list {\n box-shadow: var(--combobox-popover-shadow);\n }\n\n @keyframes fade {\n from {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .popover {\n animation: none;\n }\n }\n}\n","import type { ReactNode, Ref } from 'react';\nimport {\n Button as AriaButton,\n ComboBox as AriaComboBox,\n Input as AriaInput,\n ListBox as AriaListBox,\n Popover as AriaPopover,\n useFilter,\n} from 'react-aria-components';\nimport { ChevronDown } from '@pearpages/pulp-icons';\nimport { Icon } from '../icon';\nimport { AriaFieldDescription, AriaFieldError, AriaFieldLabel, ariaFieldClass, isPresent } from '../internal/AriaField';\nimport { listClass, renderEmpty, renderOption, type ListboxItem } from '../listbox/ListboxOptions';\nimport { Spinner } from '../spinner';\nimport styles from './Combobox.module.css';\n\nexport type ComboboxItem = ListboxItem;\nexport type ComboboxSize = 'sm' | 'md' | 'lg';\n/** `contains` and `startsWith` filter `items` as the user types; `none` shows `items` as given (you filtered them, typically after a fetch). */\nexport type ComboboxFilter = 'contains' | 'startsWith' | 'none';\n\nexport interface ComboboxProps {\n /** Visible label. Always rendered. */\n label: ReactNode;\n description?: ReactNode;\n /** Presence marks the control invalid and announces the message. */\n error?: ReactNode;\n /** The options. With `filter=\"none\"` they are shown as given. */\n items: readonly ComboboxItem[];\n /** Selected id (controlled). `null` is \"nothing selected\". */\n value?: string | null;\n defaultValue?: string | null;\n onChange?: (value: string | null) => void;\n /** The text in the input (controlled). Pair with `onInputChange` to fetch options as the user types. */\n inputValue?: string;\n defaultInputValue?: string;\n onInputChange?: (text: string) => void;\n /** @default 'contains' */\n filter?: ComboboxFilter;\n /** Keeps the list open with a spinner in the control while options load. @default false */\n loading?: boolean;\n /** Shown in the open list when no option matches. @default 'No results' */\n emptyMessage?: ReactNode;\n /** Announced while `loading`. @default 'Loading…' */\n loadingMessage?: ReactNode;\n /** Let the typed text stand as the value even when it matches no option. @default false */\n allowsCustomValue?: boolean;\n /** When the list opens: on focus, on typing, or only from the button and arrow keys. @default 'input' */\n menuTrigger?: 'focus' | 'input' | 'manual';\n placeholder?: string;\n /** @default 'md' */\n size?: ComboboxSize;\n /** @default false */\n required?: boolean;\n /** @default false */\n disabled?: boolean;\n /** @default false */\n readOnly?: boolean;\n /** Submitted with a form: the selected id (or the text with `allowsCustomValue`). */\n name?: string;\n autoFocus?: boolean;\n /** Applied to the field wrapper, not the input. */\n className?: string;\n /** The `<input>` element. */\n ref?: Ref<HTMLInputElement>;\n}\n\n/**\n * A text input that suggests options as the user types, on React Aria's\n * combobox (decision record 001): the list is announced through\n * `aria-activedescendant`, arrows move, Enter selects, Escape closes and\n * then clears. Static lists filter themselves; for async options, control\n * `inputValue`, fetch, and pass the results with `filter=\"none\"` and\n * `loading` while they arrive. Options look the same as Listbox's.\n *\n * @status experimental\n * @category Forms\n * @accessibility A `combobox` input with `aria-autocomplete=\"list\"`; the open list is a `listbox` announced through `aria-activedescendant`, so focus stays in the input. Arrows move, Enter selects, Escape closes and then clears. Loading and empty states are announced from inside the list.\n * @do Use `filter=\"none\"` with controlled `inputValue` for server-side search.\n * Keep `loading` true until the results for the current text arrive.\n * @dont Use it for fewer than five fixed options; use Picker or the native Select.\n * Set `menuTrigger=\"focus\"` on a long list; opening on every focus is noisy.\n */\nexport function Combobox({\n label,\n description,\n error,\n items,\n value,\n defaultValue,\n onChange,\n inputValue,\n defaultInputValue,\n onInputChange,\n filter = 'contains',\n loading = false,\n emptyMessage = 'No results',\n loadingMessage = 'Loading…',\n allowsCustomValue = false,\n menuTrigger = 'input',\n placeholder,\n size = 'md',\n required = false,\n disabled = false,\n readOnly = false,\n name,\n autoFocus,\n className,\n ref,\n}: ComboboxProps) {\n const { contains, startsWith } = useFilter({ sensitivity: 'base' });\n const invalid = isPresent(error);\n // `items` means \"already filtered\"; `defaultItems` lets React Aria filter.\n const collection = filter === 'none' ? { items } : { defaultItems: items, defaultFilter: filter === 'startsWith' ? startsWith : contains };\n\n return (\n <AriaComboBox\n {...collection}\n className={ariaFieldClass(styles.root, className)}\n data-size={size}\n data-loading={loading ? '' : undefined}\n selectedKey={value}\n defaultSelectedKey={defaultValue}\n onSelectionChange={(key) => onChange?.(key === null ? null : String(key))}\n inputValue={inputValue}\n defaultInputValue={defaultInputValue}\n onInputChange={onInputChange}\n allowsCustomValue={allowsCustomValue}\n allowsEmptyCollection\n menuTrigger={menuTrigger}\n isRequired={required}\n isDisabled={disabled}\n isReadOnly={readOnly}\n isInvalid={invalid}\n name={name}\n >\n <AriaFieldLabel required={required}>{label}</AriaFieldLabel>\n <div className={styles.control}>\n <AriaInput ref={ref} className={styles.input} placeholder={placeholder} autoFocus={autoFocus} />\n <AriaButton className={styles.button}>\n {loading ? (\n <Spinner size=\"inherit\" tone=\"inherit\" decorative />\n ) : (\n <Icon size=\"inherit\">\n <ChevronDown />\n </Icon>\n )}\n </AriaButton>\n </div>\n <AriaFieldDescription>{description}</AriaFieldDescription>\n <AriaFieldError>{error}</AriaFieldError>\n <AriaPopover className={styles.popover} placement=\"bottom start\">\n <AriaListBox className={listClass(styles.list)} renderEmptyState={() => renderEmpty(loading ? loadingMessage : emptyMessage)}>\n {renderOption}\n </AriaListBox>\n </AriaPopover>\n </AriaComboBox>\n );\n}\n\nCombobox.displayName = 'Combobox';\n"]}
@@ -1,5 +1,5 @@
1
- import { isPresent, AriaFieldLabel, AriaFieldDescription, AriaFieldError, ariaFieldClass } from './chunk-4HRNRTXI.js';
2
1
  import { renderOption, renderEmpty, listClass } from './chunk-T76YSNTZ.js';
2
+ import { isPresent, AriaFieldLabel, AriaFieldDescription, AriaFieldError, ariaFieldClass } from './chunk-4HRNRTXI.js';
3
3
  import { Icon } from './chunk-ASNTNTJW.js';
4
4
  import { Select, Button, SelectValue, Popover, ListBox } from 'react-aria-components';
5
5
  import { ChevronDown } from '@pearpages/pulp-icons';
@@ -63,5 +63,5 @@ function Picker({
63
63
  Picker.displayName = "Picker";
64
64
 
65
65
  export { Picker };
66
- //# sourceMappingURL=chunk-J5S2HVHO.js.map
67
- //# sourceMappingURL=chunk-J5S2HVHO.js.map
66
+ //# sourceMappingURL=chunk-WDWTQKIJ.js.map
67
+ //# sourceMappingURL=chunk-WDWTQKIJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/picker/Picker.module.css","../src/picker/Picker.tsx"],"names":["root","trigger","value","chevron","popover","list","AriaSelect","AriaButton","AriaSelectValue","AriaPopover","AriaListBox"],"mappings":";;;;;;;;AAAA,IAAA,cAAA,GAAA;AAAA,EAOG,IAAA,EAAAA,aAAAA;AAAA,EAoBA,OAAA,EAAAC,gBAAAA;AAAA,EAkCA,KAAA,EAAAC,cAAAA;AAAA,EAWA,OAAA,EAAAC,gBAAAA;AAAA,EAKA,OAAA,EAAAC,gBAAAA;AAAA,EAUA,IAAA,EAAAC;AAAA,CAAA;AC9BI,SAAS,MAAA,CAAO;AAAA,EACrB,KAAA;AAAA,EACA,WAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA,GAAc,cAAA;AAAA,EACd,YAAA,GAAe,YAAA;AAAA,EACf,IAAA,GAAO,IAAA;AAAA,EACP,QAAA,GAAW,KAAA;AAAA,EACX,QAAA,GAAW,KAAA;AAAA,EACX,IAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAgB;AACd,EAAA,MAAM,OAAA,GAAU,UAAU,KAAK,CAAA;AAC/B,EAAA,uBACE,IAAA;AAAA,IAACC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,cAAA,CAAe,cAAA,CAAO,IAAA,EAAM,SAAS,CAAA;AAAA,MAChD,WAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAa,KAAA;AAAA,MACb,kBAAA,EAAoB,YAAA;AAAA,MACpB,iBAAA,EAAmB,CAAC,GAAA,KAAQ,QAAA,GAAW,QAAQ,IAAA,GAAO,IAAA,GAAO,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,MACxE,WAAA;AAAA,MACA,UAAA,EAAY,QAAA;AAAA,MACZ,UAAA,EAAY,QAAA;AAAA,MACZ,SAAA,EAAW,OAAA;AAAA,MACX,IAAA;AAAA,MACA,SAAA;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,cAAA,EAAA,EAAe,UAAqB,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBAC3C,IAAA,CAACC,MAAA,EAAA,EAAW,GAAA,EAAU,SAAA,EAAW,eAAO,OAAA,EACtC,QAAA,EAAA;AAAA,0BAAA,GAAA,CAACC,WAAA,EAAA,EAAgB,SAAA,EAAW,cAAA,CAAO,KAAA,EAAO,CAAA;AAAA,0BAC1C,GAAA,CAAC,QAAK,IAAA,EAAK,SAAA,EAAU,WAAW,cAAA,CAAO,OAAA,EACrC,QAAA,kBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,CAAA,EACf;AAAA,SAAA,EACF,CAAA;AAAA,wBACA,GAAA,CAAC,wBAAsB,QAAA,EAAA,WAAA,EAAY,CAAA;AAAA,wBACnC,GAAA,CAAC,kBAAgB,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBACvB,GAAA,CAACC,WAAY,SAAA,EAAW,cAAA,CAAO,SAAS,SAAA,EAAU,cAAA,EAChD,8BAACC,OAAA,EAAA,EAAY,SAAA,EAAW,UAAU,cAAA,CAAO,IAAI,GAAG,KAAA,EAAc,gBAAA,EAAkB,MAAM,WAAA,CAAY,YAAY,CAAA,EAC3G,QAAA,EAAA,YAAA,EACH,CAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;AAEA,MAAA,CAAO,WAAA,GAAc,QAAA","file":"chunk-J5S2HVHO.js","sourcesContent":["/*\n * Every value is a token (--picker-*); the options read --listbox-*. The\n * trigger is a button drawn like a text field. The popover is positioned by\n * React Aria; the gap is its own padding and --trigger-width (vendor) sizes it.\n */\n\n@layer components {\n .root {\n --_height: var(--picker-height-md);\n --_font-size: var(--picker-font-size-md);\n --_border: var(--picker-border-default);\n\n font-family: var(--picker-font-family);\n\n &[data-size=\"sm\"] {\n --_height: var(--picker-height-sm);\n --_font-size: var(--picker-font-size-sm);\n }\n\n &[data-size=\"lg\"] {\n --_height: var(--picker-height-lg);\n --_font-size: var(--picker-font-size-lg);\n }\n\n &[data-invalid] { --_border: var(--picker-border-invalid); }\n }\n\n .trigger {\n display: flex;\n align-items: center;\n gap: var(--picker-gap);\n inline-size: 100%;\n min-block-size: var(--_height);\n padding-block: 0;\n padding-inline: var(--picker-padding-inline);\n border: var(--size-hairline) solid var(--_border);\n border-radius: var(--picker-radius);\n background-color: var(--picker-bg);\n color: var(--picker-fg);\n font-family: inherit;\n font-size: var(--_font-size);\n text-align: start;\n cursor: pointer;\n transition: border-color var(--picker-motion-duration) var(--motion-easing-standard);\n\n &[data-focus-visible] {\n border-color: var(--picker-border-focus);\n outline: var(--picker-focus-ring-width) solid var(--picker-focus-ring);\n outline-offset: var(--picker-focus-ring-offset);\n }\n\n &[data-disabled] {\n cursor: not-allowed;\n opacity: 0.55;\n }\n\n &[data-hovered]:not([data-focus-visible], [data-disabled]) {\n border-color: var(--picker-border-hover);\n }\n }\n\n .value {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n\n &[data-placeholder] {\n color: var(--picker-placeholder);\n }\n }\n\n .chevron {\n flex: none;\n color: var(--picker-icon);\n }\n\n .popover {\n inline-size: var(--trigger-width);\n padding-block-start: var(--picker-gap);\n animation: fade var(--picker-popover-duration) var(--motion-easing-standard);\n\n &[data-placement=\"top\"] {\n padding-block: 0 var(--picker-gap);\n }\n }\n\n .list {\n box-shadow: var(--picker-popover-shadow);\n }\n\n @keyframes fade {\n from {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .popover {\n animation: none;\n }\n }\n}\n","import type { ReactNode, Ref } from 'react';\nimport { Button as AriaButton, ListBox as AriaListBox, Popover as AriaPopover, Select as AriaSelect, SelectValue as AriaSelectValue } from 'react-aria-components';\nimport { ChevronDown } from '@pearpages/pulp-icons';\nimport { Icon } from '../icon';\nimport { AriaFieldDescription, AriaFieldError, AriaFieldLabel, ariaFieldClass, isPresent } from '../internal/AriaField';\nimport { listClass, renderEmpty, renderOption, type ListboxItem } from '../listbox/ListboxOptions';\nimport styles from './Picker.module.css';\n\nexport type PickerItem = ListboxItem;\nexport type PickerSize = 'sm' | 'md' | 'lg';\n\nexport interface PickerProps {\n /** Visible label. Always rendered. */\n label: ReactNode;\n description?: ReactNode;\n /** Presence marks the control invalid and announces the message. */\n error?: ReactNode;\n /** The options. Ids are the values. */\n items: readonly PickerItem[];\n /** Selected id (controlled). `null` is \"nothing selected\". */\n value?: string | null;\n defaultValue?: string | null;\n onChange?: (value: string | null) => void;\n /** Shown in the control until a value is chosen. @default 'Select…' */\n placeholder?: string;\n /** Shown when `items` is empty. @default 'No options' */\n emptyMessage?: ReactNode;\n /** @default 'md' */\n size?: PickerSize;\n /** @default false */\n required?: boolean;\n /** @default false */\n disabled?: boolean;\n /** Submitted with a form through a hidden native select. */\n name?: string;\n autoFocus?: boolean;\n /** Applied to the field wrapper, not the button. */\n className?: string;\n /** The trigger `<button>`. */\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * A single-choice select with rich options (a description line, consistent\n * styling in every browser), on React Aria's select (decision record 001):\n * a button opens a listbox, arrows and typing move, Enter chooses, Escape\n * closes, and a hidden native select carries the value in forms. For a\n * plain list of words, the native `Select` is the default; for several\n * choices at once, use Listbox.\n *\n * @status experimental\n * @category Forms\n * @accessibility A `button` named by the label with `aria-haspopup=\"listbox\"` and `aria-expanded`; the popup is a `listbox` with selected and disabled options; a hidden native select carries `name` and value for forms. Arrows open and move, typing jumps, Enter chooses, Escape closes and returns focus.\n * @do Use a `description` line on options when the label alone is ambiguous.\n * Prefer the native Select for a plain list of words.\n * @dont Use `placeholder` as the only label.\n */\nexport function Picker({\n label,\n description,\n error,\n items,\n value,\n defaultValue,\n onChange,\n placeholder = 'Select…',\n emptyMessage = 'No options',\n size = 'md',\n required = false,\n disabled = false,\n name,\n autoFocus,\n className,\n ref,\n}: PickerProps) {\n const invalid = isPresent(error);\n return (\n <AriaSelect\n className={ariaFieldClass(styles.root, className)}\n data-size={size}\n selectedKey={value}\n defaultSelectedKey={defaultValue}\n onSelectionChange={(key) => onChange?.(key === null ? null : String(key))}\n placeholder={placeholder}\n isRequired={required}\n isDisabled={disabled}\n isInvalid={invalid}\n name={name}\n autoFocus={autoFocus}\n >\n <AriaFieldLabel required={required}>{label}</AriaFieldLabel>\n <AriaButton ref={ref} className={styles.trigger}>\n <AriaSelectValue className={styles.value} />\n <Icon size=\"inherit\" className={styles.chevron}>\n <ChevronDown />\n </Icon>\n </AriaButton>\n <AriaFieldDescription>{description}</AriaFieldDescription>\n <AriaFieldError>{error}</AriaFieldError>\n <AriaPopover className={styles.popover} placement=\"bottom start\">\n <AriaListBox className={listClass(styles.list)} items={items} renderEmptyState={() => renderEmpty(emptyMessage)}>\n {renderOption}\n </AriaListBox>\n </AriaPopover>\n </AriaSelect>\n );\n}\n\nPicker.displayName = 'Picker';\n"]}
1
+ {"version":3,"sources":["../src/picker/Picker.module.css","../src/picker/Picker.tsx"],"names":["root","trigger","value","chevron","popover","list","AriaSelect","AriaButton","AriaSelectValue","AriaPopover","AriaListBox"],"mappings":";;;;;;;;AAAA,IAAA,cAAA,GAAA;AAAA,EAOG,IAAA,EAAAA,aAAAA;AAAA,EAoBA,OAAA,EAAAC,gBAAAA;AAAA,EAkCA,KAAA,EAAAC,cAAAA;AAAA,EAWA,OAAA,EAAAC,gBAAAA;AAAA,EAKA,OAAA,EAAAC,gBAAAA;AAAA,EAUA,IAAA,EAAAC;AAAA,CAAA;AC9BI,SAAS,MAAA,CAAO;AAAA,EACrB,KAAA;AAAA,EACA,WAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA,GAAc,cAAA;AAAA,EACd,YAAA,GAAe,YAAA;AAAA,EACf,IAAA,GAAO,IAAA;AAAA,EACP,QAAA,GAAW,KAAA;AAAA,EACX,QAAA,GAAW,KAAA;AAAA,EACX,IAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAgB;AACd,EAAA,MAAM,OAAA,GAAU,UAAU,KAAK,CAAA;AAC/B,EAAA,uBACE,IAAA;AAAA,IAACC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,cAAA,CAAe,cAAA,CAAO,IAAA,EAAM,SAAS,CAAA;AAAA,MAChD,WAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAa,KAAA;AAAA,MACb,kBAAA,EAAoB,YAAA;AAAA,MACpB,iBAAA,EAAmB,CAAC,GAAA,KAAQ,QAAA,GAAW,QAAQ,IAAA,GAAO,IAAA,GAAO,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,MACxE,WAAA;AAAA,MACA,UAAA,EAAY,QAAA;AAAA,MACZ,UAAA,EAAY,QAAA;AAAA,MACZ,SAAA,EAAW,OAAA;AAAA,MACX,IAAA;AAAA,MACA,SAAA;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,cAAA,EAAA,EAAe,UAAqB,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBAC3C,IAAA,CAACC,MAAA,EAAA,EAAW,GAAA,EAAU,SAAA,EAAW,eAAO,OAAA,EACtC,QAAA,EAAA;AAAA,0BAAA,GAAA,CAACC,WAAA,EAAA,EAAgB,SAAA,EAAW,cAAA,CAAO,KAAA,EAAO,CAAA;AAAA,0BAC1C,GAAA,CAAC,QAAK,IAAA,EAAK,SAAA,EAAU,WAAW,cAAA,CAAO,OAAA,EACrC,QAAA,kBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,CAAA,EACf;AAAA,SAAA,EACF,CAAA;AAAA,wBACA,GAAA,CAAC,wBAAsB,QAAA,EAAA,WAAA,EAAY,CAAA;AAAA,wBACnC,GAAA,CAAC,kBAAgB,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBACvB,GAAA,CAACC,WAAY,SAAA,EAAW,cAAA,CAAO,SAAS,SAAA,EAAU,cAAA,EAChD,8BAACC,OAAA,EAAA,EAAY,SAAA,EAAW,UAAU,cAAA,CAAO,IAAI,GAAG,KAAA,EAAc,gBAAA,EAAkB,MAAM,WAAA,CAAY,YAAY,CAAA,EAC3G,QAAA,EAAA,YAAA,EACH,CAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;AAEA,MAAA,CAAO,WAAA,GAAc,QAAA","file":"chunk-WDWTQKIJ.js","sourcesContent":["/*\n * Every value is a token (--picker-*); the options read --listbox-*. The\n * trigger is a button drawn like a text field. The popover is positioned by\n * React Aria; the gap is its own padding and --trigger-width (vendor) sizes it.\n */\n\n@layer components {\n .root {\n --_height: var(--picker-height-md);\n --_font-size: var(--picker-font-size-md);\n --_border: var(--picker-border-default);\n\n font-family: var(--picker-font-family);\n\n &[data-size=\"sm\"] {\n --_height: var(--picker-height-sm);\n --_font-size: var(--picker-font-size-sm);\n }\n\n &[data-size=\"lg\"] {\n --_height: var(--picker-height-lg);\n --_font-size: var(--picker-font-size-lg);\n }\n\n &[data-invalid] { --_border: var(--picker-border-invalid); }\n }\n\n .trigger {\n display: flex;\n align-items: center;\n gap: var(--picker-gap);\n inline-size: 100%;\n min-block-size: var(--_height);\n padding-block: 0;\n padding-inline: var(--picker-padding-inline);\n border: var(--size-hairline) solid var(--_border);\n border-radius: var(--picker-radius);\n background-color: var(--picker-bg);\n color: var(--picker-fg);\n font-family: inherit;\n font-size: var(--_font-size);\n text-align: start;\n cursor: pointer;\n transition: border-color var(--picker-motion-duration) var(--motion-easing-standard);\n\n &[data-focus-visible] {\n border-color: var(--picker-border-focus);\n outline: var(--picker-focus-ring-width) solid var(--picker-focus-ring);\n outline-offset: var(--picker-focus-ring-offset);\n }\n\n &[data-disabled] {\n cursor: not-allowed;\n opacity: 0.55;\n }\n\n &[data-hovered]:not([data-focus-visible], [data-disabled]) {\n border-color: var(--picker-border-hover);\n }\n }\n\n .value {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n\n &[data-placeholder] {\n color: var(--picker-placeholder);\n }\n }\n\n .chevron {\n flex: none;\n color: var(--picker-icon);\n }\n\n .popover {\n inline-size: var(--trigger-width);\n padding-block-start: var(--picker-gap);\n animation: fade var(--picker-popover-duration) var(--motion-easing-standard);\n\n &[data-placement=\"top\"] {\n padding-block: 0 var(--picker-gap);\n }\n }\n\n .list {\n box-shadow: var(--picker-popover-shadow);\n }\n\n @keyframes fade {\n from {\n opacity: 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .popover {\n animation: none;\n }\n }\n}\n","import type { ReactNode, Ref } from 'react';\nimport { Button as AriaButton, ListBox as AriaListBox, Popover as AriaPopover, Select as AriaSelect, SelectValue as AriaSelectValue } from 'react-aria-components';\nimport { ChevronDown } from '@pearpages/pulp-icons';\nimport { Icon } from '../icon';\nimport { AriaFieldDescription, AriaFieldError, AriaFieldLabel, ariaFieldClass, isPresent } from '../internal/AriaField';\nimport { listClass, renderEmpty, renderOption, type ListboxItem } from '../listbox/ListboxOptions';\nimport styles from './Picker.module.css';\n\nexport type PickerItem = ListboxItem;\nexport type PickerSize = 'sm' | 'md' | 'lg';\n\nexport interface PickerProps {\n /** Visible label. Always rendered. */\n label: ReactNode;\n description?: ReactNode;\n /** Presence marks the control invalid and announces the message. */\n error?: ReactNode;\n /** The options. Ids are the values. */\n items: readonly PickerItem[];\n /** Selected id (controlled). `null` is \"nothing selected\". */\n value?: string | null;\n defaultValue?: string | null;\n onChange?: (value: string | null) => void;\n /** Shown in the control until a value is chosen. @default 'Select…' */\n placeholder?: string;\n /** Shown when `items` is empty. @default 'No options' */\n emptyMessage?: ReactNode;\n /** @default 'md' */\n size?: PickerSize;\n /** @default false */\n required?: boolean;\n /** @default false */\n disabled?: boolean;\n /** Submitted with a form through a hidden native select. */\n name?: string;\n autoFocus?: boolean;\n /** Applied to the field wrapper, not the button. */\n className?: string;\n /** The trigger `<button>`. */\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * A single-choice select with rich options (a description line, consistent\n * styling in every browser), on React Aria's select (decision record 001):\n * a button opens a listbox, arrows and typing move, Enter chooses, Escape\n * closes, and a hidden native select carries the value in forms. For a\n * plain list of words, the native `Select` is the default; for several\n * choices at once, use Listbox.\n *\n * @status experimental\n * @category Forms\n * @accessibility A `button` named by the label with `aria-haspopup=\"listbox\"` and `aria-expanded`; the popup is a `listbox` with selected and disabled options; a hidden native select carries `name` and value for forms. Arrows open and move, typing jumps, Enter chooses, Escape closes and returns focus.\n * @do Use a `description` line on options when the label alone is ambiguous.\n * Prefer the native Select for a plain list of words.\n * @dont Use `placeholder` as the only label.\n */\nexport function Picker({\n label,\n description,\n error,\n items,\n value,\n defaultValue,\n onChange,\n placeholder = 'Select…',\n emptyMessage = 'No options',\n size = 'md',\n required = false,\n disabled = false,\n name,\n autoFocus,\n className,\n ref,\n}: PickerProps) {\n const invalid = isPresent(error);\n return (\n <AriaSelect\n className={ariaFieldClass(styles.root, className)}\n data-size={size}\n selectedKey={value}\n defaultSelectedKey={defaultValue}\n onSelectionChange={(key) => onChange?.(key === null ? null : String(key))}\n placeholder={placeholder}\n isRequired={required}\n isDisabled={disabled}\n isInvalid={invalid}\n name={name}\n autoFocus={autoFocus}\n >\n <AriaFieldLabel required={required}>{label}</AriaFieldLabel>\n <AriaButton ref={ref} className={styles.trigger}>\n <AriaSelectValue className={styles.value} />\n <Icon size=\"inherit\" className={styles.chevron}>\n <ChevronDown />\n </Icon>\n </AriaButton>\n <AriaFieldDescription>{description}</AriaFieldDescription>\n <AriaFieldError>{error}</AriaFieldError>\n <AriaPopover className={styles.popover} placement=\"bottom start\">\n <AriaListBox className={listClass(styles.list)} items={items} renderEmptyState={() => renderEmpty(emptyMessage)}>\n {renderOption}\n </AriaListBox>\n </AriaPopover>\n </AriaSelect>\n );\n}\n\nPicker.displayName = 'Picker';\n"]}
@@ -0,0 +1,28 @@
1
+ import { Dialog } from './chunk-IDNGNQNV.js';
2
+ import { classes } from './chunk-MI4QUKQS.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ // src/sheet/Sheet.module.css
6
+ var Sheet_default = {
7
+ content: "Sheet_content"
8
+ };
9
+ function Sheet(props) {
10
+ return /* @__PURE__ */ jsx(Dialog, { ...props });
11
+ }
12
+ function SheetContent({ placement = "end", className, ...rest }) {
13
+ return /* @__PURE__ */ jsx(Dialog.Content, { ...rest, placement, className: classes(Sheet_default.content, className) });
14
+ }
15
+ Sheet.displayName = "Sheet";
16
+ SheetContent.displayName = "Sheet.Content";
17
+ Sheet.Trigger = Dialog.Trigger;
18
+ Sheet.Content = SheetContent;
19
+ Sheet.Header = Dialog.Header;
20
+ Sheet.Title = Dialog.Title;
21
+ Sheet.Description = Dialog.Description;
22
+ Sheet.Close = Dialog.Close;
23
+ Sheet.Body = Dialog.Body;
24
+ Sheet.Footer = Dialog.Footer;
25
+
26
+ export { Sheet };
27
+ //# sourceMappingURL=chunk-XUROYTJ7.js.map
28
+ //# sourceMappingURL=chunk-XUROYTJ7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sheet/Sheet.module.css","../src/sheet/Sheet.tsx"],"names":["content"],"mappings":";;;;;AAAA,IAAA,aAAA,GAAA;AAAA,EASG,OAAA,EAAAA;AAAA,CAAA;AC8BI,SAAS,MAAM,KAAA,EAAmB;AACvC,EAAA,uBAAO,GAAA,CAAC,MAAA,EAAA,EAAQ,GAAG,KAAA,EAAO,CAAA;AAC5B;AAMA,SAAS,aAAa,EAAE,SAAA,GAAY,OAAO,SAAA,EAAW,GAAG,MAAK,EAAsB;AAClF,EAAA,uBAAO,GAAA,CAAC,MAAA,CAAO,OAAA,EAAP,EAAgB,GAAG,IAAA,EAAM,SAAA,EAAsB,SAAA,EAAW,OAAA,CAAQ,aAAA,CAAO,OAAA,EAAS,SAAS,CAAA,EAAG,CAAA;AACxG;AAEA,KAAA,CAAM,WAAA,GAAc,OAAA;AACpB,YAAA,CAAa,WAAA,GAAc,eAAA;AAC3B,KAAA,CAAM,UAAU,MAAA,CAAO,OAAA;AACvB,KAAA,CAAM,OAAA,GAAU,YAAA;AAChB,KAAA,CAAM,SAAS,MAAA,CAAO,MAAA;AACtB,KAAA,CAAM,QAAQ,MAAA,CAAO,KAAA;AACrB,KAAA,CAAM,cAAc,MAAA,CAAO,WAAA;AAC3B,KAAA,CAAM,QAAQ,MAAA,CAAO,KAAA;AACrB,KAAA,CAAM,OAAO,MAAA,CAAO,IAAA;AACpB,KAAA,CAAM,SAAS,MAAA,CAAO,MAAA","file":"chunk-XUROYTJ7.js","sourcesContent":["/*\n * The vendor (@pearpages/modals) docks the dialog and sizes it from three variables it declares on\n * :root. This class sits on the dialog element itself, so its own declarations beat the inherited\n * :root defaults whatever the cascade layer or import order: no specificity fight with the\n * vendor's unlayered rules, which only *read* these. Colour, type and motion timing come from\n * Dialog's mapping on the portal root.\n */\n\n@layer components {\n .content {\n --modal-width-sheet: var(--sheet-width);\n --modal-height-sheet: var(--sheet-height);\n --modal-animation-translate-sheet: var(--sheet-motion-translate);\n }\n}\n","import type { ComponentProps } from 'react';\nimport { Dialog, type DialogProps } from '../dialog';\nimport { classes } from '../internal/classes';\nimport styles from './Sheet.module.css';\n\nexport type SheetProps = DialogProps;\n\n/** The edge a sheet docks to. `start` and `end` follow the writing direction. */\nexport type SheetPlacement = 'start' | 'end' | 'top' | 'bottom';\n\nexport interface SheetContentProps extends Omit<ComponentProps<typeof Dialog.Content>, 'placement'> {\n /**\n * The edge the sheet docks to. `start` and `end` take `--sheet-width` and the full height;\n * `top` and `bottom` take the full width and `--sheet-height`. There is no `center`: a centred\n * sheet is a Dialog.\n * @default 'end'\n */\n placement?: SheetPlacement;\n}\n\n/**\n * A dialog docked to an edge of the viewport: a side panel for detail or settings that keeps the\n * page in view, or a bottom sheet on a phone. It is Dialog with a `placement`: the same parts\n * (`Sheet.Trigger`, `Sheet.Content`, `Sheet.Header`, `Sheet.Title`, `Sheet.Description`,\n * `Sheet.Close`, `Sheet.Body`, `Sheet.Footer`), the same focus trap, stacking and dismissal, and\n * the same colours and type, from Dialog's tokens. Only the size is Sheet's: `--sheet-width`,\n * `--sheet-height` and `--sheet-motion-translate`, the distance it slides in from its edge.\n *\n * It needs what Dialog needs: `DialogSystem` mounted once near the app root, and\n * `@pearpages/modals/styles.css` imported into the `vendor` layer.\n *\n * @status experimental\n * @category Overlays\n * @accessibility Dialog's: `role=\"dialog\"` with `aria-modal`, named by `Sheet.Title` and described by `Sheet.Description`; focus moves in on open, is trapped, and returns to the trigger on close; Escape and the backdrop dismiss it; the page behind is inert. Docking changes the layout only, never the semantics.\n * @do Always render a `Sheet.Title`.\n * @do Dock to `end` for detail and settings beside the page, and to `bottom` for choices on a narrow screen.\n * @dont Use it for a short confirmation; a centred Dialog reads as more urgent and takes less room.\n * @dont Put the page's primary content in a sheet: it is modal, so everything behind it is out of reach.\n */\nexport function Sheet(props: SheetProps) {\n return <Dialog {...props} />;\n}\n\n/**\n * The docked dialog element. Dialog's `Content`, with `placement` and the class that maps pulp's\n * `--sheet-*` tokens onto the vendor's sheet variables.\n */\nfunction SheetContent({ placement = 'end', className, ...rest }: SheetContentProps) {\n return <Dialog.Content {...rest} placement={placement} className={classes(styles.content, className)} />;\n}\n\nSheet.displayName = 'Sheet';\nSheetContent.displayName = 'Sheet.Content';\nSheet.Trigger = Dialog.Trigger;\nSheet.Content = SheetContent;\nSheet.Header = Dialog.Header;\nSheet.Title = Dialog.Title;\nSheet.Description = Dialog.Description;\nSheet.Close = Dialog.Close;\nSheet.Body = Dialog.Body;\nSheet.Footer = Dialog.Footer;\n"]}
package/dist/combobox.js CHANGED
@@ -1,9 +1,9 @@
1
- export { Combobox } from './chunk-Q6J6BMSB.js';
1
+ export { Combobox } from './chunk-GUZMLYST.js';
2
2
  import './chunk-I4KC4V6A.js';
3
3
  import './chunk-Z4PN6B2C.js';
4
+ import './chunk-T76YSNTZ.js';
4
5
  import './chunk-4HRNRTXI.js';
5
6
  import './chunk-YUHS5S5Y.js';
6
- import './chunk-T76YSNTZ.js';
7
7
  import './chunk-ASNTNTJW.js';
8
8
  import './chunk-MI4QUKQS.js';
9
9
  //# sourceMappingURL=combobox.js.map