@jfdevelops/react-layout 1.2.0 → 1.3.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.
Files changed (57) hide show
  1. package/dist/create-config/create-defined-resource-layout.cjs +40 -4
  2. package/dist/create-config/create-defined-resource-layout.cjs.map +1 -1
  3. package/dist/create-config/create-defined-resource-layout.d.cts +33 -13
  4. package/dist/create-config/create-defined-resource-layout.d.cts.map +1 -1
  5. package/dist/create-config/create-defined-resource-layout.d.mts +33 -13
  6. package/dist/create-config/create-defined-resource-layout.d.mts.map +1 -1
  7. package/dist/create-config/create-defined-resource-layout.mjs +40 -4
  8. package/dist/create-config/create-defined-resource-layout.mjs.map +1 -1
  9. package/dist/create-config/define-layout.cjs +140 -22
  10. package/dist/create-config/define-layout.cjs.map +1 -1
  11. package/dist/create-config/define-layout.d.cts +46 -15
  12. package/dist/create-config/define-layout.d.cts.map +1 -1
  13. package/dist/create-config/define-layout.d.mts +46 -15
  14. package/dist/create-config/define-layout.d.mts.map +1 -1
  15. package/dist/create-config/define-layout.mjs +140 -22
  16. package/dist/create-config/define-layout.mjs.map +1 -1
  17. package/dist/create-config/for-resource.cjs.map +1 -1
  18. package/dist/create-config/for-resource.d.cts +2 -1
  19. package/dist/create-config/for-resource.d.cts.map +1 -1
  20. package/dist/create-config/for-resource.d.mts +2 -1
  21. package/dist/create-config/for-resource.d.mts.map +1 -1
  22. package/dist/create-config/for-resource.mjs.map +1 -1
  23. package/dist/create-config/inspection.cjs +65 -0
  24. package/dist/create-config/inspection.cjs.map +1 -0
  25. package/dist/create-config/inspection.mjs +64 -0
  26. package/dist/create-config/inspection.mjs.map +1 -0
  27. package/dist/create-config/layout-name.cjs +21 -0
  28. package/dist/create-config/layout-name.cjs.map +1 -0
  29. package/dist/create-config/layout-name.d.cts +49 -0
  30. package/dist/create-config/layout-name.d.cts.map +1 -0
  31. package/dist/create-config/layout-name.d.mts +49 -0
  32. package/dist/create-config/layout-name.d.mts.map +1 -0
  33. package/dist/create-config/layout-name.mjs +20 -0
  34. package/dist/create-config/layout-name.mjs.map +1 -0
  35. package/dist/create-config/merge-layout-props.cjs +20 -0
  36. package/dist/create-config/merge-layout-props.cjs.map +1 -0
  37. package/dist/create-config/merge-layout-props.mjs +19 -0
  38. package/dist/create-config/merge-layout-props.mjs.map +1 -0
  39. package/dist/create-config/render-state.cjs.map +1 -1
  40. package/dist/create-config/render-state.d.cts +17 -13
  41. package/dist/create-config/render-state.d.cts.map +1 -1
  42. package/dist/create-config/render-state.d.mts +17 -13
  43. package/dist/create-config/render-state.d.mts.map +1 -1
  44. package/dist/create-config/render-state.mjs.map +1 -1
  45. package/dist/create-config/state-composable.cjs +3 -0
  46. package/dist/create-config/state-composable.cjs.map +1 -1
  47. package/dist/create-config/state-composable.mjs +3 -1
  48. package/dist/create-config/state-composable.mjs.map +1 -1
  49. package/dist/index.cjs +2 -0
  50. package/dist/index.d.cts +2 -1
  51. package/dist/index.d.mts +2 -1
  52. package/dist/index.mjs +2 -1
  53. package/dist/utils/capitalize.d.cts +6 -0
  54. package/dist/utils/capitalize.d.cts.map +1 -0
  55. package/dist/utils/capitalize.d.mts +6 -0
  56. package/dist/utils/capitalize.d.mts.map +1 -0
  57. package/package.json +5 -4
@@ -2,19 +2,22 @@ import { InvalidConfigError } from "../errors/invalid-config.mjs";
2
2
  import { MissingResourceProviderError } from "../errors/missing-resource-provider.mjs";
3
3
  import { LayoutConfigurationError } from "../errors/layout-configuration-error.mjs";
