twico-ui 1.3.2 → 1.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/llms.txt CHANGED
@@ -291,6 +291,8 @@ Props:
291
291
  - `children` — `React.ReactNode` — Main content; fills the remaining space and scrolls independently of the fixed sidebar and header.
292
292
  - `height` — `string | number` · default `"100dvh"` — Overall shell height; pass a fixed value to embed the shell inside a smaller region (e.g. a demo).
293
293
  - `padded` — `boolean` · default `true` — Pads the scrollable content region with the standard page padding; set false for edge-to-edge content.
294
+ - `sidebarOpen` — `boolean` — Open state of the sidebar's off-canvas drawer on mobile; providing this (or onSidebarOpenChange) makes the shell forward overlay/open/onOpenChange to the sidebar element.
295
+ - `onSidebarOpenChange` — `(open: boolean) => void` — Called when the sidebar drawer requests open or close; wire a Navbar's onMenuClick to () => onSidebarOpenChange(true) for the mobile menu button.
294
296
 
295
297
  ```jsx
296
298
  import { AppShell, Sidebar, Button } from "twico-ui";
@@ -1330,14 +1332,16 @@ Props:
1330
1332
  - `rowKey` — `(row,index)=>key` · default `(r,i)=>r.id??i` — Returns a stable unique key per row, defaulting to the row's id or its index, to keep selection and renders consistent.
1331
1333
  - `checkboxSelection` — `boolean` · default `false` — Adds a leading checkbox column for selecting rows, enabling batch actions; off by default.
1332
1334
  - `batchActions` — `DatatableBatchAction array` · default `[]` — Defines actions shown in the selection toolbar that operate on the currently selected rows; empty by default.
1333
- - `density` — `compact|standard|comfortable` · default `standard` — Sets the row height preset (compact, standard, or comfortable) to trade vertical space for readability, defaulting to standard.
1335
+ - `density` — `compact|standard|comfortable` · default `comfortable` — Sets the row height preset (compact, standard, or comfortable) to trade vertical space for readability, defaulting to comfortable.
1334
1336
  - `pageSize` — `number` · default `10` — Sets the initial number of rows shown per page, defaulting to 10; pass 0 to disable pagination.
1335
1337
  - `pageSizeOptions` — `number array` · default `[5,10,25,50]` — Lists the selectable rows-per-page values offered in the pagination control, defaulting to [5,10,25,50].
1336
1338
  - `height` — `number` · default `440` — Sets the maximum scroll height of the grid body in pixels before rows scroll internally, defaulting to 440.
1337
1339
  - `serverMode` — `boolean` · default `false` — Switches to server-side data handling so sorting, filtering, and paging are delegated to your backend; off by default.
1338
1340
  - `rowCount` — `number` · default `-` — Tells the grid the total number of server-side rows so pagination can render correctly in serverMode.
1339
1341
  - `onServerChange` — `(state)=>void` · default `-` — Fires with the current query state (sort, filter, page) in serverMode so you can fetch the matching rows.
1340
- - `showExport` — `boolean` · default `true` — Shows the toolbar Export button for downloading data in supported formats; enabled by default.
1342
+ - `showExport` — `boolean` · default `false` — Shows the toolbar Export button (CSV split button with a format menu); hidden by default — enable it to expose exporting.
1343
+ - `showDensity` — `boolean` · default `false` — Shows the toolbar density button that cycles compact/standard/comfortable; hidden by default (the density prop still sets the row height).
1344
+ - `showPivot` — `boolean` · default `false` — Shows the toolbar Pivot button; hidden by default, but appears automatically when a pivot model or pivotMode is provided.
1341
1345
  - `exportFilename` — `string` · default `export` — Sets the base filename used for exported files, defaulting to "export".
1342
1346
  - `aggregationValues` — `object|null` · default `null` — Supplies precomputed footer aggregation values for serverMode, where totals cannot be calculated client-side; null by default.
1343
1347
  - `disableColumnReorder` — `boolean` · default `false` — Prevents users from dragging columns into a new order, locking the column arrangement; off by default.
@@ -1351,7 +1355,7 @@ Props:
1351
1355
  - `onRowClick` — `(row,key)=>void` · default `-` — Fires with the clicked row and its key, useful for navigation or opening a detail view.
1352
1356
  - `onCellClick` — `(value,row,field)=>void` · default `-` — Fires with the clicked cell's value, its row, and the field name when a cell is clicked.
1353
1357
  - `onActiveCellChange` — `(cell|null)=>void` · default `-` — Fires when the active cell changes via keyboard or click, receiving the new cell descriptor or null.
1354
- - `showAggregation` — `boolean` · default `false` — Shows a totals row with column aggregations beneath the data on initial render; off by default.
1358
+ - `showAggregation` — `boolean` · default `false` — Shows the toolbar Aggregation button and starts with the totals row on; hidden by default, so enable it to expose column aggregations.
1355
1359
  - `ariaLabel` — `string` · default `Data table` — Sets the grid's accessible aria-label for screen readers, defaulting to "Data table".
1356
1360
  - `rowGrouping` — `string array` · default `[]` — Lists the field names to group rows by on initial render, collapsing rows into expandable groups; empty by default.
1357
1361
  - `rowPinning` — `boolean` · default `false` — Allows users to pin rows to the top or bottom so they stay visible while scrolling; off by default.
@@ -1453,6 +1457,8 @@ export default function Example() {
1453
1457
  onRowUpdate={(updated) => setRows((d) => d.map((r) => (r.id === updated.id ? updated : r)))}
1454
1458
  onBatchUpdate={(changed) => setRows((d) => d.map((r) => changed.find((c) => c.id === r.id) || r))}
1455
1459
  showAggregation
1460
+ showDensity
1461
+ showExport
1456
1462
  rowPinning
1457
1463
  pageSize={10}
1458
1464
  />
@@ -1770,6 +1776,11 @@ Props:
1770
1776
  - `defaultCollapsed` — `boolean` · default `false` — Initial collapsed state for uncontrolled usage, defaulting to false so the sidebar starts fully expanded.
1771
1777
  - `collapsible` — `boolean` · default `true` — Shows the collapse toggle button when true (the default), allowing users to switch to the icon-only rail.
1772
1778
  - `onCollapsedChange` — `(collapsed: boolean) => void` — Fires with the new boolean state whenever the collapse toggle is activated, useful for persisting the preference.
1779
+ - `overlay` — `boolean` · default `false` — Renders the sidebar as an off-canvas drawer instead of an in-flow rail: a fixed slide-over panel behind a dismissable backdrop, portaled to the body, with a focus trap, Escape-to-close, and scroll lock.
1780
+ - `open` — `boolean` — Controlled open state of the overlay drawer (only meaningful when overlay is set); the parent owns visibility.
1781
+ - `defaultOpen` — `boolean` · default `false` — Initial open state of the overlay drawer for uncontrolled usage, defaulting to closed.
1782
+ - `onOpenChange` — `(open: boolean) => void` — Fires when the overlay drawer requests open or close (backdrop click or Escape); wire it to your open state.
1783
+ - `navLabel` — `string` · default `"Main"` — Accessible name for the inner nav landmark and the overlay dialog, so assistive tech can distinguish multiple navigations.
1773
1784
 
1774
1785
  ```jsx
