@milktop/react-ui 0.2.2 → 0.2.4

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
@@ -35,6 +35,13 @@ so a hand-built component looks just like the shortcut:
35
35
  No CLI and no copied snippets: every shortcut ships in the package, and
36
36
  where one doesn't fit you build from the parts.
37
37
 
38
+ Our own parts hang off their component the same way, so one import covers
39
+ them: `Sidebar.Section`, `Sidebar.Item`, `Menu.Item`, `Tabs.Tab`,
40
+ `Accordion.Item`. Where one of ours takes a name Ark also uses, Ark's part
41
+ moves aside (`Accordion.RawItem`). They're also exported by name
42
+ (`NavSection`, `MenuItem`, `Tab`…), for imports by name and, later, server
43
+ components; the dotted form is the one the docs use.
44
+
38
45
  ## Components
39
46
 
40
47
  So far:
@@ -57,18 +64,17 @@ So far:
57
64
  - Forms: Field · Fields
58
65
  - Overlays: Tooltip · Popover · HoverCard · Dialog · Sheet (both: props or
59
66
  composed from Header/Body/Footer) · ActionBar · Menu · ContextMenu (Menu.Item,
60
- Separator, Group, Checkbox, RadioGroup/Radio, Submenu; also as MenuItem…) ·
67
+ Separator, Group, Checkbox, RadioGroup/Radio, Submenu) ·
61
68
  Toaster (`toaster.success(…)` from anywhere) · `confirm()` (`await confirm({ … })`
62
69
  from anywhere, no state or markup)
63
70
  - Scheduling and ordering (React only): EventCalendar (day, n days, work week or
64
71
  week by the hour, with resources such as vets or rooms as columns; drag to
65
72
  select a time, drag or arrow-key events to move and resize them, all-day
66
73
  events, a date jump) · SortableList (drag or keyboard, on dnd-kit)
67
- - Disclosure: Tabs/Tab (with counts and dots, inline or in the corner) · Accordion · Collapsible
74
+ - Disclosure: Tabs (Tabs.Tab, with counts and dots, inline or in the corner) · Accordion · Collapsible
68
75
  - Navigation: AppShell (flat or inset; the sidebar collapses to a rail with ⌘B
69
76
  and is a drawer on phones; inset, or `scroll="page"`, scrolls only the page
70
- on wide screens) · Topbar · Sidebar (NavSection, NavGroup, NavItem,
71
- SidebarUser; also Sidebar.Section…) · Page · Breadcrumbs · Command (⌘K)
77
+ on wide screens) · Topbar · Sidebar (Sidebar.Section, Group, Item, User) · Page · Breadcrumbs · Command (⌘K)
72
78
 
73
79
  That's all of svelte-ui's components.
74
80
 
@@ -11,8 +11,10 @@
11
11
  * the item is listed under), `icon`, `description`, `shortcut` text,
12
12
  * `keywords` (extra words to match) and `disabled`. An item with `href`
13
13
  * goes there when picked
14
- * - `load`: optional async function(query) returning items, for server
15
- * search (debounced by `debounce` ms, `loadingText` meanwhile)
14
+ * - `load`: optional async function(query) returning more items, for server
15
+ * search (debounced by `debounce` ms, `loadingText` meanwhile). They're
16
+ * listed after the matching `items`, which filter as you type while the
17
+ * search runs
16
18
  * - `onSelect`: called with the item's `value` (or the item, if it has none)
17
19
  * - `trigger`: the element that opens it (it gets the trigger's props)
18
20
  * - `open`, `defaultOpen`; `onOpenChange`: called with the new open state
@@ -12,8 +12,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  * the item is listed under), `icon`, `description`, `shortcut` text,
13
13
  * `keywords` (extra words to match) and `disabled`. An item with `href`
14
14
  * goes there when picked
15
- * - `load`: optional async function(query) returning items, for server
16
- * search (debounced by `debounce` ms, `loadingText` meanwhile)
15
+ * - `load`: optional async function(query) returning more items, for server
16
+ * search (debounced by `debounce` ms, `loadingText` meanwhile). They're
17
+ * listed after the matching `items`, which filter as you type while the
18
+ * search runs
17
19
  * - `onSelect`: called with the item's `value` (or the item, if it has none)