4
4
  import { ResourceModuleLoadError, ResourceRenderError } from "../errors/resource-state-error.mjs";
5
+ import { capitalize } from "../utils/capitalize.mjs";
6
+ import { StateComposableContext, createStateReferences, stateReferenceSource } from "./state-composable.mjs";
7
+ import { mergeLayoutProps } from "./merge-layout-props.mjs";
8
+ import { inspectDefinition, trackComposable } from "./inspection.mjs";
5
9
  import { createIsValidResourceFn, normalizeResourceDefinitions, normalizeResources, toResourceEnum } from "../resource.mjs";
6
10
  import { functionalUpdate, pick } from "../utils.mjs";
7
- import { capitalize } from "../utils/capitalize.mjs";
8
11
  import { isResourceConfigComponentKey } from "./component-keys.mjs";
9
12
  import { createResourceConfig } from "./get-component.mjs";
10
13
  import { createResourceLinksFn } from "./create-resource-links.mjs";
11
14
  import { createForResource } from "./for-resource.mjs";
12
15
  import { createForResources } from "./for-resources/create-for-resources.mjs";
13
16
  import { ResourceLayoutShellPublisherContext, useResourceLayoutShellPublisher } from "./shell-context.mjs";
14
- import { StateComposableContext, createStateReferences } from "./state-composable.mjs";
15
17
  import { deferredRenderStateSymbol, definedResourceLayoutComponentSymbol, findDefinedRenderState, isDeferredRenderState, isLazyRenderState, lazyRenderStateSymbol } from "./render-state.mjs";
16
18
  import { LayoutComposablePresetProvider, collectComposablePresetEntries, makeComposable, resolveLayoutComposables } from "@jfdevelops/react-layout-composables";
17
19
  import { Component, Suspense, createContext, lazy, useCallback, useContext, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
20
+ import { inspectionId } from "@jfdevelops/react-layout-devtools-core";
18
21
  import { attachComponentPropDefinitions, createPrimitivePropBuilder, createProp, resolvePropDefinitionValues, validateProps } from "@jfdevelops/react-layout-validator";
19
22
  import { Fragment, jsx } from "react/jsx-runtime";
20
23
 
@@ -138,9 +141,21 @@ function assertUnreservedResourceSlugs(resources, owner) {
138
141
  if (typeof resource !== "string") assertUnreservedResourceSlugs(resource.subResources, slug);
139
142
  }
140
143
  }
