@deftui/addons 0.2.4 → 0.2.5

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.
@@ -14,6 +14,7 @@ function ModalRenderer({ view, onPresent, onError }) {
14
14
  }, child.id)) : null;
15
15
  const { root: Root, component: Content } = view.definition;
16
16
  return /* @__PURE__ */ jsxs(Root, {
17
+ ...view.rootConfig,
17
18
  ...view.rootProps,
18
19
  children: [/* @__PURE__ */ jsx(Content, {
19
20
  ...view.props,
@@ -41,6 +41,9 @@ function createModalRegistry(options) {
41
41
  function getServerSnapshot() {
42
42
  return EMPTY;
43
43
  }
44
+ function getServerOpenSnapshot() {
45
+ return false;
46
+ }
44
47
  function createRootProps(instance) {
45
48
  return {
46
49
  open: instance.phase !== "closing",
@@ -57,13 +60,14 @@ function createModalRegistry(options) {
57
60
  const children = [...instance.children].map(build);
58
61
  const open = instance.phase !== "closing";
59
62
  const existing = instance.view;
60
- if (existing && existing.definition === instance.definition && existing.props === instance.props && existing.rootProps.open === open && sameViews(existing.children, children)) return existing;
63
+ if (existing && existing.definition === instance.definition && existing.props === instance.props && existing.rootConfig === instance.rootConfig && existing.rootProps.open === open && sameViews(existing.children, children)) return existing;
61
64
  return instance.view = {
62
65
  id: instance.id,
63
66
  definition: instance.definition,
64
67
  props: instance.props,
65
68
  modal: instance.modal,
66
69
  rootProps: createRootProps(instance),
70
+ rootConfig: instance.rootConfig,
67
71
  children
68
72
  };
69
73
  }
@@ -201,6 +205,19 @@ function createModalRegistry(options) {
201
205
  if (!parent || parent.phase !== "active") throw new Error("A child call cannot be opened from a settled parent.");
202
206
  parentId = id;
203
207
  }
208
+ if (options?.id) for (const instance of instances.values()) {
209
+ if (instance.phase !== "active" || instance.definition !== definition || instance.dedupeKey !== options.id) continue;
210
+ instance.props = {
211
+ ...instance.props,
212
+ ...props
213
+ };
214
+ if (options.rootProps) instance.rootConfig = {
215
+ ...instance.rootConfig,
216
+ ...options.rootProps
217
+ };
218
+ publish();
219
+ return instance.call;
220
+ }
204
221
  const id = nextId++;
205
222
  let resolveResult;
206
223
  let rejectResult;
@@ -231,6 +248,8 @@ function createModalRegistry(options) {
231
248
  parentId,
232
249
  children: /* @__PURE__ */ new Set(),
233
250
  props,
251
+ rootConfig: { ...options?.rootProps },
252
+ dedupeKey: options?.id,
234
253
  phase: "active",
235
254
  presented: false,
236
255
  resolveResult,
@@ -252,10 +271,28 @@ function createModalRegistry(options) {
252
271
  function dismissAll() {
253
272
  for (const instance of [...roots].reverse()) finish(instance, { status: "dismissed" });
254
273
  }
274
+ function dismissTop() {
275
+ let top;
276
+ for (const instance of instances.values()) {
277
+ if (instance.phase !== "active") continue;
278
+ if (!top || instance.id > top.id) top = instance;
279
+ }
280
+ if (top) finish(top, { status: "dismissed" });
281
+ }
282
+ function isAnyModalOpen() {
283
+ for (const instance of instances.values()) if (instance.phase !== "closing") return true;
284
+ return false;
285
+ }
286
+ function useAnyModalOpen() {
287
+ return useSyncExternalStore(subscribe, isAnyModalOpen, getServerOpenSnapshot);
288
+ }
255
289
  const registry = {
256
290
  Provider,
257
291
  define,
258
- dismissAll
292
+ dismissAll,
293
+ dismissTop,
294
+ isAnyModalOpen,
295
+ useAnyModalOpen
259
296
  };
260
297
  if (cache && options) cache.set(options.key, registry);
261
298
  return registry;
@@ -6,9 +6,11 @@ export interface ModalRootProps {
6
6
  onOpenChangeComplete: (open: boolean) => void;
7
7
  }
8
8
  /** A Base UI root component controlled by the registry. */
9
- export type ModalRootComponent = ComponentType<ModalRootProps & {
9
+ export type ModalRootComponent<RootProps extends object = {}> = ComponentType<ModalRootProps & RootProps & {
10
10
  children?: ReactNode;
11
11
  }>;
12
+ /** Root props the caller may override for one call. */
13
+ export type ModalRootOverrides<RootProps extends object> = Partial<Omit<RootProps, keyof ModalRootProps | "children">>;
12
14
  /** Per-call controls exposed to the modal component. */
13
15
  export interface ModalController<Props extends object = {}, Output = void> {
14
16
  /** The current call, suitable for `show(..., { parent: modal.call })`. */
@@ -41,18 +43,23 @@ export interface ModalCall<Props, Output> extends Promise<ModalResult<Output>>,
41
43
  dismiss: () => void;
42
44
  }
43
45
  /** A callable definition doubles as a Fast Refresh-compatible component handle. */
44
- export type ModalDefinition<Props extends object = {}, Output = void> = FunctionComponent<{}> & {
46
+ export type ModalDefinition<Props extends object = {}, Output = void, RootProps extends object = {}> = FunctionComponent<{}> & {
45
47
  /** Open this definition through its owning registry. */
46
- readonly show: ModalShow<Props, Output>;
48
+ readonly show: ModalShow<Props, Output, RootProps>;
47
49
  };
48
- export interface ModalShowOptions {
50
+ export interface ModalShowOptions<RootProps extends object = {}> {
51
+ /** Reuse the active call with the same id for this definition. */
52
+ id?: string;
49
53
  /** Render this call inside the parent call's root. */
50
54
  parent?: ModalHandle;
55
+ /** Root props to shallow-merge for this call. */
56
+ rootProps?: ModalRootOverrides<RootProps>;
51
57
  }
52
58
  type ModalArguments<Props> = {} extends Props ? [props?: Props] : [props: Props];
53
- interface ModalShow<Props, Output> {
59
+ type ModalShowOptionsArguments<Props, RootProps extends object> = {} extends Props ? [props: Props | undefined, options: ModalShowOptions<RootProps>] : [props: Props, options: ModalShowOptions<RootProps>];
60
+ interface ModalShow<Props, Output, RootProps extends object = {}> {
54
61
  (...args: ModalArguments<Props>): ModalCall<Props, Output>;
55
- (props: Props, options: ModalShowOptions): ModalCall<Props, Output>;
62
+ (...args: ModalShowOptionsArguments<Props, RootProps>): ModalCall<Props, Output>;
56
63
  }
57
64
  export interface ModalRegistry {
58
65
  /** Mount once around the application. Unmounting dismisses pending calls. */
@@ -60,9 +67,15 @@ export interface ModalRegistry {
60
67
  children?: ReactNode;
61
68
  }>;
62
69
  /** Register a modal component. The registry controls the root and injects child calls through its children. */
63
- define<Props extends object, Output = void>(root: ModalRootComponent, component: ComponentType<Modal<Props, Output>>): ModalDefinition<Props, Output>;
70
+ define<Props extends object, Output = void, RootProps extends object = {}>(root: ModalRootComponent<RootProps>, component: ComponentType<Modal<Props, Output>>): ModalDefinition<Props, Output, RootProps>;
64
71
  /** Dismiss every active call, waiting for children to exit before their parents. */
65
72
  dismissAll: () => void;
73
+ /** Dismiss the most recently opened active call. */
74
+ dismissTop: () => void;
75
+ /** Whether at least one modal root is open. Closing roots no longer count. */
76
+ isAnyModalOpen: () => boolean;
77
+ /** Reactive form of `isAnyModalOpen` for React components. */
78
+ useAnyModalOpen: () => boolean;
66
79
  }
67
80
  export interface ModalRegistryOptions {
68
81
  /** The registration module's `import.meta.hot`. */
package/dist/modal.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- import { Modal, ModalCall, ModalController, ModalDefinition, ModalHandle, ModalRegistry, ModalRegistryOptions, ModalResult, ModalRootComponent, ModalRootProps, ModalShowOptions } from "./modal-registry/types.mjs";
1
+ import { Modal, ModalCall, ModalController, ModalDefinition, ModalHandle, ModalRegistry, ModalRegistryOptions, ModalResult, ModalRootComponent, ModalRootOverrides, ModalRootProps, ModalShowOptions } from "./modal-registry/types.mjs";
2
2
  import { createModalRegistry } from "./modal-registry/registry.mjs";
3
- export { type Modal, type ModalCall, type ModalController, type ModalDefinition, type ModalHandle, type ModalRegistry, type ModalRegistryOptions, type ModalResult, type ModalRootComponent, type ModalRootProps, type ModalShowOptions, createModalRegistry };
3
+ export { type Modal, type ModalCall, type ModalController, type ModalDefinition, type ModalHandle, type ModalRegistry, type ModalRegistryOptions, type ModalResult, type ModalRootComponent, type ModalRootOverrides, type ModalRootProps, type ModalShowOptions, createModalRegistry };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftui/addons",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/KiriRainCat/deftui.git",
@@ -40,7 +40,7 @@
40
40
  "fix": "vp check --fix"
41
41
  },
42
42
  "devDependencies": {
43
- "@deftui/react": "^0.2.4",
43
+ "@deftui/react": "^0.2.5",
44
44
  "@tanstack/react-router": "^1.170.46",
45
45
  "@types/react": "^19.3.0",
46
46
  "@types/react-dom": "^19.3.0",
@@ -49,7 +49,7 @@
49
49
  "vite-plus": "catalog:"
50
50
  },
51
51
  "peerDependencies": {
52
- "@deftui/react": "^0.2.4",
52
+ "@deftui/react": "^0.2.5",
53
53
  "@tanstack/react-router": "^1.170.0",
54
54
  "react": "^19.0.0",
55
55
  "react-dom": "^19.0.0",