18
20
  * - `trigger`: the element that opens it (it gets the trigger's props)
19
21
  * - `open`, `defaultOpen`; `onOpenChange`: called with the new open state
@@ -41,9 +43,11 @@ export function formatHotkey(key = 'k') {
41
43
  return mac ? `⌘${key.toUpperCase()}` : `Ctrl ${key.toUpperCase()}`;
42
44
  }
43
45
  const keyOf = (item) => String(item.value ?? item.href ?? item.label);
46
+ // Lowercase without accents, so "tache" finds "Tâches"
47
+ const fold = (text) => text.normalize('NFD').replace(/\p{M}/gu, '').toLowerCase();
44
48
  // Every word typed must appear in the label, description, group or keywords.
45
49
  function matches(item, words) {
46
- const text = [item.label, item.description, item.group, ...(item.keywords ?? [])].join(' ').toLowerCase();
50
+ const text = fold([item.label, item.description, item.group, ...(item.keywords ?? [])].join(' '));
47
51
  return words.every((word) => text.includes(word));
48
52
  }
49
53
  function go(href) {
@@ -68,8 +72,11 @@ export function Command({ items = [], load = null, debounce = 200, onSelect, tri
68
72
  const listEl = useRef(null);
69
73
  const ticket = useRef(0);
70
74
  const timer = useRef(undefined);
71
- const words = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
72
- const found = load ? loaded : words.length ? items.filter((item) => matches(item, words)) : items;
75
+ // The items filter at once; loaded results from the last search stay
76
+ // below them until the new ones arrive.
77
+ const words = fold(query).split(/\s+/).filter(Boolean);
78
+ const local = words.length ? items.filter((item) => matches(item, words)) : items;
79
+ const found = load ? [...local, ...loaded] : local;
73
80
  // Results in group order (by first appearance), so the list reads and the
74
81
  // arrow keys move the same way.
75
82
  const groups = [...new Set(found.map((item) => item.group ?? ''))]
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Nav items under a label that opens and closes.
3
3
  *
4
- * <NavGroup label="Forms" icon="lucide:form" defaultOpen>
5
- * <NavItem href="/input">Input</NavItem>
6
- * </NavGroup>
4
+ * <Sidebar.Group label="Forms" icon="lucide:form" defaultOpen>
5
+ * <Sidebar.Item href="/input">Input</Sidebar.Item>
6
+ * </Sidebar.Group>
7
7
  *
8
8
  * - `label`: its text; `icon`: an Iconify name
9
9
  * - `open`, `defaultOpen`; `onOpenChange`: called with the new open state.
@@ -2,9 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * Nav items under a label that opens and closes.
4
4
  *
5
- * <NavGroup label="Forms" icon="lucide:form" defaultOpen>
6
- * <NavItem href="/input">Input</NavItem>
7
- * </NavGroup>
5
+ * <Sidebar.Group label="Forms" icon="lucide:form" defaultOpen>
6
+ * <Sidebar.Item href="/input">Input</Sidebar.Item>
7
+ * </Sidebar.Group>
8
8
  *
9
9
  * - `label`: its text; `icon`: an Iconify name
10
10
  * - `open`, `defaultOpen`; `onOpenChange`: called with the new open state.
@@ -2,8 +2,8 @@
2
2
  * A sidebar link (or a button, without `href`). Other props (onClick,
3
3
  * data-*) go on the link.
4
4
  *
5
- * <NavItem icon="lucide:users" href="/students" active={isCurrent('/students')} badge={3}>Students</NavItem>
6
- * <NavItem icon="lucide:users" link={<Link to="/students" />}>Students</NavItem>
5
+ * <Sidebar.Item icon="lucide:users" href="/students" active={isCurrent('/students')} badge={3}>Students</Sidebar.Item>
6
+ * <Sidebar.Item icon="lucide:users" link={<Link to="/students" />}>Students</Sidebar.Item>
7
7
  *
8
8
  * - `icon`: an Iconify name
9
9
  * - `href`: where it goes (without one it's a button: give it `onClick`)
@@ -3,8 +3,8 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
3
3
  * A sidebar link (or a button, without `href`). Other props (onClick,
4
4
  * data-*) go on the link.
5
5
  *
6
- * <NavItem icon="lucide:users" href="/students" active={isCurrent('/students')} badge={3}>Students</NavItem>
7
- * <NavItem icon="lucide:users" link={<Link to="/students" />}>Students</NavItem>
6
+ * <Sidebar.Item icon="lucide:users" href="/students" active={isCurrent('/students')} badge={3}>Students</Sidebar.Item>
7
+ * <Sidebar.Item icon="lucide:users" link={<Link to="/students" />}>Students</Sidebar.Item>
8
8
  *
9
9
  * - `icon`: an Iconify name
10
10
  * - `href`: where it goes (without one it's a button: give it `onClick`)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * A group of nav items, with an optional heading (hidden in the rail).
3
3
  *
4
- * <NavSection heading="Settings" collapsible>…NavGroups…</NavSection>
4
+ * <Sidebar.Section heading="Settings" collapsible>…groups…</Sidebar.Section>
5
5
  *
6
6
  * - `heading`
7
7
  * - `collapsible`: a button beside the heading that collapses (or expands)
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * A group of nav items, with an optional heading (hidden in the rail).
4
4
  *
5
- * <NavSection heading="Settings" collapsible>…NavGroups…</NavSection>
5
+ * <Sidebar.Section heading="Settings" collapsible>…groups…</Sidebar.Section>
6
6
  *
7
7
  * - `heading`
8
8
  * - `collapsible`: a button beside the heading that collapses (or expands)
@@ -2,16 +2,16 @@
2
2
  * An app sidebar: a logo, the nav, and a footer. In an AppShell it collapses
3
3
  * to an icon rail (its button, or ⌘B) and becomes a drawer on phones.
4
4
  *
5
- * <Sidebar logo="TutorApp" footer={<SidebarUser name="Ada Lovelace" items={account} />}>
6
- * <NavSection heading="Menu">
7
- * <NavItem icon="lucide:house" href="/" active>Dashboard</NavItem>
8
- * <NavGroup label="Settings" icon="lucide:settings">…</NavGroup>
9
- * </NavSection>
5
+ * <Sidebar logo="TutorApp" footer={<Sidebar.User name="Ada Lovelace" items={account} />}>
6
+ * <Sidebar.Section heading="Menu">
7
+ * <Sidebar.Item icon="lucide:house" href="/" active>Dashboard</Sidebar.Item>
8
+ * <Sidebar.Group label="Settings" icon="lucide:settings">…</Sidebar.Group>
9
+ * </Sidebar.Section>
10
10
  * </Sidebar>
11
11
  *
12
12
  * - `logo`; `logoCollapsed`: shown instead in the rail (a mark)
13
- * - `footer`: along the bottom (a SidebarUser)
14
- * - `accordion`: one NavGroup open at a time
13
+ * - `footer`: along the bottom (a Sidebar.User)
14
+ * - `accordion`: one Sidebar.Group open at a time
15
15
  * - `items`: the nav as data, in place of (or before) the content:
16
16
  * `[{ heading, items: [{ label, icon, href, active, badge, items }] }]`
17
17
  * - `edge`: a thin strip along the right edge that collapses or expands it
@@ -21,8 +21,8 @@
21
21
  * - restyle its items with the `--ui-sidebar-*` tokens, e.g.
22
22
  * style={{ '--ui-sidebar-active': 'var(--ui-accent-soft)' }}
23
23
  *
24
- * Its parts are on it too: Sidebar.Section, Sidebar.Group, Sidebar.Item and
25
- * Sidebar.User are NavSection, NavGroup, NavItem and SidebarUser.
24
+ * Its parts are on it: Sidebar.Section, Sidebar.Group, Sidebar.Item and
25
+ * Sidebar.User (also exported as NavSection, NavGroup, NavItem and SidebarUser).
26
26
  */
27
27
  import { type HTMLAttributes, type ReactNode } from 'react';
28
28
  import '../icons.js';
@@ -3,16 +3,16 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
3
3
  * An app sidebar: a logo, the nav, and a footer. In an AppShell it collapses
4
4
  * to an icon rail (its button, or ⌘B) and becomes a drawer on phones.
5
5
  *
6
- * <Sidebar logo="TutorApp" footer={<SidebarUser name="Ada Lovelace" items={account} />}>
7
- * <NavSection heading="Menu">
8
- * <NavItem icon="lucide:house" href="/" active>Dashboard</NavItem>
9
- * <NavGroup label="Settings" icon="lucide:settings">…</NavGroup>
10
- * </NavSection>
6
+ * <Sidebar logo="TutorApp" footer={<Sidebar.User name="Ada Lovelace" items={account} />}>
7
+ * <Sidebar.Section heading="Menu">
8
+ * <Sidebar.Item icon="lucide:house" href="/" active>Dashboard</Sidebar.Item>
9
+ * <Sidebar.Group label="Settings" icon="lucide:settings">…</Sidebar.Group>
10
+ * </Sidebar.Section>
11
11
  * </Sidebar>
12
12
  *
13
13
  * - `logo`; `logoCollapsed`: shown instead in the rail (a mark)
14
- * - `footer`: along the bottom (a SidebarUser)
15
- * - `accordion`: one NavGroup open at a time
14
+ * - `footer`: along the bottom (a Sidebar.User)
15
+ * - `accordion`: one Sidebar.Group open at a time
16
16
  * - `items`: the nav as data, in place of (or before) the content:
17
17
  * `[{ heading, items: [{ label, icon, href, active, badge, items }] }]`
18
18
  * - `edge`: a thin strip along the right edge that collapses or expands it
@@ -22,8 +22,8 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
22
22
  * - restyle its items with the `--ui-sidebar-*` tokens, e.g.
23
23
  * style={{ '--ui-sidebar-active': 'var(--ui-accent-soft)' }}
24
24
  *
25
- * Its parts are on it too: Sidebar.Section, Sidebar.Group, Sidebar.Item and
26
- * Sidebar.User are NavSection, NavGroup, NavItem and SidebarUser.
25
+ * Its parts are on it: Sidebar.Section, Sidebar.Group, Sidebar.Item and
26
+ * Sidebar.User (also exported as NavSection, NavGroup, NavItem and SidebarUser).
27
27
  */
28
28
  import { useMemo, useState } from 'react';
29
29
  import '../icons.js';
@@ -3,7 +3,7 @@
3
3
  * it, opening a menu of account actions upwards (to the right in the rail,
4
4
  * where only the avatar shows).
5
5
  *
6
- * <SidebarUser name="Ada Lovelace" description="ada@example.com" items={account} onSelect={run} />
6
+ * <Sidebar.User name="Ada Lovelace" description="ada@example.com" items={account} onSelect={run} />
7
7
  *
8
8
  * It's a Menu with the user as its trigger: give it `items`, Menu's child
9
9
  * components, or both, and `onSelect` (the rest of Menu's props apply too).
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Tabs: the Tab children are the panels, and the tab list is built from
3
- * their labels.
2
+ * Tabs: the Tabs.Tab children (also Tab) are the panels, and the tab list
3
+ * is built from their labels.
4
4
  *
5
5
  * <Tabs value={tab} onValueChange={setTab}>
6
- * <Tab value="lessons" label="Lessons" icon="lucide:calendar">…</Tab>
7
- * <Tab value="invoices" label="Invoices" count={2} countColor="danger">…</Tab>
6
+ * <Tabs.Tab value="lessons" label="Lessons" icon="lucide:calendar">…</Tabs.Tab>
7
+ * <Tabs.Tab value="invoices" label="Invoices" count={2} countColor="danger">…</Tabs.Tab>
8
8
  * </Tabs>
9
9
  *
10
10
  * Arrow keys move between tabs (selecting as they go), Home/End jump to the
@@ -73,6 +73,7 @@ export type TabsProps = Omit<Ark.RootProps, 'value' | 'defaultValue' | 'onValueC
73
73
  };
74
74
  declare function TabsShortcut({ value, defaultValue, onValueChange, variant, counts, renderTrigger, className, classes, children, ...rest }: TabsProps): import("react").JSX.Element;
75
75
  export declare const Tabs: typeof TabsShortcut & {
76
+ Tab: typeof Tab;
76
77
  Root: import("react").ForwardRefExoticComponent<Ark.RootProps & import("react").RefAttributes<HTMLDivElement>>;
77
78
  List: import("react").ForwardRefExoticComponent<Ark.ListProps & import("react").RefAttributes<HTMLDivElement>>;
78
79
  Trigger: import("react").ForwardRefExoticComponent<Ark.TriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
package/dist/tabs/Tabs.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
- * Tabs: the Tab children are the panels, and the tab list is built from
4
- * their labels.
3
+ * Tabs: the Tabs.Tab children (also Tab) are the panels, and the tab list
4
+ * is built from their labels.
5
5
  *
6
6
  * <Tabs value={tab} onValueChange={setTab}>
7
- * <Tab value="lessons" label="Lessons" icon="lucide:calendar">…</Tab>
8
- * <Tab value="invoices" label="Invoices" count={2} countColor="danger">…</Tab>
7
+ * <Tabs.Tab value="lessons" label="Lessons" icon="lucide:calendar">…</Tabs.Tab>
8
+ * <Tabs.Tab value="invoices" label="Invoices" count={2} countColor="danger">…</Tabs.Tab>
9
9
  * </Tabs>
10
10
  *
11
11
  * Arrow keys move between tabs (selecting as they go), Home/End jump to the
@@ -80,6 +80,7 @@ function TabsShortcut({ value, defaultValue, onValueChange, variant = 'line', co
80
80
  }), _jsx(Ark.Indicator, { ...part('indicator') })] }), _jsx("div", { ...part('panels'), children: children })] }));
81
81
  }
82
82
  export const Tabs = withParts(TabsShortcut, {
83
+ Tab,
83
84
  Root: Ark.Root,
84
85
  List: Ark.List,
85
86
  Trigger: Ark.Trigger,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milktop/react-ui",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "React components on Ark UI (Zag state machines), with imba-ui's API and design",
5
5
  "license": "MIT",
6
6
  "author": "André Goldstein",