141
- function defineResourceLayoutImpl(options, inheritedStateScope) {
144
+ function defineResourceLayoutImpl(options, inheritedStateScope, inheritedInspection) {
142
145
  const { options: inProps = {}, resources, layout, namespace: defaultNamespace = false } = options;
143
146
  const shellId = options.shellId;
147
+ const inspection = inheritedInspection ? void 0 : inspectDefinition(resources);
148
+ if (inspection) inspection.store.node({
149
+ id: inspection.definition,
150
+ kind: "definition",
151
+ label: "Layout definition",
152
+ details: {
153
+ props: layout.props,
154
+ propSchemas: typeof inProps === "function" ? void 0 : inProps,
155
+ options: typeof inProps === "function" ? "Resource-specific schemas" : Object.keys(inProps),
156
+ states: typeof layout.render === "function" ? ["default"] : Object.keys(layout.render)
157
+ }
158
+ });
144
159
  assertUnreservedResourceSlugs(resources);
145
160
  const resourcesAccessor = createLayoutResourcesAccessor(resources);
146
161
  const ResourceContext = createContext(void 0);
@@ -158,19 +173,70 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
158
173
  const normalizedResources = normalizeResources(resources);
159
174
  const resourcesEnum = createPrimitivePropBuilder("string").enum(toResourceEnum(normalizedResources));
160
175
  const definedResourceLayout = ((layoutOptions) => {
161
- const { name, props: instancePropDefinitions, namespace = defaultNamespace, ...layoutOptionProps } = layoutOptions;
176
+ const { name: suppliedName, props: instancePropDefinitions, namespace = defaultNamespace, ...layoutOptionProps } = layoutOptions;
177
+ const naming = suppliedName ?? options.name;
178
+ const name = typeof naming === "function" ? naming({
179
+ resource: layoutOptions.resource,
180
+ states: typeof layout.render === "function" ? ["default"] : Object.keys(layout.render),
181
+ capitalize
182
+ }) : naming;
183
+ if (name !== void 0 && (typeof name !== "string" || !name)) throw new LayoutConfigurationError("A non-empty layout name is required");
162
184
  const createComposableLayout = makeComposable();
163
185
  const nameProp = createProp.string().literal(name);
186
+ const rawResolvedOptions = typeof inProps === "function" ? inProps({
187
+ resource: resourcesEnum,
188
+ name: nameProp
189
+ }) : inProps;
164
190
  const { resolvedInProps, splitInProps } = splitLayoutInProps({
165
- ...typeof inProps === "function" ? inProps({
166
- resource: resourcesEnum,
167
- name: nameProp
168
- }) : inProps,
191
+ ...rawResolvedOptions,
169
192
  ...instancePropDefinitions,
170
193
  ...layoutOptionProps
171
194
  });
172
195
  const { composables, props: legacyLayoutProps } = layout;
173
196
  const renderStates = typeof layout.render === "function" ? { default: layout.render } : layout.render;
197
+ const inspectionStore = inheritedInspection?.store ?? inspection.store;
198
+ const layoutId = inheritedInspection?.layout ?? inspectionStore.node({
199
+ id: inspectionId("layout"),
200
+ kind: "layout",
201
+ label: name ?? String(layoutOptionProps.resource),
202
+ details: {
203
+ resource: layoutOptionProps.resource,
204
+ props: legacyLayoutProps,
205
+ propSchemas: rawResolvedOptions
206
+ }
207
+ });
208
+ const resourceId = inheritedInspection?.resource ?? inspection?.resourceIds.get(String(layoutOptionProps.resource));
209
+ if (inspection) {
210
+ inspectionStore.edge(inspection.definition, layoutId, "defines");
211
+ if (resourceId) inspectionStore.edge(resourceId, layoutId, "renders");
212
+ }
213
+ const stateIds = new Map(Object.keys(renderStates).map((state) => [state, inheritedInspection?.state ?? inspectionStore.node({
214
+ id: inspectionId("state"),
215
+ kind: "state",
216
+ label: state,
217
+ unresolved: isLazyRenderState(renderStates[state]) || isDeferredRenderState(renderStates[state])
218
+ })]));
219
+ for (const id of stateIds.values()) inspectionStore.edge(layoutId, id, "defines");
220
+ const activeInspection = {
221
+ store: inspectionStore,
222
+ layout: layoutId,
223
+ resource: resourceId,
224
+ state: stateIds.get("default")
225
+ };
226
+ const referenceStateIds = /* @__PURE__ */ new Map();
227
+ function getReferenceStateId(input, label) {
228
+ let id = referenceStateIds.get(input);
229
+ if (!id) {
230
+ id = inspectionStore.node({
231
+ id: inspectionId("state-reference"),
232
+ kind: "state",
233
+ label,
234
+ details: { relationship: "Referenced render state" }
235
+ });
236
+ referenceStateIds.set(input, id);
237
+ }
238
+ return id;
239
+ }
174
240
  const referenceComponents = /* @__PURE__ */ new Map();
175
241
  const stateScope = inheritedStateScope ?? {
176
242
  states: createStateReferences(renderStates),
@@ -189,8 +255,18 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
189
255
  const defaultRenderDefinition = getSynchronousRenderStateDefinition(defaultRenderInput, legacyLayoutProps) ?? { render: () => null };
190
256
  if (typeof defaultRenderDefinition !== "object" || defaultRenderDefinition === null || !("render" in defaultRenderDefinition) || typeof defaultRenderDefinition.render !== "function") throw new LayoutConfigurationError("The \"default\" render state must define a render function");
191
257
  const defaultState = defaultRenderDefinition;
258
+ if (!inheritedInspection) inspectionStore.node({
259
+ id: activeInspection.state,
260
+ kind: "state",
261
+ label: "default",
262
+ unresolved: createdDefaultState,
263
+ details: {
264
+ props: mergeLayoutProps(legacyLayoutProps, defaultState.props),
265
+ propSchemas: rawResolvedOptions
266
+ }
267
+ });
192
268
  const render = defaultState.render;
193
- const layoutProps = defaultState.props ?? legacyLayoutProps;
269
+ const layoutProps = mergeLayoutProps(legacyLayoutProps, defaultState.props);
194
270
  const customLayoutProps = layoutProps?.custom;
195
271
  const includeLayoutProps = layoutProps?.include;
196
272
  const layoutOptionValues = layoutOptionProps;
@@ -208,7 +284,26 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
208
284
  ...composables?.(create, stateScope.states) ?? {},
209
285
  ...defaultState.composables?.(create, stateScope.states)
210
286
  }) : void 0;
211
- const resolvedComposables = defaultComposables ? resolveLayoutComposables(defaultComposables, layoutContext) : void 0;
287
+ const resolvedComposables = defaultComposables ? { ...resolveLayoutComposables(defaultComposables, layoutContext) } : void 0;
288
+ for (const [key, component] of Object.entries(resolvedComposables ?? {})) {
289
+ const id = inspectionStore.node({
290
+ id: inspectionId("composable"),
291
+ kind: "composable",
292
+ label: key,
293
+ details: {
294
+ displayName: component.displayName,
295
+ relationship: "declared; may not be rendered"
296
+ }
297
+ });
298
+ inspectionStore.edge(activeInspection.state, id, "declares");
299
+ resolvedComposables[key] = trackComposable(component, inspectionStore, id);
300
+ const reference = component[stateReferenceSource];
301
+ if (reference) {
302
+ const stateName = Object.entries(renderStates).find(([, input]) => input === reference.input)?.[0];
303
+ const target = stateName ? stateIds.get(stateName) : getReferenceStateId(reference.input, reference.name);
304
+ inspectionStore.edge(id, target, "references");
305
+ }
306
+ }
212
307
  const mergedResolvedInProps = { ...resolvedInProps };
213
308
  for (const { props: presetPropDefinitions } of collectComposablePresetEntries(resolvedComposables)) Object.assign(mergedResolvedInProps, presetPropDefinitions);
214
309
  const composablePresetProps = Object.fromEntries(collectComposablePresetEntries(resolvedComposables).map(({ key, props: presetPropDefinitions }) => {
@@ -228,6 +323,10 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
228
323
  const stateComponents = {};
229
324
  let CreatedDefaultStateComponent;
230
325
  function RenderDefaultState({ renderProps }) {
326
+ useLayoutEffect(() => {
327
+ if (CreatedDefaultStateComponent) return;
328
+ return inspectionStore.mount(activeInspection.layout, activeInspection.state, ...resourceId ? [resourceId] : []);
329
+ }, []);
231
330
  if (CreatedDefaultStateComponent) return /* @__PURE__ */ jsx(CreatedDefaultStateComponent, { ...renderProps });
232
331
  return render(renderProps, mergedRenderContext);
233
332
  }
@@ -295,6 +394,7 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
295
394
  return { makeComposable: createComposableLayout(compositionOptions) };
296
395
  }
297
396
  function createStateComponent(stateName, input, validateStateName = true, fallbackStateName = stateName) {
397
+ const inspectedId = validateStateName ? stateIds.get(stateName) : getReferenceStateId(input, stateName);
298
398
  if (isLazyRenderState(input)) {
299
399
  const loadState = input[lazyRenderStateSymbol];
300
400
  function createLazyState() {
@@ -303,7 +403,7 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
303
403
  throw new ResourceModuleLoadError(String(layoutContext.resource), error);
304
404
  }));
305
405
  if (validateStateName && stateDefinition.state !== void 0 && stateDefinition.state !== stateName) throw new LayoutConfigurationError(`Lazy render state "${stateDefinition.state}" cannot be used as "${stateName}"`);
306
- return { default: createLoadedStateComponent(validateStateName ? stateName : stateDefinition.state ?? stateName, stateDefinition) };
406
+ return { default: createLoadedStateComponent(validateStateName ? stateName : stateDefinition.state ?? stateName, stateDefinition, inspectedId) };
307
407
  });
308
408
  }
309
409
  function LazyStateComponent(props) {
@@ -336,7 +436,7 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
336
436
  }
337
437
  const definition = isDeferredRenderState(input) ? input[deferredRenderStateSymbol] : typeof input === "function" ? { render: input } : input;
338
438
  if (typeof definition !== "object" || definition === null || !("render" in definition) || typeof definition.render !== "function") throw new LayoutConfigurationError(`Render state "${stateName}" must define a render function`);
339
- const StateComponent = createLoadedStateComponent(stateName, definition);
439
+ const StateComponent = createLoadedStateComponent(stateName, definition, inspectedId);
340
440
  if (!isDeferredRenderState(input)) return StateComponent;
341
441
  const DeferredState = lazy(() => Promise.resolve({ default: StateComponent }));
342
442
  function DeferredStateComponent(props) {
@@ -359,7 +459,17 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
359
459
  }
360
460
  return DeferredStateComponent;
361
461
  }
362
- function createLoadedStateComponent(stateName, definition) {
462
+ function createLoadedStateComponent(stateName, definition, inspectedState = stateIds.get(stateName)) {
463
+ if (inspectedState) inspectionStore.node({
464
+ id: inspectedState,
465
+ kind: "state",
466
+ label: stateName,
467
+ unresolved: false,
468
+ details: {
469
+ props: mergeLayoutProps(legacyLayoutProps, definition.props),
470
+ propSchemas: rawResolvedOptions
471
+ }
472
+ });
363
473
  const stateComposables = composables || definition.composables ? (create) => ({
364
474
  ...composables?.(create, stateScope.states) ?? {},
365
475
  ...definition.composables?.(create, stateScope.states)
@@ -373,11 +483,15 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
373
483
  options: inProps,
374
484
  layout: {
375
485
  composables: stateComposables,
376
- props: definition.props,
486
+ props: mergeLayoutProps(legacyLayoutProps, definition.props),
377
487
  render: stateRender
378
488
  }
379
- }, stateScope).createResourceLayout({
489
+ }, stateScope, {
490
+ ...activeInspection,
491
+ state: inspectedState ?? getReferenceStateId(definition, stateName)
492
+ }).createResourceLayout({
380
493
  ...layoutOptions,
494
+ name,
381
495
  namespace: false
382
496
  });
383
497
  StateComponent.displayName = `${name}.${stateName}`;
@@ -409,7 +523,7 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
409
523
  const definition = mergedResolvedInProps[key];
410
524
  return [[key, isIncludedPropRequired(includeLayoutProps?.[key]) ? definition : definition.optional?.() ?? definition]];
411
525
  }));
