@campfire-interactive/ui 0.4.1 → 0.5.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/dist/index.d.ts +86 -1
- package/dist/index.js +39 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
import * as RadixMenu from '@radix-ui/react-dropdown-menu';
|
|
3
4
|
import * as RadixCheckbox from '@radix-ui/react-checkbox';
|
|
4
5
|
import * as RadixSwitch from '@radix-ui/react-switch';
|
|
@@ -1353,6 +1354,90 @@ interface PaginationProps {
|
|
|
1353
1354
|
*/
|
|
1354
1355
|
declare function Pagination({ page, pageSize, totalItems, onPageChange, showRange, labels, className, }: PaginationProps): react.JSX.Element | null;
|
|
1355
1356
|
|
|
1357
|
+
interface ListToolbarProps {
|
|
1358
|
+
/** The search field. Normally a `SearchInput`. */
|
|
1359
|
+
search?: ReactNode;
|
|
1360
|
+
/** The filter control. Normally a `FilterBar`. */
|
|
1361
|
+
filters?: ReactNode;
|
|
1362
|
+
/**
|
|
1363
|
+
* The page's actions — the Add button, a column chooser, an export. Pushed
|
|
1364
|
+
* to the right.
|
|
1365
|
+
*/
|
|
1366
|
+
actions?: ReactNode;
|
|
1367
|
+
className?: string;
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* SEARCH, FILTERS AND ACTIONS, in the order every list in the suite wants them.
|
|
1371
|
+
*
|
|
1372
|
+
* ── Why this exists ───────────────────────────────────────────────────────
|
|
1373
|
+
*
|
|
1374
|
+
* `SearchInput`, `FilterBar`, `Table` and `Pagination` were all here already
|
|
1375
|
+
* and every screen wired its own row around them — so they drifted. Within one
|
|
1376
|
+
* app, one table put search left of the filters and another put it right; one
|
|
1377
|
+
* pushed the Add button into the card header and another into the toolbar. The
|
|
1378
|
+
* parts were shared and the arrangement was not, which is the half of
|
|
1379
|
+
* consistency users actually see.
|
|
1380
|
+
*
|
|
1381
|
+
* ── The order is the opinion ──────────────────────────────────────────────
|
|
1382
|
+
*
|
|
1383
|
+
* Search, then filters, then a gap, then actions. Search first because it is
|
|
1384
|
+
* how most people start and it is a text field rather than a value; filters
|
|
1385
|
+
* beside it because they narrow the same list; actions hard right because they
|
|
1386
|
+
* are the only controls here that CHANGE something rather than change what you
|
|
1387
|
+
* are looking at.
|
|
1388
|
+
*
|
|
1389
|
+
* ── What it is not ────────────────────────────────────────────────────────
|
|
1390
|
+
*
|
|
1391
|
+
* Not a data component. It takes no rows, owns no state and knows nothing
|
|
1392
|
+
* about paging — it is a row with three slots and a wrap rule. Pagination
|
|
1393
|
+
* lives UNDER the table, where the rows end, not up here: a pager above the
|
|
1394
|
+
* data is a control for something the reader has not seen yet.
|
|
1395
|
+
*
|
|
1396
|
+
* At phone width the slots stack and each takes the full row, so a search
|
|
1397
|
+
* field never ends up sharing 180px with a filter button.
|
|
1398
|
+
*/
|
|
1399
|
+
declare function ListToolbar({ search, filters, actions, className }: ListToolbarProps): react.JSX.Element;
|
|
1400
|
+
|
|
1401
|
+
interface PagedRows<T> {
|
|
1402
|
+
/** The slice to render. */
|
|
1403
|
+
rows: T[];
|
|
1404
|
+
/** 1-based, for `Pagination`. */
|
|
1405
|
+
page: number;
|
|
1406
|
+
setPage: (page: number) => void;
|
|
1407
|
+
pageSize: number;
|
|
1408
|
+
setPageSize: (size: number) => void;
|
|
1409
|
+
/** The unpaged total, for `Pagination`'s range readout. */
|
|
1410
|
+
totalItems: number;
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* CLIENT-SIDE PAGING over rows already in hand.
|
|
1414
|
+
*
|
|
1415
|
+
* ── When this is the right answer, and when it is not ─────────────────────
|
|
1416
|
+
*
|
|
1417
|
+
* Use it when the whole list is already loaded and the problem is the DOM:
|
|
1418
|
+
* five hundred rows in a table is slow to render, slow to scroll and
|
|
1419
|
+
* impossible to read. It does NOT make a request smaller, and a list that can
|
|
1420
|
+
* outgrow one response needs a paged ENDPOINT instead — this hook over an
|
|
1421
|
+
* unbounded fetch is pagination theatre, and the worst version of it, because
|
|
1422
|
+
* the screen then looks like it handles scale.
|
|
1423
|
+
*
|
|
1424
|
+
* ── The bug this exists to prevent ────────────────────────────────────────
|
|
1425
|
+
*
|
|
1426
|
+
* Page 7 of 9, then type into the search box and the list narrows to three
|
|
1427
|
+
* rows: the table renders EMPTY, because page 7 of a one-page list is past the
|
|
1428
|
+
* end. Every hand-rolled version of this has had it, and it reads as "the
|
|
1429
|
+
* search is broken" rather than as a paging fault.
|
|
1430
|
+
*
|
|
1431
|
+
* So the page is CLAMPED on every render — not reset. Clamping keeps you on
|
|
1432
|
+
* page 3 when a row is deleted from a long list, where a reset would throw you
|
|
1433
|
+
* back to the top for no reason the reader can see; and it still pulls you to
|
|
1434
|
+
* the last real page when the list shrinks under you.
|
|
1435
|
+
*
|
|
1436
|
+
* Changing the page SIZE keeps the first row you were looking at on screen,
|
|
1437
|
+
* rather than keeping the page number and jumping somewhere unrelated.
|
|
1438
|
+
*/
|
|
1439
|
+
declare function usePagedRows<T>(all: readonly T[], initialPageSize?: number): PagedRows<T>;
|
|
1440
|
+
|
|
1356
1441
|
/**
|
|
1357
1442
|
* Date and money formatting, because eleven apps call `toLocaleDateString`
|
|
1358
1443
|
* raw and two wrote their own `formatMoney`.
|
|
@@ -1395,4 +1480,4 @@ declare function formatNumber(value: number | null | undefined, decimals?: numbe
|
|
|
1395
1480
|
*/
|
|
1396
1481
|
declare function formatPercent(value: number | null | undefined, decimals?: number): string;
|
|
1397
1482
|
|
|
1398
|
-
export { Accordion, AccordionContent, AccordionItem, type AccordionProps, AccordionRoot, type AccordionSection, AccordionTrigger, type AccordionTriggerProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Badge, type BadgeProps, type BadgeTone, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardBody, CardDescription, CardFooter, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, type CheckboxProps, type ClassValue, Combobox, type ComboboxOption, type ComboboxProps, ComingSoon, type ComingSoonProps, ConfirmDialog, type ConfirmDialogProps, CountBadge, type CountBadgeProps, CreatableSelect, type CreatableSelectProps, Dialog, DialogBody, DialogClose, DialogContent, type DialogContentProps, DialogDescription, DialogFooter, DialogHeader, DialogPortal, DialogRoot, type DialogSize, DialogTitle, DialogTrigger, EmptyState, type EmptyStateProps, ErrorState, type ErrorStateProps, Field, type FieldProps, Fieldset, type FieldsetProps, FilterBar, type FilterBarProps, type FilterSelect, type FilterToggle, type FilterValue, IconButton, type IconButtonProps, Input, type InputProps, type InputSize, Label, type LabelProps, LoadingState, type LoadingStateProps, Menu, type MenuAction, MenuCheckboxItem, MenuContent, MenuGroup, MenuItem, type MenuItemProps, MenuLabel, type MenuProps, MenuRadioGroup, MenuRadioItem, MenuRoot, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, Modal, type ModalProps, type MoneyOptions, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverRoot, PopoverTrigger, Progress, type ProgressProps, ScrollArea, type ScrollAreaProps, SearchInput, type SearchInputProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, type SelectOption, type SelectProps, SelectRoot, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, Separator, type SeparatorProps, Sheet, SheetBody, SheetClose, SheetContent, type SheetContentProps, SheetDescription, SheetFooter, SheetHeader, SheetPortal, SheetTitle, SheetTrigger, SideModal, Skeleton, type SkeletonProps, Slider, type SliderProps, type SortDirection, type SortState, SplitButton, type SplitButtonProps, Switch, type SwitchProps, type TabDefinition, TabSet, type TabSetProps, Table, TableBody, TableCaption, TableCell, type TableCellProps, TableEmpty, TableFoot, TableFooter, TableHead, type TableHeadProps, TableHeader, type TableProps, TableRow, type TableRowProps, TableScroller, Tabs, TabsContent, TabsList, type TabsListProps, TabsRoot, TabsTrigger, type TabsTriggerProps, Textarea, type TextareaProps, type ToastOptions, ToastProvider, type ToastProviderProps, type ToastTone, ToggleGroup, type ToggleGroupProps, type ToggleOption, Tooltip, TooltipContent, type TooltipContentProps, type TooltipProps, TooltipProvider, TooltipRoot, TooltipTrigger, type UseSortStateResult, cn, formatDate, formatDateTime, formatMoney, formatNumber, formatPercent, useFieldControl, useSortState, useToast };
|
|
1483
|
+
export { Accordion, AccordionContent, AccordionItem, type AccordionProps, AccordionRoot, type AccordionSection, AccordionTrigger, type AccordionTriggerProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Badge, type BadgeProps, type BadgeTone, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardBody, CardDescription, CardFooter, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, Checkbox, type CheckboxProps, type ClassValue, Combobox, type ComboboxOption, type ComboboxProps, ComingSoon, type ComingSoonProps, ConfirmDialog, type ConfirmDialogProps, CountBadge, type CountBadgeProps, CreatableSelect, type CreatableSelectProps, Dialog, DialogBody, DialogClose, DialogContent, type DialogContentProps, DialogDescription, DialogFooter, DialogHeader, DialogPortal, DialogRoot, type DialogSize, DialogTitle, DialogTrigger, EmptyState, type EmptyStateProps, ErrorState, type ErrorStateProps, Field, type FieldProps, Fieldset, type FieldsetProps, FilterBar, type FilterBarProps, type FilterSelect, type FilterToggle, type FilterValue, IconButton, type IconButtonProps, Input, type InputProps, type InputSize, Label, type LabelProps, ListToolbar, type ListToolbarProps, LoadingState, type LoadingStateProps, Menu, type MenuAction, MenuCheckboxItem, MenuContent, MenuGroup, MenuItem, type MenuItemProps, MenuLabel, type MenuProps, MenuRadioGroup, MenuRadioItem, MenuRoot, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, Modal, type ModalProps, type MoneyOptions, type PagedRows, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverRoot, PopoverTrigger, Progress, type ProgressProps, ScrollArea, type ScrollAreaProps, SearchInput, type SearchInputProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, type SelectOption, type SelectProps, SelectRoot, SelectSeparator, SelectTrigger, type SelectTriggerProps, SelectValue, Separator, type SeparatorProps, Sheet, SheetBody, SheetClose, SheetContent, type SheetContentProps, SheetDescription, SheetFooter, SheetHeader, SheetPortal, SheetTitle, SheetTrigger, SideModal, Skeleton, type SkeletonProps, Slider, type SliderProps, type SortDirection, type SortState, SplitButton, type SplitButtonProps, Switch, type SwitchProps, type TabDefinition, TabSet, type TabSetProps, Table, TableBody, TableCaption, TableCell, type TableCellProps, TableEmpty, TableFoot, TableFooter, TableHead, type TableHeadProps, TableHeader, type TableProps, TableRow, type TableRowProps, TableScroller, Tabs, TabsContent, TabsList, type TabsListProps, TabsRoot, TabsTrigger, type TabsTriggerProps, Textarea, type TextareaProps, type ToastOptions, ToastProvider, type ToastProviderProps, type ToastTone, ToggleGroup, type ToggleGroupProps, type ToggleOption, Tooltip, TooltipContent, type TooltipContentProps, type TooltipProps, TooltipProvider, TooltipRoot, TooltipTrigger, type UseSortStateResult, cn, formatDate, formatDateTime, formatMoney, formatNumber, formatPercent, useFieldControl, usePagedRows, useSortState, useToast };
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ function styleInject(css, { insertAt } = {}) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// src/styles/base.css
|
|
24
|
-
styleInject('@layer components {\n :where(:root) {\n --cfi-ui-surface: var(--cfi-surface, #ffffff);\n --cfi-ui-surface-raised: var(--cfi-surface-raised, #ffffff);\n --cfi-ui-surface-sunken: var(--cfi-surface-sunken, #f9fafb);\n --cfi-ui-surface-hover: var(--cfi-surface-hover, #f9fafb);\n --cfi-ui-surface-active: var(--cfi-surface-active, #f3f4f6);\n --cfi-ui-surface-selected: var(--cfi-surface-selected, #f3f4f6);\n --cfi-ui-surface-disabled: var(--cfi-surface-disabled, #f3f4f6);\n --cfi-ui-surface-muted: var(--cfi-surface-muted, #ececf0);\n --cfi-ui-input-bg: var(--cfi-input-bg, #f3f3f5);\n --cfi-ui-switch-bg: var(--cfi-switch-bg, #cbced4);\n --cfi-ui-content: var(--cfi-content, oklch(0.145 0 0));\n --cfi-ui-content-muted: var(--cfi-content-muted, #666676);\n --cfi-ui-content-subtle: var(--cfi-content-subtle, #9ca3af);\n --cfi-ui-content-inverse: var(--cfi-content-inverse, #ffffff);\n --cfi-ui-content-disabled: var(--cfi-content-disabled, #9ca3af);\n --cfi-ui-content-link: var(--cfi-content-link, #1d4ed8);\n --cfi-ui-border: var(--cfi-border, #e5e7eb);\n --cfi-ui-border-subtle: var(--cfi-border-subtle, #f3f4f6);\n --cfi-ui-border-strong: var(--cfi-border-strong, #d1d5db);\n --cfi-ui-accent: var(--cfi-action, #030213);\n --cfi-ui-accent-hover: var(--cfi-action-hover, #1a1a2e);\n --cfi-ui-accent-soft: var(--cfi-action-soft, #f3f4f6);\n --cfi-ui-accent-fg: var(--cfi-action-fg, #ffffff);\n --cfi-ui-success: var(--cfi-color-success, #10b981);\n --cfi-ui-success-soft: var(--cfi-color-success-soft, #ecfdf5);\n --cfi-ui-success-text: var(--cfi-color-success-text, #047857);\n --cfi-ui-success-border: var(--cfi-color-success-border, #a7f3d0);\n --cfi-ui-warning: var(--cfi-color-warning, #f59e0b);\n --cfi-ui-warning-soft: var(--cfi-color-warning-soft, #fffbeb);\n --cfi-ui-warning-text: var(--cfi-color-warning-text, #b45309);\n --cfi-ui-warning-border: var(--cfi-color-warning-border, #fde68a);\n --cfi-ui-error: var(--cfi-color-error, #ef4444);\n --cfi-ui-error-strong: var(--cfi-color-error-strong, #dc2626);\n --cfi-ui-error-soft: var(--cfi-color-error-soft, #fef2f2);\n --cfi-ui-error-text: var(--cfi-color-error-text, #b91c1c);\n --cfi-ui-error-border: var(--cfi-color-error-border, #fecaca);\n --cfi-ui-info: var(--cfi-color-info, #3b82f6);\n --cfi-ui-info-soft: var(--cfi-color-info-soft, #eff6ff);\n --cfi-ui-info-text: var(--cfi-color-info-text, #1d4ed8);\n --cfi-ui-info-border: var(--cfi-color-info-border, #bfdbfe);\n --cfi-ui-neutral-soft: var(--cfi-color-neutral-soft, #f3f4f6);\n --cfi-ui-neutral-text: var(--cfi-color-neutral-text, #374151);\n --cfi-ui-neutral-border: var(--cfi-color-neutral-border, #e5e7eb);\n --cfi-ui-focus-color: var(--cfi-focus-ring-color, var(--cfi-ui-accent));\n --cfi-ui-focus-width: var(--cfi-focus-ring-width, 2px);\n --cfi-ui-focus-offset: var(--cfi-focus-ring-offset, 2px);\n --cfi-ui-overlay: var(--cfi-overlay, rgb(17 24 39 / 0.45));\n --cfi-ui-font: var(--cfi-font-family, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);\n --cfi-ui-font-mono: var(--cfi-font-family-mono, ui-monospace, Consolas, monospace);\n --cfi-ui-text-xs: var(--cfi-font-size-xs, 0.75rem);\n --cfi-ui-text-sm: var(--cfi-font-size-sm, 0.875rem);\n --cfi-ui-text-base: var(--cfi-font-size-base, 1rem);\n --cfi-ui-text-lg: var(--cfi-font-size-lg, 1.125rem);\n --cfi-ui-text-xl: var(--cfi-font-size-xl, 1.25rem);\n --cfi-ui-weight-normal: var(--cfi-font-weight-normal, 400);\n --cfi-ui-weight-medium: var(--cfi-font-weight-medium, 500);\n --cfi-ui-weight-semibold: var(--cfi-font-weight-semibold, 600);\n --cfi-ui-leading-tight: var(--cfi-line-height-tight, 1.2);\n --cfi-ui-leading-snug: var(--cfi-line-height-snug, 1.35);\n --cfi-ui-leading-normal: var(--cfi-line-height-normal, 1.5);\n --cfi-ui-tracking-caps: var(--cfi-letter-spacing-caps, 0.08em);\n --cfi-ui-space-3xs: var(--cfi-spacing-3xs, 0.125rem);\n --cfi-ui-space-2xs: var(--cfi-spacing-2xs, 0.1875rem);\n --cfi-ui-space-xs: var(--cfi-spacing-xs, 0.25rem);\n --cfi-ui-space-sm: var(--cfi-spacing-sm, 0.5rem);\n --cfi-ui-space-md: var(--cfi-spacing-md, 1rem);\n --cfi-ui-space-lg: var(--cfi-spacing-lg, 1.5rem);\n --cfi-ui-space-xl: var(--cfi-spacing-xl, 2rem);\n --cfi-ui-radius-sm: var(--cfi-radius-sm, 0.375rem);\n --cfi-ui-radius-md: var(--cfi-radius-md, 0.5rem);\n --cfi-ui-radius-lg: var(--cfi-radius-lg, 0.625rem);\n --cfi-ui-radius-xl: var(--cfi-radius-xl, 0.875rem);\n --cfi-ui-radius-full: var(--cfi-radius-full, 9999px);\n --cfi-ui-shadow-xs: var(--cfi-shadow-xs, 0 1px 1px rgb(17 24 39 / 0.04));\n --cfi-ui-shadow-sm: var(--cfi-shadow-sm, 0 1px 2px rgb(17 24 39 / 0.05));\n --cfi-ui-shadow-md: var(--cfi-shadow-md, 0 4px 6px rgb(17 24 39 / 0.07));\n --cfi-ui-shadow-lg: var(--cfi-shadow-lg, 0 10px 15px rgb(17 24 39 / 0.1));\n --cfi-ui-shadow-xl: var(--cfi-shadow-xl, 0 20px 40px -12px rgb(17 24 39 / 0.22));\n --cfi-ui-transition-fast: var(--cfi-transition-fast, 120ms cubic-bezier(0.4, 0, 0.2, 1));\n --cfi-ui-transition-base: var(--cfi-transition-base, 200ms cubic-bezier(0.4, 0, 0.2, 1));\n --cfi-ui-z-dropdown: var(--cfi-z-dropdown,
|
|
24
|
+
styleInject('@layer components {\n :where(:root) {\n --cfi-ui-surface: var(--cfi-surface, #ffffff);\n --cfi-ui-surface-raised: var(--cfi-surface-raised, #ffffff);\n --cfi-ui-surface-sunken: var(--cfi-surface-sunken, #f9fafb);\n --cfi-ui-surface-hover: var(--cfi-surface-hover, #f9fafb);\n --cfi-ui-surface-active: var(--cfi-surface-active, #f3f4f6);\n --cfi-ui-surface-selected: var(--cfi-surface-selected, #f3f4f6);\n --cfi-ui-surface-disabled: var(--cfi-surface-disabled, #f3f4f6);\n --cfi-ui-surface-muted: var(--cfi-surface-muted, #ececf0);\n --cfi-ui-input-bg: var(--cfi-input-bg, #f3f3f5);\n --cfi-ui-switch-bg: var(--cfi-switch-bg, #cbced4);\n --cfi-ui-content: var(--cfi-content, oklch(0.145 0 0));\n --cfi-ui-content-muted: var(--cfi-content-muted, #666676);\n --cfi-ui-content-subtle: var(--cfi-content-subtle, #9ca3af);\n --cfi-ui-content-inverse: var(--cfi-content-inverse, #ffffff);\n --cfi-ui-content-disabled: var(--cfi-content-disabled, #9ca3af);\n --cfi-ui-content-link: var(--cfi-content-link, #1d4ed8);\n --cfi-ui-border: var(--cfi-border, #e5e7eb);\n --cfi-ui-border-subtle: var(--cfi-border-subtle, #f3f4f6);\n --cfi-ui-border-strong: var(--cfi-border-strong, #d1d5db);\n --cfi-ui-accent: var(--cfi-action, #030213);\n --cfi-ui-accent-hover: var(--cfi-action-hover, #1a1a2e);\n --cfi-ui-accent-soft: var(--cfi-action-soft, #f3f4f6);\n --cfi-ui-accent-fg: var(--cfi-action-fg, #ffffff);\n --cfi-ui-success: var(--cfi-color-success, #10b981);\n --cfi-ui-success-soft: var(--cfi-color-success-soft, #ecfdf5);\n --cfi-ui-success-text: var(--cfi-color-success-text, #047857);\n --cfi-ui-success-border: var(--cfi-color-success-border, #a7f3d0);\n --cfi-ui-warning: var(--cfi-color-warning, #f59e0b);\n --cfi-ui-warning-soft: var(--cfi-color-warning-soft, #fffbeb);\n --cfi-ui-warning-text: var(--cfi-color-warning-text, #b45309);\n --cfi-ui-warning-border: var(--cfi-color-warning-border, #fde68a);\n --cfi-ui-error: var(--cfi-color-error, #ef4444);\n --cfi-ui-error-strong: var(--cfi-color-error-strong, #dc2626);\n --cfi-ui-error-soft: var(--cfi-color-error-soft, #fef2f2);\n --cfi-ui-error-text: var(--cfi-color-error-text, #b91c1c);\n --cfi-ui-error-border: var(--cfi-color-error-border, #fecaca);\n --cfi-ui-info: var(--cfi-color-info, #3b82f6);\n --cfi-ui-info-soft: var(--cfi-color-info-soft, #eff6ff);\n --cfi-ui-info-text: var(--cfi-color-info-text, #1d4ed8);\n --cfi-ui-info-border: var(--cfi-color-info-border, #bfdbfe);\n --cfi-ui-neutral-soft: var(--cfi-color-neutral-soft, #f3f4f6);\n --cfi-ui-neutral-text: var(--cfi-color-neutral-text, #374151);\n --cfi-ui-neutral-border: var(--cfi-color-neutral-border, #e5e7eb);\n --cfi-ui-focus-color: var(--cfi-focus-ring-color, var(--cfi-ui-accent));\n --cfi-ui-focus-width: var(--cfi-focus-ring-width, 2px);\n --cfi-ui-focus-offset: var(--cfi-focus-ring-offset, 2px);\n --cfi-ui-overlay: var(--cfi-overlay, rgb(17 24 39 / 0.45));\n --cfi-ui-font: var(--cfi-font-family, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);\n --cfi-ui-font-mono: var(--cfi-font-family-mono, ui-monospace, Consolas, monospace);\n --cfi-ui-text-xs: var(--cfi-font-size-xs, 0.75rem);\n --cfi-ui-text-sm: var(--cfi-font-size-sm, 0.875rem);\n --cfi-ui-text-base: var(--cfi-font-size-base, 1rem);\n --cfi-ui-text-lg: var(--cfi-font-size-lg, 1.125rem);\n --cfi-ui-text-xl: var(--cfi-font-size-xl, 1.25rem);\n --cfi-ui-weight-normal: var(--cfi-font-weight-normal, 400);\n --cfi-ui-weight-medium: var(--cfi-font-weight-medium, 500);\n --cfi-ui-weight-semibold: var(--cfi-font-weight-semibold, 600);\n --cfi-ui-leading-tight: var(--cfi-line-height-tight, 1.2);\n --cfi-ui-leading-snug: var(--cfi-line-height-snug, 1.35);\n --cfi-ui-leading-normal: var(--cfi-line-height-normal, 1.5);\n --cfi-ui-tracking-caps: var(--cfi-letter-spacing-caps, 0.08em);\n --cfi-ui-space-3xs: var(--cfi-spacing-3xs, 0.125rem);\n --cfi-ui-space-2xs: var(--cfi-spacing-2xs, 0.1875rem);\n --cfi-ui-space-xs: var(--cfi-spacing-xs, 0.25rem);\n --cfi-ui-space-sm: var(--cfi-spacing-sm, 0.5rem);\n --cfi-ui-space-md: var(--cfi-spacing-md, 1rem);\n --cfi-ui-space-lg: var(--cfi-spacing-lg, 1.5rem);\n --cfi-ui-space-xl: var(--cfi-spacing-xl, 2rem);\n --cfi-ui-radius-sm: var(--cfi-radius-sm, 0.375rem);\n --cfi-ui-radius-md: var(--cfi-radius-md, 0.5rem);\n --cfi-ui-radius-lg: var(--cfi-radius-lg, 0.625rem);\n --cfi-ui-radius-xl: var(--cfi-radius-xl, 0.875rem);\n --cfi-ui-radius-full: var(--cfi-radius-full, 9999px);\n --cfi-ui-shadow-xs: var(--cfi-shadow-xs, 0 1px 1px rgb(17 24 39 / 0.04));\n --cfi-ui-shadow-sm: var(--cfi-shadow-sm, 0 1px 2px rgb(17 24 39 / 0.05));\n --cfi-ui-shadow-md: var(--cfi-shadow-md, 0 4px 6px rgb(17 24 39 / 0.07));\n --cfi-ui-shadow-lg: var(--cfi-shadow-lg, 0 10px 15px rgb(17 24 39 / 0.1));\n --cfi-ui-shadow-xl: var(--cfi-shadow-xl, 0 20px 40px -12px rgb(17 24 39 / 0.22));\n --cfi-ui-transition-fast: var(--cfi-transition-fast, 120ms cubic-bezier(0.4, 0, 0.2, 1));\n --cfi-ui-transition-base: var(--cfi-transition-base, 200ms cubic-bezier(0.4, 0, 0.2, 1));\n --cfi-ui-z-dropdown: max(var(--cfi-z-dropdown, 1350), calc(var(--cfi-z-popover, 1300) + 50));\n --cfi-ui-z-overlay: var(--cfi-z-overlay, 1100);\n --cfi-ui-z-modal: var(--cfi-z-modal, 1200);\n --cfi-ui-z-popover: var(--cfi-z-popover, 1300);\n --cfi-ui-z-toast: var(--cfi-z-toast, 1400);\n --cfi-ui-z-tooltip: var(--cfi-z-tooltip, 1500);\n --cfi-ui-control-height-sm: 2rem;\n --cfi-ui-control-height-md: 2.25rem;\n --cfi-ui-control-height-lg: 2.75rem;\n }\n .cfi-ui {\n box-sizing: border-box;\n font-family: var(--cfi-ui-font);\n }\n .cfi-ui *,\n .cfi-ui *::before,\n .cfi-ui *::after {\n box-sizing: border-box;\n }\n .cfi-ui:focus-visible,\n .cfi-ui :focus-visible {\n outline: var(--cfi-ui-focus-width) solid var(--cfi-ui-focus-color);\n outline-offset: var(--cfi-ui-focus-offset);\n }\n .cfi-ui-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n @keyframes cfi-ui-spin {\n to {\n transform: rotate(360deg);\n }\n }\n @keyframes cfi-ui-pulse {\n 50% {\n opacity: 0.45;\n }\n }\n @keyframes cfi-ui-fade-in {\n from {\n opacity: 0;\n }\n }\n @keyframes cfi-ui-zoom-in {\n from {\n opacity: 0;\n transform: scale(0.97);\n }\n }\n @keyframes cfi-ui-slide-in-right {\n from {\n transform: translateX(100%);\n }\n }\n @keyframes cfi-ui-slide-in-left {\n from {\n transform: translateX(-100%);\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .cfi-ui,\n .cfi-ui *,\n [data-cfi-ui-portal],\n [data-cfi-ui-portal] * {\n animation-duration: 1ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 1ms !important;\n }\n }\n}\n');
|
|
25
25
|
|
|
26
26
|
// src/lib/cn.ts
|
|
27
27
|
function cn(...values) {
|
|
@@ -2041,6 +2041,42 @@ function Pagination({
|
|
|
2041
2041
|
] });
|
|
2042
2042
|
}
|
|
2043
2043
|
|
|
2044
|
+
// src/ListToolbar.css
|
|
2045
|
+
styleInject("@layer components {\n :where(.cfi-ui-list-toolbar) {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--cfi-ui-space-sm);\n min-width: 0;\n }\n .cfi-ui-list-toolbar__search {\n flex: 1 1 14rem;\n min-width: 0;\n max-width: 22rem;\n }\n .cfi-ui-list-toolbar__filters {\n display: flex;\n align-items: center;\n min-width: 0;\n flex: 0 1 auto;\n }\n .cfi-ui-list-toolbar__actions {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-xs);\n margin-inline-start: auto;\n }\n @media (max-width: 40rem) {\n .cfi-ui-list-toolbar__search,\n .cfi-ui-list-toolbar__filters,\n .cfi-ui-list-toolbar__actions {\n flex: 1 1 100%;\n max-width: none;\n margin-inline-start: 0;\n }\n }\n}\n");
|
|
2046
|
+
|
|
2047
|
+
// src/ListToolbar.tsx
|
|
2048
|
+
import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2049
|
+
function ListToolbar({ search, filters, actions, className }) {
|
|
2050
|
+
return /* @__PURE__ */ jsxs25("div", { className: cn("cfi-ui", "cfi-ui-list-toolbar", className), children: [
|
|
2051
|
+
search && /* @__PURE__ */ jsx26("div", { className: "cfi-ui-list-toolbar__search", children: search }),
|
|
2052
|
+
filters && /* @__PURE__ */ jsx26("div", { className: "cfi-ui-list-toolbar__filters", children: filters }),
|
|
2053
|
+
actions && /* @__PURE__ */ jsx26("div", { className: "cfi-ui-list-toolbar__actions", children: actions })
|
|
2054
|
+
] });
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
// src/usePagedRows.ts
|
|
2058
|
+
import { useEffect as useEffect4, useMemo as useMemo6, useState as useState7 } from "react";
|
|
2059
|
+
function usePagedRows(all, initialPageSize = 25) {
|
|
2060
|
+
const [page, setPage] = useState7(1);
|
|
2061
|
+
const [pageSize, setSize] = useState7(initialPageSize);
|
|
2062
|
+
const totalItems = all.length;
|
|
2063
|
+
const pageCount = Math.max(1, Math.ceil(totalItems / Math.max(1, pageSize)));
|
|
2064
|
+
const clamped = Math.min(Math.max(1, page), pageCount);
|
|
2065
|
+
useEffect4(() => {
|
|
2066
|
+
if (clamped !== page) setPage(clamped);
|
|
2067
|
+
}, [clamped, page]);
|
|
2068
|
+
const rows = useMemo6(
|
|
2069
|
+
() => all.slice((clamped - 1) * pageSize, clamped * pageSize),
|
|
2070
|
+
[all, clamped, pageSize]
|
|
2071
|
+
);
|
|
2072
|
+
const setPageSize = (size) => {
|
|
2073
|
+
const firstRow = (clamped - 1) * pageSize;
|
|
2074
|
+
setSize(size);
|
|
2075
|
+
setPage(Math.floor(firstRow / Math.max(1, size)) + 1);
|
|
2076
|
+
};
|
|
2077
|
+
return { rows, page: clamped, setPage, pageSize, setPageSize, totalItems };
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2044
2080
|
// src/format.ts
|
|
2045
2081
|
function formatDate(value) {
|
|
2046
2082
|
const d = toDate(value);
|
|
@@ -2122,6 +2158,7 @@ export {
|
|
|
2122
2158
|
IconButton,
|
|
2123
2159
|
Input,
|
|
2124
2160
|
Label3 as Label,
|
|
2161
|
+
ListToolbar,
|
|
2125
2162
|
LoadingState,
|
|
2126
2163
|
Menu,
|
|
2127
2164
|
MenuCheckboxItem,
|
|
@@ -2205,6 +2242,7 @@ export {
|
|
|
2205
2242
|
formatNumber,
|
|
2206
2243
|
formatPercent,
|
|
2207
2244
|
useFieldControl,
|
|
2245
|
+
usePagedRows,
|
|
2208
2246
|
useSortState,
|
|
2209
2247
|
useToast
|
|
2210
2248
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@campfire-interactive/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Shared UI primitives for the Campfire Suite — Radix behavior, plain CSS styled entirely from the design tokens, no Tailwind required",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|