1775
1786
  import { Sidebar } from "twico-ui";
@@ -2275,7 +2286,7 @@ function Page() {
2275
2286
  }
2276
2287
  ```
2277
2288
 
2278
- ## Hooks (23)
2289
+ ## Hooks (25)
2279
2290
 
2280
2291
  ### useIsomorphicLayoutEffect
2281
2292
 
@@ -2319,18 +2330,18 @@ export function useDisclosure(initial?: boolean): { open: boolean; onOpen: () =>
2319
2330
 
2320
2331
  ### useControllableState
2321
2332
 
2322
- Controlled/uncontrolled state — the pattern every form component needs.
2333
+ Controlled/uncontrolled state — the pattern every form component needs. Returns `[value, setValue, isControlled]`; the third element reports whether a `value` prop is currently driving the state. In development it warns once if a component switches between controlled and uncontrolled.
2323
2334
 
2324
2335
  ```ts
2325
- export function useControllableState<T>(options?: { value?: T; defaultValue?: T; onChange?: (value: T) => void; }): [T, (next: T | ((prev: T) => T)) => void];
2336
+ export function useControllableState<T>(options?: { value?: T; defaultValue?: T; onChange?: (value: T) => void; }): [T, (next: T | ((prev: T) => T)) => void, boolean];
2326
2337
  ```
2327
2338
 
2328
2339
  ### useMediaQuery
2329
2340
 
2330
- Reactively match a media query (SSR-safe).
2341
+ Reactively match a media query. Returns `defaultValue` on the server and the first client render, then syncs after mount — so hydration never mismatches.
2331
2342
 
2332
2343
  ```ts