412
- const rootComponent = attachComponentPropDefinitions(Object.assign(Component, {
526
+ const createdComponent = Object.assign(Component, {
413
527
  displayName: name,
414
528
  props: void 0,
415
529
  resource: void 0,
@@ -419,7 +533,9 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
419
533
  }),
420
534
  ...stateComponents,
421
535
  [definedResourceLayoutComponentSymbol]: true
422
- }), {
536
+ });
537
+ inspectionStore.attach(createdComponent);
538
+ const rootComponent = attachComponentPropDefinitions(createdComponent, {
423
539
  ...customLayoutProps,
424
540
  ...includedComponentDefinitions
425
541
  });
@@ -463,7 +579,7 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
463
579
  for (const { props: presetPropDefinitions } of collectComposablePresetEntries(resolvedComposables)) Object.assign(resolvedInProps, presetPropDefinitions);
464
580
  return {
465
581
  ...resolvedInProps,
466
- ...(defaultState?.props ?? layout.props)?.custom
582
+ ...mergeLayoutProps(layout.props, defaultState?.props)?.custom
467
583
  };
468
584
  }
469
585
  const { createLayoutForResourceBuilder, forResource } = createForResource({
@@ -480,14 +596,16 @@ function defineResourceLayoutImpl(options, inheritedStateScope) {
480
596
  })
481
597
  };
482
598
  if (layout.composables) createResourceLayoutExtras.makeComposable = createTopLevelMakeComposable();
483
- return {
599
+ const createResourceLayout = Object.assign(definedResourceLayout, createResourceLayoutExtras);
600
+ const createResourceLinks = createResourceLinksFn(resources);
601
+ return (inspection?.store.attach ?? ((value) => value))({
484
602
  createResourceConfig,
485
- createResourceLayout: Object.assign(definedResourceLayout, createResourceLayoutExtras),
486
- createResourceLinks: createResourceLinksFn(resources),
603
+ createResourceLayout,
604
+ createResourceLinks,
487
605
  resources: resourcesAccessor,
488
606
  ResourceProvider,
489
607
  useResource
490
- };
608
+ });
491
609
  }
492
610
  function defineResources(...resources) {
493
611
  return resources;