@iris-ui-kit/react 0.1.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/LICENSE +21 -0
- package/dist/SidebarLayout-fWQRvK0a.d.cts +26 -0
- package/dist/SidebarLayout-fWQRvK0a.d.ts +26 -0
- package/dist/admin.cjs +1583 -0
- package/dist/admin.cjs.map +1 -0
- package/dist/admin.d.cts +183 -0
- package/dist/admin.d.ts +183 -0
- package/dist/admin.js +9 -0
- package/dist/admin.js.map +1 -0
- package/dist/async.cjs +97 -0
- package/dist/async.cjs.map +1 -0
- package/dist/async.d.cts +57 -0
- package/dist/async.d.ts +57 -0
- package/dist/async.js +5 -0
- package/dist/async.js.map +1 -0
- package/dist/behaviors.cjs +569 -0
- package/dist/behaviors.cjs.map +1 -0
- package/dist/behaviors.d.cts +188 -0
- package/dist/behaviors.d.ts +188 -0
- package/dist/behaviors.js +4 -0
- package/dist/behaviors.js.map +1 -0
- package/dist/chunk-2TQSTINK.js +541 -0
- package/dist/chunk-2TQSTINK.js.map +1 -0
- package/dist/chunk-53KFLVIZ.js +68 -0
- package/dist/chunk-53KFLVIZ.js.map +1 -0
- package/dist/chunk-6BMFMCMQ.js +140 -0
- package/dist/chunk-6BMFMCMQ.js.map +1 -0
- package/dist/chunk-AUA5GBT6.js +143 -0
- package/dist/chunk-AUA5GBT6.js.map +1 -0
- package/dist/chunk-DE6V6NOI.js +122 -0
- package/dist/chunk-DE6V6NOI.js.map +1 -0
- package/dist/chunk-HEJGN6UI.js +78 -0
- package/dist/chunk-HEJGN6UI.js.map +1 -0
- package/dist/chunk-IDD35UJ6.js +83 -0
- package/dist/chunk-IDD35UJ6.js.map +1 -0
- package/dist/chunk-J5FB2UB2.js +166 -0
- package/dist/chunk-J5FB2UB2.js.map +1 -0
- package/dist/chunk-OMOTUHYI.js +66 -0
- package/dist/chunk-OMOTUHYI.js.map +1 -0
- package/dist/chunk-RZNYIFV2.js +34 -0
- package/dist/chunk-RZNYIFV2.js.map +1 -0
- package/dist/chunk-SJO74IB3.js +54 -0
- package/dist/chunk-SJO74IB3.js.map +1 -0
- package/dist/chunk-SORLRLS4.js +53 -0
- package/dist/chunk-SORLRLS4.js.map +1 -0
- package/dist/chunk-TO7YVSAI.js +64 -0
- package/dist/chunk-TO7YVSAI.js.map +1 -0
- package/dist/chunk-TQ33XJIK.js +22 -0
- package/dist/chunk-TQ33XJIK.js.map +1 -0
- package/dist/chunk-UWUUEZCD.js +950 -0
- package/dist/chunk-UWUUEZCD.js.map +1 -0
- package/dist/chunk-VWCJJRSC.js +1200 -0
- package/dist/chunk-VWCJJRSC.js.map +1 -0
- package/dist/chunk-WIHBIDVJ.js +54 -0
- package/dist/chunk-WIHBIDVJ.js.map +1 -0
- package/dist/chunk-XCK4RIXB.js +66 -0
- package/dist/chunk-XCK4RIXB.js.map +1 -0
- package/dist/chunk-YAHXR2CJ.js +48 -0
- package/dist/chunk-YAHXR2CJ.js.map +1 -0
- package/dist/chunk-YXIO5THG.js +4 -0
- package/dist/chunk-YXIO5THG.js.map +1 -0
- package/dist/chunk-ZKBDUWUD.js +156 -0
- package/dist/chunk-ZKBDUWUD.js.map +1 -0
- package/dist/data.cjs +112 -0
- package/dist/data.cjs.map +1 -0
- package/dist/data.d.cts +53 -0
- package/dist/data.d.ts +53 -0
- package/dist/data.js +5 -0
- package/dist/data.js.map +1 -0
- package/dist/error-boundary.cjs +106 -0
- package/dist/error-boundary.cjs.map +1 -0
- package/dist/error-boundary.d.cts +41 -0
- package/dist/error-boundary.d.ts +41 -0
- package/dist/error-boundary.js +6 -0
- package/dist/error-boundary.js.map +1 -0
- package/dist/floating.cjs +163 -0
- package/dist/floating.cjs.map +1 -0
- package/dist/floating.d.cts +92 -0
- package/dist/floating.d.ts +92 -0
- package/dist/floating.js +5 -0
- package/dist/floating.js.map +1 -0
- package/dist/form.cjs +159 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.d.cts +120 -0
- package/dist/form.d.ts +120 -0
- package/dist/form.js +5 -0
- package/dist/form.js.map +1 -0
- package/dist/i18n.cjs +100 -0
- package/dist/i18n.cjs.map +1 -0
- package/dist/i18n.d.cts +50 -0
- package/dist/i18n.d.ts +50 -0
- package/dist/i18n.js +5 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.cjs +16635 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2964 -0
- package/dist/index.d.ts +2964 -0
- package/dist/index.js +12217 -0
- package/dist/index.js.map +1 -0
- package/dist/layouts.cjs +386 -0
- package/dist/layouts.cjs.map +1 -0
- package/dist/layouts.d.cts +92 -0
- package/dist/layouts.d.ts +92 -0
- package/dist/layouts.js +6 -0
- package/dist/layouts.js.map +1 -0
- package/dist/modal-utils.cjs +168 -0
- package/dist/modal-utils.cjs.map +1 -0
- package/dist/modal-utils.d.cts +43 -0
- package/dist/modal-utils.d.ts +43 -0
- package/dist/modal-utils.js +4 -0
- package/dist/modal-utils.js.map +1 -0
- package/dist/motion.cjs +104 -0
- package/dist/motion.cjs.map +1 -0
- package/dist/motion.d.cts +41 -0
- package/dist/motion.d.ts +41 -0
- package/dist/motion.js +4 -0
- package/dist/motion.js.map +1 -0
- package/dist/provider.cjs +191 -0
- package/dist/provider.cjs.map +1 -0
- package/dist/provider.d.cts +62 -0
- package/dist/provider.d.ts +62 -0
- package/dist/provider.js +8 -0
- package/dist/provider.js.map +1 -0
- package/dist/resource.cjs +61 -0
- package/dist/resource.cjs.map +1 -0
- package/dist/resource.d.cts +21 -0
- package/dist/resource.d.ts +21 -0
- package/dist/resource.js +5 -0
- package/dist/resource.js.map +1 -0
- package/dist/skeletons.cjs +1304 -0
- package/dist/skeletons.cjs.map +1 -0
- package/dist/skeletons.d.cts +70 -0
- package/dist/skeletons.d.ts +70 -0
- package/dist/skeletons.js +8 -0
- package/dist/skeletons.js.map +1 -0
- package/dist/skins.cjs +134 -0
- package/dist/skins.cjs.map +1 -0
- package/dist/skins.d.cts +45 -0
- package/dist/skins.d.ts +45 -0
- package/dist/skins.js +5 -0
- package/dist/skins.js.map +1 -0
- package/dist/theme.cjs +100 -0
- package/dist/theme.cjs.map +1 -0
- package/dist/theme.d.cts +71 -0
- package/dist/theme.d.ts +71 -0
- package/dist/theme.js +5 -0
- package/dist/theme.js.map +1 -0
- package/dist/undo.cjs +73 -0
- package/dist/undo.cjs.map +1 -0
- package/dist/undo.d.cts +47 -0
- package/dist/undo.d.ts +47 -0
- package/dist/undo.js +4 -0
- package/dist/undo.js.map +1 -0
- package/package.json +61 -0
package/dist/admin.d.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { NavNode, TabsNav, TabItem } from '@iris-ui-kit/core';
|
|
3
|
+
export { NavNode, TabItem, TabsNav, TabsNavConfig, TabsNavState, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, nodeAllowsRoles, visibleNav } from '@iris-ui-kit/core';
|
|
4
|
+
|
|
5
|
+
type IrisAdminLayoutMode = 'sidebar' | 'full-content';
|
|
6
|
+
type LogoRenderer = React.ReactNode | ((state: {
|
|
7
|
+
collapsed: boolean;
|
|
8
|
+
}) => React.ReactNode);
|
|
9
|
+
type ContentRenderer = React.ReactNode | ((state: {
|
|
10
|
+
activeKey: string;
|
|
11
|
+
}) => React.ReactNode);
|
|
12
|
+
interface IrisAdminLayoutProps {
|
|
13
|
+
/** Normalized nav tree driving menu + breadcrumb. */
|
|
14
|
+
menus: NavNode[];
|
|
15
|
+
/** Active page key (controlled). */
|
|
16
|
+
activeKey?: string;
|
|
17
|
+
defaultActiveKey?: string;
|
|
18
|
+
onActiveKeyChange?: (key: string) => void;
|
|
19
|
+
/** Sidebar collapsed (controlled); undefined = uncontrolled. */
|
|
20
|
+
collapsed?: boolean;
|
|
21
|
+
defaultCollapsed?: boolean;
|
|
22
|
+
onCollapsedChange?: (next: boolean) => void;
|
|
23
|
+
mode?: IrisAdminLayoutMode;
|
|
24
|
+
appTitle?: string;
|
|
25
|
+
/** Optional shared tabs store; when present the tab bar is rendered. */
|
|
26
|
+
tabs?: TabsNav;
|
|
27
|
+
showBreadcrumb?: boolean;
|
|
28
|
+
sidebarWidth?: number | string;
|
|
29
|
+
collapsedWidth?: number | string;
|
|
30
|
+
/** Brand region; render-prop receives `{ collapsed }`. */
|
|
31
|
+
logo?: LogoRenderer;
|
|
32
|
+
/** Header end region (theme switch, user menu, …). */
|
|
33
|
+
toolbar?: React.ReactNode;
|
|
34
|
+
footer?: React.ReactNode;
|
|
35
|
+
onSelect?: (key: string, node: NavNode) => void;
|
|
36
|
+
/** Page content; render-prop receives `{ activeKey }`. */
|
|
37
|
+
children?: ContentRenderer;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The CMS / admin shell — a Vben-style, router-agnostic, data-driven layout that
|
|
41
|
+
* composes IrisSidebarLayout + IrisHeaderLayout with the admin nav pieces. One
|
|
42
|
+
* `menus` tree drives the collapsible sidebar nav, the header breadcrumb, and
|
|
43
|
+
* (when a `tabs` store is passed) the multi-tab bar; `activeKey` is the current
|
|
44
|
+
* page. The host owns routing: react to `onSelect` / `onActiveKeyChange` and
|
|
45
|
+
* render the page via the children render-prop (wrap it in keep-alive keyed by
|
|
46
|
+
* `tabs.cacheKey` for Vben-style tab caching).
|
|
47
|
+
*
|
|
48
|
+
* Modes: `sidebar` (default) and `full-content` (chrome hidden). React port of
|
|
49
|
+
* the Vue `IrisAdminLayout`.
|
|
50
|
+
*/
|
|
51
|
+
declare function IrisAdminLayout({ menus, activeKey, defaultActiveKey, onActiveKeyChange, collapsed, defaultCollapsed, onCollapsedChange, mode, appTitle, tabs, showBreadcrumb, sidebarWidth, collapsedWidth, logo, toolbar, footer, onSelect, children, }: IrisAdminLayoutProps): React.ReactElement;
|
|
52
|
+
|
|
53
|
+
interface IrisNavMenuProps {
|
|
54
|
+
/** Normalized nav tree. */
|
|
55
|
+
items: NavNode[];
|
|
56
|
+
/** Key of the active leaf. */
|
|
57
|
+
activeKey?: string;
|
|
58
|
+
/** Controlled expanded branch keys. */
|
|
59
|
+
expandedKeys?: string[];
|
|
60
|
+
/** Initial expanded keys when uncontrolled (else the active trail expands). */
|
|
61
|
+
defaultExpandedKeys?: string[];
|
|
62
|
+
/** Icon-only rail (top-level items only). */
|
|
63
|
+
collapsed?: boolean;
|
|
64
|
+
ariaLabel?: string;
|
|
65
|
+
onSelect?: (key: string, node: NavNode) => void;
|
|
66
|
+
onExpandedKeysChange?: (keys: string[]) => void;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Data-driven nested navigation menu — the sidebar nav of the admin shell.
|
|
70
|
+
* Renders a normalized `NavNode[]` tree as an accordion of expandable branches
|
|
71
|
+
* and selectable leaves; the active leaf and its ancestor branches are
|
|
72
|
+
* highlighted, and the active branch trail auto-expands. Router-agnostic: takes
|
|
73
|
+
* `activeKey`, calls `onSelect(key, node)`; the host owns navigation.
|
|
74
|
+
*
|
|
75
|
+
* `collapsed` switches to an icon-only rail (top-level only); a branch click
|
|
76
|
+
* then jumps to its first leaf. React port of the Vue `IrisNavMenu`.
|
|
77
|
+
*/
|
|
78
|
+
declare function IrisNavMenu({ items, activeKey, expandedKeys, defaultExpandedKeys, collapsed, ariaLabel, onSelect, onExpandedKeysChange, }: IrisNavMenuProps): React.ReactElement;
|
|
79
|
+
|
|
80
|
+
interface IrisAdminBreadcrumbProps {
|
|
81
|
+
/** Root→current ancestor chain (typically `findNavPath(menus, activeKey)`). */
|
|
82
|
+
trail: NavNode[];
|
|
83
|
+
/** Show each node's icon before its title. */
|
|
84
|
+
showIcon?: boolean;
|
|
85
|
+
/** Hide the breadcrumb when the trail has a single crumb. */
|
|
86
|
+
hideSingle?: boolean;
|
|
87
|
+
separator?: string;
|
|
88
|
+
onSelect?: (key: string, node: NavNode) => void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Breadcrumb trail for the admin header, driven by a `NavNode[]` ancestor chain.
|
|
92
|
+
* The last crumb is the current page (auto-marked `aria-current` by
|
|
93
|
+
* IrisBreadcrumb); earlier crumbs are clickable and call `onSelect(key, node)`.
|
|
94
|
+
* React port of the Vue `IrisAdminBreadcrumb`.
|
|
95
|
+
*/
|
|
96
|
+
declare function IrisAdminBreadcrumb({ trail, showIcon, hideSingle, separator, onSelect, }: IrisAdminBreadcrumbProps): React.ReactElement | null;
|
|
97
|
+
|
|
98
|
+
interface IrisAdminTabsProps {
|
|
99
|
+
/** Shared tabs store (from `createTabsNav`). */
|
|
100
|
+
nav: TabsNav;
|
|
101
|
+
onChange?: (key: string) => void;
|
|
102
|
+
onClose?: (key: string) => void;
|
|
103
|
+
onRefresh?: (key: string) => void;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Vben-style multi-tab bar over a shared {@link TabsNav} store. Each open page is
|
|
107
|
+
* a closable chip; clicking activates (`onChange`), the × closes, and a trailing
|
|
108
|
+
* actions dropdown runs Refresh / Close / Close-others / Close-all on the active
|
|
109
|
+
* tab. The store owns the tab list + active key; the host watches it to sync
|
|
110
|
+
* routing + keep-alive.
|
|
111
|
+
*
|
|
112
|
+
* Keyboard (WAI-ARIA tablist, automatic activation): ←/→ move + activate, Home/
|
|
113
|
+
* End jump, Delete/Backspace closes the focused tab. Roving tabindex keeps a
|
|
114
|
+
* single tab stop. React port of the Vue `IrisAdminTabs`.
|
|
115
|
+
*/
|
|
116
|
+
declare function IrisAdminTabs({ nav, onChange, onClose, onRefresh, }: IrisAdminTabsProps): React.ReactElement;
|
|
117
|
+
|
|
118
|
+
interface UseTabsNavReturn {
|
|
119
|
+
tabs: TabItem[];
|
|
120
|
+
activeKey: string | undefined;
|
|
121
|
+
/** Keep-alive include-set (composite cache keys of all open tabs). */
|
|
122
|
+
cacheKeys: string[];
|
|
123
|
+
open: TabsNav['open'];
|
|
124
|
+
activate: TabsNav['activate'];
|
|
125
|
+
close: TabsNav['close'];
|
|
126
|
+
closeOthers: TabsNav['closeOthers'];
|
|
127
|
+
closeAll: TabsNav['closeAll'];
|
|
128
|
+
closeLeft: TabsNav['closeLeft'];
|
|
129
|
+
closeRight: TabsNav['closeRight'];
|
|
130
|
+
refresh: TabsNav['refresh'];
|
|
131
|
+
setPinned: TabsNav['setPinned'];
|
|
132
|
+
cacheKey: TabsNav['cacheKey'];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* React binding for a framework-agnostic {@link TabsNav} store. Pass a shared
|
|
136
|
+
* `createTabsNav()` instance (the host owns it so the tab bar and the keep-alive
|
|
137
|
+
* content read the same state) and get the reactive `tabs` / `activeKey` /
|
|
138
|
+
* `cacheKeys` plus the store's mutators — bridged via `useStore`
|
|
139
|
+
* (`useSyncExternalStore`), mirroring the Vue adapter.
|
|
140
|
+
*
|
|
141
|
+
* ```tsx
|
|
142
|
+
* const nav = createTabsNav({ tabs: [{ key: 'home', title: 'Home', pinned: true }] })
|
|
143
|
+
* const { tabs, activeKey, open, close } = useTabsNav(nav)
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
declare function useTabsNav(nav: TabsNav): UseTabsNavReturn;
|
|
147
|
+
|
|
148
|
+
interface UseAdminShellConfig {
|
|
149
|
+
/** Normalized nav tree driving menu + breadcrumb. */
|
|
150
|
+
menus: NavNode[];
|
|
151
|
+
/** Active page key (controlled); undefined = uncontrolled. */
|
|
152
|
+
activeKey?: string;
|
|
153
|
+
defaultActiveKey?: string;
|
|
154
|
+
onActiveKeyChange?: (key: string) => void;
|
|
155
|
+
/** Fired whenever a nav node / tab becomes active, with the resolved leaf node. */
|
|
156
|
+
onSelect?: (key: string, node: NavNode) => void;
|
|
157
|
+
/** Optional shared tabs store; the shell opens/reconciles tabs through it. */
|
|
158
|
+
tabs?: TabsNav;
|
|
159
|
+
}
|
|
160
|
+
interface UseAdminShellReturn {
|
|
161
|
+
/** Current active leaf key ('' when none). */
|
|
162
|
+
activeKey: string;
|
|
163
|
+
/** Select a nav node: a branch redirects to its first leaf, opens its tab. */
|
|
164
|
+
navigate: (node: NavNode) => void;
|
|
165
|
+
/** Reconcile when the active TAB changed (e.g. a close picked a neighbor). */
|
|
166
|
+
syncFromTab: (key: string) => void;
|
|
167
|
+
/** Ancestor→node breadcrumb trail of the active key. */
|
|
168
|
+
breadcrumb: NavNode[];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* React binding for the framework-agnostic {@link createAdminShell} controller —
|
|
172
|
+
* the menu→tab→active-key wiring that every AdminLayout used to re-implement
|
|
173
|
+
* byte-for-byte. The hook owns the controlled/uncontrolled `activeKey`
|
|
174
|
+
* reconciliation (idiomatic to React) and delegates navigate / syncFromTab /
|
|
175
|
+
* branch-resolution / breadcrumb to the shared core shell.
|
|
176
|
+
*
|
|
177
|
+
* Controlled mode renders from the prop directly (no flicker); an effect keeps
|
|
178
|
+
* the shell's guard-base synced to the prop so the active-change callback fires
|
|
179
|
+
* with the same semantics as before.
|
|
180
|
+
*/
|
|
181
|
+
declare function useAdminShell(config: UseAdminShellConfig): UseAdminShellReturn;
|
|
182
|
+
|
|
183
|
+
export { IrisAdminBreadcrumb, type IrisAdminBreadcrumbProps, IrisAdminLayout, type IrisAdminLayoutMode, type IrisAdminLayoutProps, IrisAdminTabs, type IrisAdminTabsProps, IrisNavMenu, type IrisNavMenuProps, type UseAdminShellConfig, type UseAdminShellReturn, type UseTabsNavReturn, useAdminShell, useTabsNav };
|
package/dist/admin.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
export { IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisNavMenu, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, nodeAllowsRoles, useAdminShell, useTabsNav, visibleNav } from './chunk-VWCJJRSC.js';
|
|
3
|
+
import './chunk-XCK4RIXB.js';
|
|
4
|
+
import './chunk-J5FB2UB2.js';
|
|
5
|
+
import './chunk-OMOTUHYI.js';
|
|
6
|
+
import './chunk-6BMFMCMQ.js';
|
|
7
|
+
import './chunk-RZNYIFV2.js';
|
|
8
|
+
//# sourceMappingURL=admin.js.map
|
|
9
|
+
//# sourceMappingURL=admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":";","file":"admin.js"}
|
package/dist/async.cjs
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var React2 = require('react');
|
|
5
|
+
var core = require('@iris-ui-kit/core');
|
|
6
|
+
|
|
7
|
+
function _interopNamespace(e) {
|
|
8
|
+
if (e && e.__esModule) return e;
|
|
9
|
+
var n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
Object.keys(e).forEach(function (k) {
|
|
12
|
+
if (k !== 'default') {
|
|
13
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return e[k]; }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
|
|
26
|
+
|
|
27
|
+
// src/async/useAsyncResource.ts
|
|
28
|
+
function useStore(store) {
|
|
29
|
+
return React2.useSyncExternalStore(
|
|
30
|
+
store.subscribe,
|
|
31
|
+
store.getState,
|
|
32
|
+
store.getState
|
|
33
|
+
// SSR fallback — same as client snapshot in our case.
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/async/useAsyncResource.ts
|
|
38
|
+
function useAsyncResource(fetcher, options = {}) {
|
|
39
|
+
const latest = React2__namespace.useRef(fetcher);
|
|
40
|
+
latest.current = fetcher;
|
|
41
|
+
const ref = React2__namespace.useRef(null);
|
|
42
|
+
if (ref.current === null) {
|
|
43
|
+
const config = "initialData" in options ? { initialData: options.initialData } : {};
|
|
44
|
+
ref.current = core.createAsyncResource((...params) => latest.current(...params), config);
|
|
45
|
+
}
|
|
46
|
+
const resource = ref.current;
|
|
47
|
+
const state = useStore(resource.store);
|
|
48
|
+
const immediate = React2__namespace.useRef(options.immediate ?? false);
|
|
49
|
+
React2__namespace.useEffect(() => {
|
|
50
|
+
if (immediate.current) void resource.load(...[]);
|
|
51
|
+
return () => resource.cancel();
|
|
52
|
+
}, [resource]);
|
|
53
|
+
return {
|
|
54
|
+
...state,
|
|
55
|
+
isLoading: state.status === "loading",
|
|
56
|
+
isError: state.status === "error",
|
|
57
|
+
load: resource.load,
|
|
58
|
+
reload: resource.reload,
|
|
59
|
+
mutate: resource.mutate,
|
|
60
|
+
cancel: resource.cancel,
|
|
61
|
+
reset: resource.reset
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function usePaginatedResource(fetcher, options = {}) {
|
|
65
|
+
const latest = React2__namespace.useRef(fetcher);
|
|
66
|
+
latest.current = fetcher;
|
|
67
|
+
const ref = React2__namespace.useRef(null);
|
|
68
|
+
if (ref.current === null) {
|
|
69
|
+
ref.current = core.createPaginatedResource((query) => latest.current(query), {
|
|
70
|
+
pageSize: options.pageSize,
|
|
71
|
+
mode: options.mode
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const resource = ref.current;
|
|
75
|
+
const state = useStore(resource.store);
|
|
76
|
+
const initial = React2__namespace.useRef({ immediate: options.immediate ?? false, mode: options.mode });
|
|
77
|
+
React2__namespace.useEffect(() => {
|
|
78
|
+
if (!initial.current.immediate) return;
|
|
79
|
+
void (initial.current.mode === "infinite" ? resource.loadMore() : resource.goToPage(1));
|
|
80
|
+
}, [resource]);
|
|
81
|
+
return {
|
|
82
|
+
...state,
|
|
83
|
+
isLoading: state.status === "loading",
|
|
84
|
+
isError: state.status === "error",
|
|
85
|
+
// `state` drives the re-render; hasMore() then reads the fresh batch state.
|
|
86
|
+
hasMore: resource.hasMore(),
|
|
87
|
+
goToPage: resource.goToPage,
|
|
88
|
+
loadMore: resource.loadMore,
|
|
89
|
+
refresh: resource.refresh,
|
|
90
|
+
setPageSize: resource.setPageSize
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
exports.useAsyncResource = useAsyncResource;
|
|
95
|
+
exports.usePaginatedResource = usePaginatedResource;
|
|
96
|
+
//# sourceMappingURL=async.cjs.map
|
|
97
|
+
//# sourceMappingURL=async.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/useStore.ts","../src/async/useAsyncResource.ts","../src/async/usePaginatedResource.ts"],"names":["useSyncExternalStore","React2","createAsyncResource","React3","createPaginatedResource"],"mappings":"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","file":"async.cjs","sourcesContent":["import * as React from 'react'\nimport { useSyncExternalStore } from 'react'\nimport type { ReadonlyStore, Store } from '@iris-ui-kit/core'\n\n/**\n * Bridge a framework-agnostic `@iris-ui-kit/core` store into React reactivity.\n * Uses React 18's `useSyncExternalStore` — the contract is identical to the\n * one Vue uses via `ref + onMounted` and Solid uses via `createSignal`. The\n * **same** store can power Button, Popover, Dialog, etc. on any framework.\n */\nexport function useStore<T>(store: ReadonlyStore<T>): T {\n return useSyncExternalStore(\n store.subscribe,\n store.getState,\n store.getState, // SSR fallback — same as client snapshot in our case.\n )\n}\n\n/**\n * Subscribe to a DERIVED slice of a core store — the component re-renders only\n * when `selector(state)` changes per `isEqual` (default `Object.is`), not on\n * every store emission. Built on {@link Store.subscribeWith}. Inline closures\n * for `selector`/`isEqual` are fine (captured via refs; the subscription is not\n * re-created between renders).\n */\nexport function useStoreSelector<T, U>(\n store: Store<T>,\n selector: (state: T) => U,\n isEqual?: (a: U, b: U) => boolean,\n): U {\n const [slice, setSlice] = React.useState(() => selector(store.getState()))\n const selRef = React.useRef(selector)\n selRef.current = selector\n const eqRef = React.useRef(isEqual)\n eqRef.current = isEqual\n React.useEffect(() => {\n const read = (s: T): U => selRef.current(s)\n const eq = (a: U, b: U): boolean => (eqRef.current ?? Object.is)(a, b)\n // Re-sync in case the store moved between render and this effect.\n setSlice((prev) => {\n const next = read(store.getState())\n return eq(prev, next) ? prev : next\n })\n return store.subscribeWith(read, setSlice, eq)\n }, [store])\n return slice\n}\n","import * as React from 'react'\nimport {\n createAsyncResource,\n type AsyncResource,\n type AsyncResourceConfig,\n type AsyncState,\n} from '@iris-ui-kit/core'\nimport { useStore } from '../useStore'\n\nexport interface UseAsyncResourceOptions<T> extends AsyncResourceConfig<T> {\n /** Run `load()` (no params) once on mount. Default `false`. */\n immediate?: boolean\n}\n\nexport interface UseAsyncResourceReturn<T, P extends unknown[]> extends AsyncState<T> {\n isLoading: boolean\n isError: boolean\n load: AsyncResource<T, P>['load']\n reload: AsyncResource<T, P>['reload']\n mutate: AsyncResource<T, P>['mutate']\n cancel: AsyncResource<T, P>['cancel']\n reset: AsyncResource<T, P>['reset']\n}\n\n/**\n * React binding for the framework-agnostic async resource. Instantiates the\n * resource once and bridges it via `useSyncExternalStore`. The fetcher is read\n * through a ref each render so it never goes stale, while seeding/immediacy are\n * captured on first render.\n *\n * ```tsx\n * const users = useAsyncResource(() => api.listUsers(), { immediate: true })\n * <IrisTable data={users.data ?? []} loading={users.isLoading} error={users.isError} />\n * ```\n */\nexport function useAsyncResource<T, P extends unknown[] = []>(\n fetcher: (...params: P) => Promise<T>,\n options: UseAsyncResourceOptions<T> = {},\n): UseAsyncResourceReturn<T, P> {\n const latest = React.useRef(fetcher)\n latest.current = fetcher\n\n const ref = React.useRef<AsyncResource<T, P> | null>(null)\n if (ref.current === null) {\n const config: AsyncResourceConfig<T> =\n 'initialData' in options ? { initialData: options.initialData } : {}\n ref.current = createAsyncResource<T, P>((...params) => latest.current(...params), config)\n }\n const resource = ref.current\n const state = useStore(resource.store)\n\n const immediate = React.useRef(options.immediate ?? false)\n React.useEffect(() => {\n if (immediate.current) void resource.load(...([] as unknown as P))\n // Abort any in-flight request on unmount so it can't write back into an\n // unmounted component (the token guard already prevents the state write;\n // this also cancels the underlying request when the fetcher honors signal).\n return () => resource.cancel()\n }, [resource])\n\n return {\n ...state,\n isLoading: state.status === 'loading',\n isError: state.status === 'error',\n load: resource.load,\n reload: resource.reload,\n mutate: resource.mutate,\n cancel: resource.cancel,\n reset: resource.reset,\n }\n}\n","import * as React from 'react'\nimport {\n createPaginatedResource,\n type PageQuery,\n type PageResult,\n type PaginatedResource,\n type PaginatedState,\n type PaginationMode,\n} from '@iris-ui-kit/core'\nimport { useStore } from '../useStore'\n\nexport interface UsePaginatedResourceOptions {\n pageSize?: number\n mode?: PaginationMode\n /** Load the first page on mount (page 1, or `loadMore` in infinite mode). */\n immediate?: boolean\n}\n\nexport interface UsePaginatedResourceReturn<T> extends PaginatedState<T> {\n isLoading: boolean\n isError: boolean\n hasMore: boolean\n goToPage: PaginatedResource<T>['goToPage']\n loadMore: PaginatedResource<T>['loadMore']\n refresh: PaginatedResource<T>['refresh']\n setPageSize: PaginatedResource<T>['setPageSize']\n}\n\n/**\n * React binding for the server-side pagination resource. Instantiates once and\n * bridges via `useSyncExternalStore`; the fetcher is read through a ref so it\n * never goes stale.\n *\n * ```tsx\n * const p = usePaginatedResource((q) => api.rows(q), { pageSize: 25, immediate: true })\n * <IrisTable data={p.items} loading={p.isLoading} error={p.isError} />\n * <IrisPagination total={p.total ?? 0} value={p.page} onValueChange={p.goToPage} />\n * ```\n */\nexport function usePaginatedResource<T>(\n fetcher: (query: PageQuery) => Promise<PageResult<T>>,\n options: UsePaginatedResourceOptions = {},\n): UsePaginatedResourceReturn<T> {\n const latest = React.useRef(fetcher)\n latest.current = fetcher\n\n const ref = React.useRef<PaginatedResource<T> | null>(null)\n if (ref.current === null) {\n ref.current = createPaginatedResource<T>((query) => latest.current(query), {\n pageSize: options.pageSize,\n mode: options.mode,\n })\n }\n const resource = ref.current\n const state = useStore(resource.store)\n\n const initial = React.useRef({ immediate: options.immediate ?? false, mode: options.mode })\n React.useEffect(() => {\n if (!initial.current.immediate) return\n void (initial.current.mode === 'infinite' ? resource.loadMore() : resource.goToPage(1))\n }, [resource])\n\n return {\n ...state,\n isLoading: state.status === 'loading',\n isError: state.status === 'error',\n // `state` drives the re-render; hasMore() then reads the fresh batch state.\n hasMore: resource.hasMore(),\n goToPage: resource.goToPage,\n loadMore: resource.loadMore,\n refresh: resource.refresh,\n setPageSize: resource.setPageSize,\n }\n}\n"]}
|
package/dist/async.d.cts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { AsyncResourceConfig, AsyncState, AsyncResource, PaginationMode, PaginatedState, PaginatedResource, PageQuery, PageResult } from '@iris-ui-kit/core';
|
|
2
|
+
|
|
3
|
+
interface UseAsyncResourceOptions<T> extends AsyncResourceConfig<T> {
|
|
4
|
+
/** Run `load()` (no params) once on mount. Default `false`. */
|
|
5
|
+
immediate?: boolean;
|
|
6
|
+
}
|
|
7
|
+
interface UseAsyncResourceReturn<T, P extends unknown[]> extends AsyncState<T> {
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
isError: boolean;
|
|
10
|
+
load: AsyncResource<T, P>['load'];
|
|
11
|
+
reload: AsyncResource<T, P>['reload'];
|
|
12
|
+
mutate: AsyncResource<T, P>['mutate'];
|
|
13
|
+
cancel: AsyncResource<T, P>['cancel'];
|
|
14
|
+
reset: AsyncResource<T, P>['reset'];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* React binding for the framework-agnostic async resource. Instantiates the
|
|
18
|
+
* resource once and bridges it via `useSyncExternalStore`. The fetcher is read
|
|
19
|
+
* through a ref each render so it never goes stale, while seeding/immediacy are
|
|
20
|
+
* captured on first render.
|
|
21
|
+
*
|
|
22
|
+
* ```tsx
|
|
23
|
+
* const users = useAsyncResource(() => api.listUsers(), { immediate: true })
|
|
24
|
+
* <IrisTable data={users.data ?? []} loading={users.isLoading} error={users.isError} />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
declare function useAsyncResource<T, P extends unknown[] = []>(fetcher: (...params: P) => Promise<T>, options?: UseAsyncResourceOptions<T>): UseAsyncResourceReturn<T, P>;
|
|
28
|
+
|
|
29
|
+
interface UsePaginatedResourceOptions {
|
|
30
|
+
pageSize?: number;
|
|
31
|
+
mode?: PaginationMode;
|
|
32
|
+
/** Load the first page on mount (page 1, or `loadMore` in infinite mode). */
|
|
33
|
+
immediate?: boolean;
|
|
34
|
+
}
|
|
35
|
+
interface UsePaginatedResourceReturn<T> extends PaginatedState<T> {
|
|
36
|
+
isLoading: boolean;
|
|
37
|
+
isError: boolean;
|
|
38
|
+
hasMore: boolean;
|
|
39
|
+
goToPage: PaginatedResource<T>['goToPage'];
|
|
40
|
+
loadMore: PaginatedResource<T>['loadMore'];
|
|
41
|
+
refresh: PaginatedResource<T>['refresh'];
|
|
42
|
+
setPageSize: PaginatedResource<T>['setPageSize'];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* React binding for the server-side pagination resource. Instantiates once and
|
|
46
|
+
* bridges via `useSyncExternalStore`; the fetcher is read through a ref so it
|
|
47
|
+
* never goes stale.
|
|
48
|
+
*
|
|
49
|
+
* ```tsx
|
|
50
|
+
* const p = usePaginatedResource((q) => api.rows(q), { pageSize: 25, immediate: true })
|
|
51
|
+
* <IrisTable data={p.items} loading={p.isLoading} error={p.isError} />
|
|
52
|
+
* <IrisPagination total={p.total ?? 0} value={p.page} onValueChange={p.goToPage} />
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function usePaginatedResource<T>(fetcher: (query: PageQuery) => Promise<PageResult<T>>, options?: UsePaginatedResourceOptions): UsePaginatedResourceReturn<T>;
|
|
56
|
+
|
|
57
|
+
export { type UseAsyncResourceOptions, type UseAsyncResourceReturn, type UsePaginatedResourceOptions, type UsePaginatedResourceReturn, useAsyncResource, usePaginatedResource };
|
package/dist/async.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { AsyncResourceConfig, AsyncState, AsyncResource, PaginationMode, PaginatedState, PaginatedResource, PageQuery, PageResult } from '@iris-ui-kit/core';
|
|
2
|
+
|
|
3
|
+
interface UseAsyncResourceOptions<T> extends AsyncResourceConfig<T> {
|
|
4
|
+
/** Run `load()` (no params) once on mount. Default `false`. */
|
|
5
|
+
immediate?: boolean;
|
|
6
|
+
}
|
|
7
|
+
interface UseAsyncResourceReturn<T, P extends unknown[]> extends AsyncState<T> {
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
isError: boolean;
|
|
10
|
+
load: AsyncResource<T, P>['load'];
|
|
11
|
+
reload: AsyncResource<T, P>['reload'];
|
|
12
|
+
mutate: AsyncResource<T, P>['mutate'];
|
|
13
|
+
cancel: AsyncResource<T, P>['cancel'];
|
|
14
|
+
reset: AsyncResource<T, P>['reset'];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* React binding for the framework-agnostic async resource. Instantiates the
|
|
18
|
+
* resource once and bridges it via `useSyncExternalStore`. The fetcher is read
|
|
19
|
+
* through a ref each render so it never goes stale, while seeding/immediacy are
|
|
20
|
+
* captured on first render.
|
|
21
|
+
*
|
|
22
|
+
* ```tsx
|
|
23
|
+
* const users = useAsyncResource(() => api.listUsers(), { immediate: true })
|
|
24
|
+
* <IrisTable data={users.data ?? []} loading={users.isLoading} error={users.isError} />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
declare function useAsyncResource<T, P extends unknown[] = []>(fetcher: (...params: P) => Promise<T>, options?: UseAsyncResourceOptions<T>): UseAsyncResourceReturn<T, P>;
|
|
28
|
+
|
|
29
|
+
interface UsePaginatedResourceOptions {
|
|
30
|
+
pageSize?: number;
|
|
31
|
+
mode?: PaginationMode;
|
|
32
|
+
/** Load the first page on mount (page 1, or `loadMore` in infinite mode). */
|
|
33
|
+
immediate?: boolean;
|
|
34
|
+
}
|
|
35
|
+
interface UsePaginatedResourceReturn<T> extends PaginatedState<T> {
|
|
36
|
+
isLoading: boolean;
|
|
37
|
+
isError: boolean;
|
|
38
|
+
hasMore: boolean;
|
|
39
|
+
goToPage: PaginatedResource<T>['goToPage'];
|
|
40
|
+
loadMore: PaginatedResource<T>['loadMore'];
|
|
41
|
+
refresh: PaginatedResource<T>['refresh'];
|
|
42
|
+
setPageSize: PaginatedResource<T>['setPageSize'];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* React binding for the server-side pagination resource. Instantiates once and
|
|
46
|
+
* bridges via `useSyncExternalStore`; the fetcher is read through a ref so it
|
|
47
|
+
* never goes stale.
|
|
48
|
+
*
|
|
49
|
+
* ```tsx
|
|
50
|
+
* const p = usePaginatedResource((q) => api.rows(q), { pageSize: 25, immediate: true })
|
|
51
|
+
* <IrisTable data={p.items} loading={p.isLoading} error={p.isError} />
|
|
52
|
+
* <IrisPagination total={p.total ?? 0} value={p.page} onValueChange={p.goToPage} />
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function usePaginatedResource<T>(fetcher: (query: PageQuery) => Promise<PageResult<T>>, options?: UsePaginatedResourceOptions): UsePaginatedResourceReturn<T>;
|
|
56
|
+
|
|
57
|
+
export { type UseAsyncResourceOptions, type UseAsyncResourceReturn, type UsePaginatedResourceOptions, type UsePaginatedResourceReturn, useAsyncResource, usePaginatedResource };
|
package/dist/async.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":";","file":"async.js"}
|