2333
- export function useMediaQuery(query: string): boolean;
2344
+ export function useMediaQuery(query: string, options?: UseMediaQueryOptions): boolean;
2334
2345
  ```
2335
2346
 
2336
2347
  ### usePrefersReducedMotion
@@ -2354,7 +2365,7 @@ export function useColorScheme(options?: UseColorSchemeOptions): { theme: "light
2354
2365
  Attach an event listener (window by default, or a ref/element) with cleanup.
2355
2366
 
2356
2367
  ```ts
2357
- export function useEventListener( eventName: string, handler: (event: any) => void, element?: React.RefObject<any> | EventTarget | null, options?: boolean | AddEventListenerOptions ): void;
2368
+ export function useEventListener( eventName: string, handler: (event: any) => void, element?: React.RefObject<any | null> | EventTarget | null, options?: boolean | AddEventListenerOptions ): void;
2358
2369
  ```
2359
2370
 
2360
2371
  ### useClickOutside
@@ -2362,7 +2373,7 @@ export function useEventListener( eventName: string, handler: (event: any) => vo
2362
2373
  Call `handler` when a pointer/touch event lands outside `ref`.
2363
2374
 
2364
2375
  ```ts
2365
- export function useClickOutside<T extends HTMLElement = HTMLElement>( ref: React.RefObject<T>, handler: (event: Event) => void, events?: string[] ): void;
2376
+ export function useClickOutside<T extends HTMLElement = HTMLElement>( ref: React.RefObject<T | null>, handler: (event: Event) => void, events?: string[] ): void;
2366
2377
  ```
2367
2378
 
2368
2379
  ### useKeyPress
@@ -2434,7 +2445,7 @@ export function useWindowSize(): { width: number; height: number };
2434
2445
  `true` while the pointer is over the element referenced by `ref`.
2435
2446
 
2436
2447
  ```ts
2437
- export function useHover<T extends HTMLElement = HTMLElement>(ref: React.RefObject<T>): boolean;
2448
+ export function useHover<T extends HTMLElement = HTMLElement>(ref: React.RefObject<T | null>): boolean;
2438
2449
  ```
2439
2450
 
2440
2451
  ### useIntersectionObserver
@@ -2442,7 +2453,7 @@ export function useHover<T extends HTMLElement = HTMLElement>(ref: React.RefObje
2442
2453
  The latest IntersectionObserver entry for the element referenced by `ref`.
2443
2454
 
2444
2455
  ```ts
2445
- export function useIntersectionObserver<T extends HTMLElement = HTMLElement>( ref: React.RefObject<T>, options?: IntersectionObserverInit ): IntersectionObserverEntry | null;
2456
+ export function useIntersectionObserver<T extends HTMLElement = HTMLElement>( ref: React.RefObject<T | null>, options?: IntersectionObserverInit ): IntersectionObserverEntry | null;
2446
2457
  ```
2447
2458
 
2448
2459
  ### useScrollLock
@@ -2461,6 +2472,22 @@ A stable, SSR-safe unique id (optionally prefixed).
2461
2472
  export function useId(prefix?: string): string;
2462
2473
  ```
2463
2474
 
2475
+ ### useFocusTrap
2476
+
2477
+ Trap focus inside the modal region referenced by `ref`. While `active`, focus moves inside on activate, Tab/Shift+Tab cycle within it, and (by default) focus is restored to the previously-focused element on deactivate. Escape is NOT handled — that stays with the component. SSR-safe.
2478
+
2479
+ ```ts
2480
+ export function useFocusTrap<T extends HTMLElement = HTMLElement>( ref: React.RefObject<T | null>, active?: boolean, options?: UseFocusTrapOptions ): void;
2481
+ ```
2482
+
2483
+ ### usePortal
2484
+
2485
+ Returns a stable `render(node)` that portals `node` to `document.body`, or `null` on the server (no document). Centralizes the overlay portal pattern.
2486
+
2487
+ ```ts
2488
+ export function usePortal(): (node: React.ReactNode) => React.ReactPortal | React.ReactNode | null;
2489
+ ```
2490
+
2464
2491
  ---
2465
2492
 
2466
2493
  _Generated by `scripts/gen-llms.mjs`. Do not edit by hand — run `npm run gen:llms`. Source of truth: the component `.d.ts` files and `hooks/index.d.ts`._
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "twico-ui",
3
- "version": "1.3.2",
3
+ "version": "1.5.0",
4
4
  "description": "Twico UI — a free, modern, themeable React + Tailwind-friendly component library. 61 components with dark mode, motion, and accessibility built in.",
5
5
  "license": "MIT",
6
6
  "author": "Twico UI",
@@ -82,6 +82,8 @@
82
82
  "gen:llms:check": "node scripts/gen-llms.mjs --check",
83
83
  "typecheck": "tsc --noEmit",
84
84
  "check:exports": "attw --pack . --profile node16 --exclude-entrypoints styles.css",
85
+ "gen:ds-bundle": "node scripts/gen-ds-bundle.mjs",
86
+ "check:ds-bundle": "node scripts/check-ds-bundle.mjs",
85
87
  "test": "vitest",
86
88
  "test:run": "vitest run",
87
89
  "size": "size-limit",
@@ -169,8 +169,9 @@
169
169
  --color-primary-subtle-fg: var(--brand-700);
170
170
  --color-primary-border: var(--brand-200);
171
171
 
172
- /* Focus ring */
173
- --color-ring: rgb(99 102 241 / 0.45);
172
+ /* Focus ring — solid brand so the 3px ring clears WCAG 2.4.11 / 1.4.11 (3:1)
173
+ on light surfaces (a translucent indigo alpha-composited to only ~1.8:1). */
174
+ --color-ring: var(--brand-500);
174
175
 
175
176
  /* Success */
176
177
  --color-success: var(--emerald-600);
@@ -180,7 +181,7 @@
180
181
 
181
182
  /* Warning */
182
183
  --color-warning: var(--amber-500);
183
- --color-warning-fg: #ffffff;
184
+ --color-warning-fg: var(--amber-950); /* dark ink on amber-500 = 6.97:1 (white was 2.15:1) */
184
185
  --color-warning-subtle: var(--amber-50);
185
186
  --color-warning-subtle-fg: var(--amber-600);
186
187
 
@@ -193,7 +194,7 @@
193
194
 
194
195
  /* Info */
195
196
  --color-info: var(--sky-500);
196
- --color-info-fg: #ffffff;
197
+ --color-info-fg: var(--sky-950); /* dark ink on sky-500 (white was 2.77:1) */
197
198
  --color-info-subtle: var(--sky-50);
198
199
  --color-info-subtle-fg: var(--sky-600);
199
200
  }
@@ -229,7 +230,7 @@
229
230
  --color-primary-subtle-fg: var(--brand-300);
230
231
  --color-primary-border: rgb(99 102 241 / 0.35);
231
232
 
232
- --color-ring: rgb(129 140 248 / 0.55);
233
+ --color-ring: var(--brand-400); /* solid brand-400 ≈ 5.7:1 on slate-900 (translucent was ~2.7:1) */
233
234
 
234
235
  --color-success: var(--emerald-500);
235
236
  --color-success-fg: #052e1d;
@@ -564,6 +565,15 @@ textarea:focus-visible,
564
565
  select:focus-visible {
565
566
  box-shadow: none;
566
567
  }
568
+ /* Windows High Contrast / forced-colors strips author box-shadows, which would
569
+ erase the box-shadow focus ring entirely. Repaint a real outline in the user's
570
+ system palette (CanvasText) — the designed ring is untouched in normal render. */
571
+ @media (forced-colors: active) {
572
+ :focus-visible {
573
+ outline: 2px solid CanvasText;
574
+ outline-offset: 2px;
575
+ }
576
+ }
567
577
 
568
578
  ::selection {
569
579
  background: var(--color-primary-subtle);