@cratis/scene.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.
Files changed (175) hide show
  1. package/.storybook/main.ts +31 -0
  2. package/.storybook/preview.ts +12 -0
  3. package/NavBar/NavBar.tsx +53 -0
  4. package/NavBar/extractNavigationItem.ts +40 -0
  5. package/NavBar/for_NavBar/when_rendering_navigation_contributions.tsx +62 -0
  6. package/NavBar/for_extractNavigationItem/when_extracting_from_a_navigation_contribution.ts +53 -0
  7. package/NavBar/index.ts +5 -0
  8. package/README.md +40 -0
  9. package/SceneElementView.stories.tsx +67 -0
  10. package/SceneElementView.tsx +22 -0
  11. package/core/CoreButton.tsx +17 -0
  12. package/core/CoreCard.tsx +15 -0
  13. package/core/CoreText.tsx +13 -0
  14. package/core/coreComponents.ts +18 -0
  15. package/core/corePackage.ts +36 -0
  16. package/core/index.ts +8 -0
  17. package/dist/cjs/NavBar/NavBar.js +47 -0
  18. package/dist/cjs/NavBar/NavBar.js.map +1 -0
  19. package/dist/cjs/NavBar/extractNavigationItem.js +38 -0
  20. package/dist/cjs/NavBar/extractNavigationItem.js.map +1 -0
  21. package/dist/cjs/SceneElementView.js +21 -0
  22. package/dist/cjs/SceneElementView.js.map +1 -0
  23. package/dist/cjs/core/CoreButton.js +22 -0
  24. package/dist/cjs/core/CoreButton.js.map +1 -0
  25. package/dist/cjs/core/CoreCard.js +22 -0
  26. package/dist/cjs/core/CoreCard.js.map +1 -0
  27. package/dist/cjs/core/CoreText.js +19 -0
  28. package/dist/cjs/core/CoreText.js.map +1 -0
  29. package/dist/cjs/core/coreComponents.js +20 -0
  30. package/dist/cjs/core/coreComponents.js.map +1 -0
  31. package/dist/cjs/core/corePackage.js +41 -0
  32. package/dist/cjs/core/corePackage.js.map +1 -0
  33. package/dist/cjs/index.js +38 -0
  34. package/dist/cjs/index.js.map +1 -0
  35. package/dist/cjs/package.json +3 -0
  36. package/dist/cjs/packages/ScenePackageBundle.js +76 -0
  37. package/dist/cjs/packages/ScenePackageBundle.js.map +1 -0
  38. package/dist/cjs/renderer/UnresolvedComponent.js +28 -0
  39. package/dist/cjs/renderer/UnresolvedComponent.js.map +1 -0
  40. package/dist/cjs/renderer/createReactRenderer.js +48 -0
  41. package/dist/cjs/renderer/createReactRenderer.js.map +1 -0
  42. package/dist/cjs/theme/SceneThemeProvider.js +44 -0
  43. package/dist/cjs/theme/SceneThemeProvider.js.map +1 -0
  44. package/dist/cjs/theme/themeTokens.js +52 -0
  45. package/dist/cjs/theme/themeTokens.js.map +1 -0
  46. package/dist/esm/NavBar/NavBar.d.ts +7 -0
  47. package/dist/esm/NavBar/NavBar.d.ts.map +1 -0
  48. package/dist/esm/NavBar/NavBar.js +45 -0
  49. package/dist/esm/NavBar/NavBar.js.map +1 -0
  50. package/dist/esm/NavBar/extractNavigationItem.d.ts +3 -0
  51. package/dist/esm/NavBar/extractNavigationItem.d.ts.map +1 -0
  52. package/dist/esm/NavBar/extractNavigationItem.js +36 -0
  53. package/dist/esm/NavBar/extractNavigationItem.js.map +1 -0
  54. package/dist/esm/NavBar/for_NavBar/when_rendering_navigation_contributions.d.ts +2 -0
  55. package/dist/esm/NavBar/for_NavBar/when_rendering_navigation_contributions.d.ts.map +1 -0
  56. package/dist/esm/NavBar/for_NavBar/when_rendering_navigation_contributions.js +53 -0
  57. package/dist/esm/NavBar/for_NavBar/when_rendering_navigation_contributions.js.map +1 -0
  58. package/dist/esm/NavBar/for_extractNavigationItem/when_extracting_from_a_navigation_contribution.d.ts +2 -0
  59. package/dist/esm/NavBar/for_extractNavigationItem/when_extracting_from_a_navigation_contribution.d.ts.map +1 -0
  60. package/dist/esm/NavBar/for_extractNavigationItem/when_extracting_from_a_navigation_contribution.js +45 -0
  61. package/dist/esm/NavBar/for_extractNavigationItem/when_extracting_from_a_navigation_contribution.js.map +1 -0
  62. package/dist/esm/NavBar/index.d.ts +3 -0
  63. package/dist/esm/NavBar/index.d.ts.map +1 -0
  64. package/dist/esm/NavBar/index.js +3 -0
  65. package/dist/esm/NavBar/index.js.map +1 -0
  66. package/dist/esm/SceneElementView.d.ts +10 -0
  67. package/dist/esm/SceneElementView.d.ts.map +1 -0
  68. package/dist/esm/SceneElementView.js +19 -0
  69. package/dist/esm/SceneElementView.js.map +1 -0
  70. package/dist/esm/SceneElementView.stories.d.ts +15 -0
  71. package/dist/esm/SceneElementView.stories.d.ts.map +1 -0
  72. package/dist/esm/SceneElementView.stories.js +54 -0
  73. package/dist/esm/SceneElementView.stories.js.map +1 -0
  74. package/dist/esm/core/CoreButton.d.ts +3 -0
  75. package/dist/esm/core/CoreButton.d.ts.map +1 -0
  76. package/dist/esm/core/CoreButton.js +20 -0
  77. package/dist/esm/core/CoreButton.js.map +1 -0
  78. package/dist/esm/core/CoreCard.d.ts +3 -0
  79. package/dist/esm/core/CoreCard.d.ts.map +1 -0
  80. package/dist/esm/core/CoreCard.js +20 -0
  81. package/dist/esm/core/CoreCard.js.map +1 -0
  82. package/dist/esm/core/CoreText.d.ts +3 -0
  83. package/dist/esm/core/CoreText.d.ts.map +1 -0
  84. package/dist/esm/core/CoreText.js +17 -0
  85. package/dist/esm/core/CoreText.js.map +1 -0
  86. package/dist/esm/core/coreComponents.d.ts +3 -0
  87. package/dist/esm/core/coreComponents.d.ts.map +1 -0
  88. package/dist/esm/core/coreComponents.js +18 -0
  89. package/dist/esm/core/coreComponents.js.map +1 -0
  90. package/dist/esm/core/corePackage.d.ts +5 -0
  91. package/dist/esm/core/corePackage.d.ts.map +1 -0
  92. package/dist/esm/core/corePackage.js +38 -0
  93. package/dist/esm/core/corePackage.js.map +1 -0
  94. package/dist/esm/core/index.d.ts +6 -0
  95. package/dist/esm/core/index.d.ts.map +1 -0
  96. package/dist/esm/core/index.js +6 -0
  97. package/dist/esm/core/index.js.map +1 -0
  98. package/dist/esm/for_SceneElementView/when_rendering_a_tree.d.ts +2 -0
  99. package/dist/esm/for_SceneElementView/when_rendering_a_tree.d.ts.map +1 -0
  100. package/dist/esm/for_SceneElementView/when_rendering_a_tree.js +44 -0
  101. package/dist/esm/for_SceneElementView/when_rendering_a_tree.js.map +1 -0
  102. package/dist/esm/for_applyThemeTokens/when_switching_themes.d.ts +2 -0
  103. package/dist/esm/for_applyThemeTokens/when_switching_themes.d.ts.map +1 -0
  104. package/dist/esm/for_applyThemeTokens/when_switching_themes.js +59 -0
  105. package/dist/esm/for_applyThemeTokens/when_switching_themes.js.map +1 -0
  106. package/dist/esm/for_themeTokens/when_converting_a_token_name.d.ts +2 -0
  107. package/dist/esm/for_themeTokens/when_converting_a_token_name.d.ts.map +1 -0
  108. package/dist/esm/for_themeTokens/when_converting_a_token_name.js +16 -0
  109. package/dist/esm/for_themeTokens/when_converting_a_token_name.js.map +1 -0
  110. package/dist/esm/for_validatePackageBundle/when_the_bundle_and_manifest_disagree.d.ts +2 -0
  111. package/dist/esm/for_validatePackageBundle/when_the_bundle_and_manifest_disagree.d.ts.map +1 -0
  112. package/dist/esm/for_validatePackageBundle/when_the_bundle_and_manifest_disagree.js +59 -0
  113. package/dist/esm/for_validatePackageBundle/when_the_bundle_and_manifest_disagree.js.map +1 -0
  114. package/dist/esm/for_validatePackageBundle/when_the_bundle_matches_its_manifest.d.ts +2 -0
  115. package/dist/esm/for_validatePackageBundle/when_the_bundle_matches_its_manifest.d.ts.map +1 -0
  116. package/dist/esm/for_validatePackageBundle/when_the_bundle_matches_its_manifest.js +8 -0
  117. package/dist/esm/for_validatePackageBundle/when_the_bundle_matches_its_manifest.js.map +1 -0
  118. package/dist/esm/index.d.ts +7 -0
  119. package/dist/esm/index.d.ts.map +1 -0
  120. package/dist/esm/index.js +14 -0
  121. package/dist/esm/index.js.map +1 -0
  122. package/dist/esm/package.json +3 -0
  123. package/dist/esm/packages/ScenePackageBundle.d.ts +15 -0
  124. package/dist/esm/packages/ScenePackageBundle.d.ts.map +1 -0
  125. package/dist/esm/packages/ScenePackageBundle.js +72 -0
  126. package/dist/esm/packages/ScenePackageBundle.js.map +1 -0
  127. package/dist/esm/packages/index.d.ts +2 -0
  128. package/dist/esm/packages/index.d.ts.map +1 -0
  129. package/dist/esm/packages/index.js +2 -0
  130. package/dist/esm/packages/index.js.map +1 -0
  131. package/dist/esm/renderer/ComponentRegistry.d.ts +8 -0
  132. package/dist/esm/renderer/ComponentRegistry.d.ts.map +1 -0
  133. package/dist/esm/renderer/ComponentRegistry.js +2 -0
  134. package/dist/esm/renderer/ComponentRegistry.js.map +1 -0
  135. package/dist/esm/renderer/UnresolvedComponent.d.ts +3 -0
  136. package/dist/esm/renderer/UnresolvedComponent.d.ts.map +1 -0
  137. package/dist/esm/renderer/UnresolvedComponent.js +26 -0
  138. package/dist/esm/renderer/UnresolvedComponent.js.map +1 -0
  139. package/dist/esm/renderer/createReactRenderer.d.ts +5 -0
  140. package/dist/esm/renderer/createReactRenderer.d.ts.map +1 -0
  141. package/dist/esm/renderer/createReactRenderer.js +46 -0
  142. package/dist/esm/renderer/createReactRenderer.js.map +1 -0
  143. package/dist/esm/renderer/index.d.ts +4 -0
  144. package/dist/esm/renderer/index.d.ts.map +1 -0
  145. package/dist/esm/renderer/index.js +4 -0
  146. package/dist/esm/renderer/index.js.map +1 -0
  147. package/dist/esm/theme/SceneThemeProvider.d.ts +10 -0
  148. package/dist/esm/theme/SceneThemeProvider.d.ts.map +1 -0
  149. package/dist/esm/theme/SceneThemeProvider.js +41 -0
  150. package/dist/esm/theme/SceneThemeProvider.js.map +1 -0
  151. package/dist/esm/theme/index.d.ts +3 -0
  152. package/dist/esm/theme/index.d.ts.map +1 -0
  153. package/dist/esm/theme/index.js +3 -0
  154. package/dist/esm/theme/index.js.map +1 -0
  155. package/dist/esm/theme/themeTokens.d.ts +5 -0
  156. package/dist/esm/theme/themeTokens.d.ts.map +1 -0
  157. package/dist/esm/theme/themeTokens.js +48 -0
  158. package/dist/esm/theme/themeTokens.js.map +1 -0
  159. package/dist/esm/tsconfig.tsbuildinfo +1 -0
  160. package/for_SceneElementView/when_rendering_a_tree.tsx +53 -0
  161. package/for_applyThemeTokens/when_switching_themes.ts +74 -0
  162. package/for_themeTokens/when_converting_a_token_name.ts +22 -0
  163. package/for_validatePackageBundle/when_the_bundle_and_manifest_disagree.ts +72 -0
  164. package/for_validatePackageBundle/when_the_bundle_matches_its_manifest.ts +12 -0
  165. package/index.ts +9 -0
  166. package/package.json +60 -0
  167. package/packages/ScenePackageBundle.ts +137 -0
  168. package/packages/index.ts +4 -0
  169. package/renderer/ComponentRegistry.ts +23 -0
  170. package/renderer/UnresolvedComponent.tsx +20 -0
  171. package/renderer/createReactRenderer.tsx +35 -0
  172. package/renderer/index.ts +6 -0
  173. package/theme/SceneThemeProvider.tsx +58 -0
  174. package/theme/index.ts +5 -0
  175. package/theme/themeTokens.ts +59 -0
@@ -0,0 +1,31 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import type { StorybookConfig } from "@storybook/react-vite";
5
+ import { dirname, join, resolve } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import type { InlineConfig } from 'vite';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
11
+ const config: StorybookConfig = {
12
+ // Scoped to the package root only (not `**`) so this glob never reaches into `dist/`, which would
13
+ // otherwise index both the source .stories.tsx and its own tsc-emitted .stories.js as duplicates.
14
+ stories: ["../*.stories.@(js|jsx|mjs|ts|tsx)"],
15
+ addons: [getAbsolutePath("@storybook/addon-links")],
16
+ framework: { name: getAbsolutePath("@storybook/react-vite"), options: {} },
17
+ async viteFinal(config: InlineConfig) {
18
+ config.resolve = config.resolve || {};
19
+ config.resolve.alias = {
20
+ ...config.resolve.alias,
21
+ '@cratis/scene.engine': resolve(__dirname, '../../engine/index.ts'),
22
+ '@cratis/scene.model': resolve(__dirname, '../../model/index.ts'),
23
+ };
24
+ return config;
25
+ },
26
+ };
27
+ export default config;
28
+
29
+ function getAbsolutePath(value: string): string {
30
+ return dirname(fileURLToPath(import.meta.resolve(join(value, "package.json"))));
31
+ }
@@ -0,0 +1,12 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import type { Preview } from "@storybook/react";
5
+
6
+ const preview: Preview = {
7
+ parameters: {
8
+ backgrounds: { default: 'light', values: [{ name: 'light', value: '#ffffff' }, { name: 'dark', value: '#1a1a1a' }] },
9
+ controls: { matchers: { color: /(background|color)$/i, date: /Date$/i } },
10
+ },
11
+ };
12
+ export default preview;
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { Contribution, NavigationItem } from '@cratis/scene.model';
5
+ import { aggregateContributions } from '@cratis/scene.engine';
6
+ import { extractNavigationItem } from './extractNavigationItem';
7
+
8
+ export interface NavBarProps {
9
+ /** Every contribution in scope - `NavBar` aggregates the ones targeting `Navigation` itself. */
10
+ contributions: Contribution[];
11
+
12
+ /**
13
+ * Turns a {@link NavigationItem}'s target screen and route parameters into a concrete route - a URL
14
+ * path, a query string, or a native deep link. Owning this as a caller-supplied function, rather than
15
+ * a fixed URL scheme, is what makes `NavBar` usable unmodified across renderers (Cratis/Scene#2).
16
+ */
17
+ renderRoute: (item: NavigationItem) => string;
18
+ }
19
+
20
+ /**
21
+ * Renders the aggregated `Navigation` contributions from across the current element tree - part of
22
+ * Cratis/Scene#2. `NavBar` is not wired to a fixed source: pass it whatever contributions are currently
23
+ * in scope and it recomputes, the same way any contribution-point consumer does.
24
+ */
25
+ export function NavBar({ contributions, renderRoute }: NavBarProps) {
26
+ const items = aggregateContributions(contributions, 'Navigation')
27
+ .map(extractNavigationItem)
28
+ .filter((item): item is NavigationItem => item !== undefined);
29
+
30
+ const hasGroups = items.some(item => item.group !== undefined);
31
+ if (!hasGroups) {
32
+ return (
33
+ <nav data-scene-navbar="">
34
+ {items.map(item => <NavBarItem key={item.targetScreen} item={item} href={renderRoute(item)} />)}
35
+ </nav>
36
+ );
37
+ }
38
+
39
+ const groupNames = [...new Set(items.map(item => item.group ?? ''))];
40
+ return (
41
+ <nav data-scene-navbar="">
42
+ {groupNames.map(groupName => (
43
+ <section key={groupName} data-scene-navbar-group={groupName}>
44
+ {items.filter(item => (item.group ?? '') === groupName).map(item => <NavBarItem key={item.targetScreen} item={item} href={renderRoute(item)} />)}
45
+ </section>
46
+ ))}
47
+ </nav>
48
+ );
49
+ }
50
+
51
+ function NavBarItem({ item, href }: { item: NavigationItem; href: string }) {
52
+ return <a href={href}>{item.label}</a>;
53
+ }
@@ -0,0 +1,40 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { BindingExpression, Contribution, ExternalComponent, NavigationItem } from '@cratis/scene.model';
5
+
6
+ /**
7
+ * Reads a {@link NavigationItem} out of a {@link Contribution} to the built-in `Navigation` contribution
8
+ * point. `Contribution.content` carries an {@link ExternalComponent} whose open `properties` bag holds the
9
+ * navigation-specific values (`label`, `targetScreen`, `routeParameterBindings`, `order`, `group`) - the
10
+ * same names as {@link NavigationItem}'s own fields. This property-bag contract is Scene#2's own choice,
11
+ * made without a real Screenplay-to-Scene translation to confirm it against (Stage#37 hasn't been built
12
+ * yet) - whoever builds that seam should either produce contributions matching this contract or this
13
+ * function should move to match whatever Stage#37 actually emits.
14
+ *
15
+ * @param contribution The contribution to read.
16
+ * @returns The extracted {@link NavigationItem}, or `undefined` when `contribution.content` isn't an {@link ExternalComponent} or is missing a required property.
17
+ */
18
+ export function extractNavigationItem(contribution: Contribution): NavigationItem | undefined {
19
+ const content = contribution.content as Partial<ExternalComponent>;
20
+ if (!content.properties) {
21
+ return undefined;
22
+ }
23
+
24
+ const { label, targetScreen, routeParameterBindings, order, group } = content.properties as Record<string, unknown>;
25
+ if (typeof label !== 'string' || typeof targetScreen !== 'string') {
26
+ return undefined;
27
+ }
28
+
29
+ return {
30
+ label,
31
+ targetScreen,
32
+ routeParameterBindings: isBindingExpressionRecord(routeParameterBindings) ? routeParameterBindings : {},
33
+ order: typeof order === 'number' ? order : contribution.order,
34
+ group: typeof group === 'string' ? group : undefined,
35
+ };
36
+ }
37
+
38
+ function isBindingExpressionRecord(value: unknown): value is Record<string, BindingExpression> {
39
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
40
+ }
@@ -0,0 +1,62 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { render, screen } from '@testing-library/react';
5
+ import { Contribution, ExternalComponent, HorizontalAlignment, VerticalAlignment, Visibility } from '@cratis/scene.model';
6
+ import { NavBar } from '../NavBar';
7
+
8
+ function navigationContribution(id: string, properties: Record<string, unknown>, order?: number): Contribution {
9
+ const content: ExternalComponent = {
10
+ id,
11
+ name: id,
12
+ properties,
13
+ visibility: Visibility.Visible,
14
+ isEnabled: true,
15
+ opacity: 1,
16
+ size: {},
17
+ zIndex: 0,
18
+ minimumSize: {},
19
+ maximumSize: {},
20
+ margin: { left: 0, top: 0, right: 0, bottom: 0 },
21
+ horizontalAlignment: HorizontalAlignment.Stretch,
22
+ verticalAlignment: VerticalAlignment.Stretch,
23
+ componentName: 'core:navigation-item',
24
+ slots: {},
25
+ };
26
+ return { contributionPointName: 'Navigation', content, order };
27
+ }
28
+
29
+ describe('when rendering navigation contributions', () => {
30
+ describe('and no item declares a group', () => {
31
+ const contributions = [
32
+ navigationContribution('adjustments', { label: 'Adjustments', targetScreen: 'Adjustments' }, 20),
33
+ navigationContribution('invoices', { label: 'Invoices', targetScreen: 'InvoiceList' }, 10),
34
+ ];
35
+
36
+ beforeEach(() => {
37
+ render(<NavBar contributions={contributions} renderRoute={item => `/${item.targetScreen}`} />);
38
+ });
39
+
40
+ it('should render every item as a link to its rendered route', () => {
41
+ screen.getByRole('link', { name: 'Invoices' }).getAttribute('href')!.should.equal('/InvoiceList');
42
+ screen.getByRole('link', { name: 'Adjustments' }).getAttribute('href')!.should.equal('/Adjustments');
43
+ });
44
+
45
+ it('should render items in aggregated order', () => {
46
+ const links = screen.getAllByRole('link').map(link => link.textContent);
47
+ links.should.deep.equal(['Invoices', 'Adjustments']);
48
+ });
49
+ });
50
+
51
+ describe('and a contribution cannot be extracted into a navigation item', () => {
52
+ const contributions = [navigationContribution('broken', { label: 'Broken' })];
53
+
54
+ beforeEach(() => {
55
+ render(<NavBar contributions={contributions} renderRoute={item => `/${item.targetScreen}`} />);
56
+ });
57
+
58
+ it('should skip it rather than render a broken link', () => {
59
+ screen.queryAllByRole('link').should.have.lengthOf(0);
60
+ });
61
+ });
62
+ });
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { Contribution, ExternalComponent, HorizontalAlignment, NavigationItem, VerticalAlignment, Visibility } from '@cratis/scene.model';
5
+ import { extractNavigationItem } from '../extractNavigationItem';
6
+
7
+ function navigationContribution(properties: Record<string, unknown>, order?: number): Contribution {
8
+ const content: ExternalComponent = {
9
+ id: 'nav-item',
10
+ name: 'nav-item',
11
+ properties,
12
+ visibility: Visibility.Visible,
13
+ isEnabled: true,
14
+ opacity: 1,
15
+ size: {},
16
+ zIndex: 0,
17
+ minimumSize: {},
18
+ maximumSize: {},
19
+ margin: { left: 0, top: 0, right: 0, bottom: 0 },
20
+ horizontalAlignment: HorizontalAlignment.Stretch,
21
+ verticalAlignment: VerticalAlignment.Stretch,
22
+ componentName: 'core:navigation-item',
23
+ slots: {},
24
+ };
25
+ return { contributionPointName: 'Navigation', content, order };
26
+ }
27
+
28
+ describe('when extracting from a navigation contribution', () => {
29
+ describe('and the required properties are present', () => {
30
+ let result: NavigationItem | undefined;
31
+
32
+ beforeEach(() => {
33
+ result = extractNavigationItem(navigationContribution({ label: 'Invoices', targetScreen: 'InvoiceList', group: 'Sales' }, 10));
34
+ });
35
+
36
+ it('should extract the label', () => result!.label.should.equal('Invoices'));
37
+ it('should extract the target screen', () => result!.targetScreen.should.equal('InvoiceList'));
38
+ it('should extract the group', () => result!.group!.should.equal('Sales'));
39
+ it('should fall back to the contribution order when the properties bag has none', () => result!.order!.should.equal(10));
40
+ });
41
+
42
+ describe('and the label is missing', () => {
43
+ it('should return undefined', () => {
44
+ (extractNavigationItem(navigationContribution({ targetScreen: 'InvoiceList' })) === undefined).should.be.true;
45
+ });
46
+ });
47
+
48
+ describe('and the target screen is missing', () => {
49
+ it('should return undefined', () => {
50
+ (extractNavigationItem(navigationContribution({ label: 'Invoices' })) === undefined).should.be.true;
51
+ });
52
+ });
53
+ });
@@ -0,0 +1,5 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ export * from './NavBar';
5
+ export * from './extractNavigationItem';
package/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Scene
2
+
3
+ The object model and runtime for Screenplay-based UIs: `Scene.Model` (platform-agnostic screens, layouts,
4
+ forms, contribution points, ui profiles and themes), `Scene.Engine` (binding/resolution runtime), and
5
+ `Scene.React` (the React renderer).
6
+
7
+ ## Packages / Deployables
8
+
9
+ [![Nuget](https://img.shields.io/nuget/v/Cratis.Scene.Model?logo=nuget)](http://nuget.org/packages/Cratis.Scene.Model)
10
+ [![NPM](https://img.shields.io/npm/v/@cratis/scene.model?label=@cratis/scene.model&logo=npm)](https://www.npmjs.com/package/@cratis/scene.model)
11
+ [![NPM](https://img.shields.io/npm/v/@cratis/scene.engine?label=@cratis/scene.engine&logo=npm)](https://www.npmjs.com/package/@cratis/scene.engine)
12
+ [![NPM](https://img.shields.io/npm/v/@cratis/scene.react?label=@cratis/scene.react&logo=npm)](https://www.npmjs.com/package/@cratis/scene.react)
13
+
14
+ ## Builds
15
+
16
+ [![.NET Build](https://github.com/cratis/scene/actions/workflows/dotnet-build.yml/badge.svg)](https://github.com/cratis/scene/actions/workflows/dotnet-build.yml)
17
+ [![JavaScript Build](https://github.com/cratis/scene/actions/workflows/javascript-build.yml/badge.svg)](https://github.com/cratis/scene/actions/workflows/javascript-build.yml)
18
+
19
+ ## Description
20
+
21
+ Scene owns the *object model and runtime* half of the Screenplay stack. `Scene.Model` is the platform-agnostic
22
+ C# object model that Stage translates Screenplay's parsed language constructs into, and that Studio's
23
+ design-time tooling reads and writes. `Scene.Engine` walks that graph, resolves bindings (data, action, form,
24
+ navigation contribution) and drives whichever renderer is plugged in — it does not know React exists.
25
+ `Scene.React` is one renderer implementing the abstract vocabulary against real React/DOM; sibling positions
26
+ are reserved for future `Scene.Native` / `Scene.Desktop` renderers. Vendor and internal component packages
27
+ (PrimeReact, internal widget libraries) are adapters plugging into the renderer contract, not part of it.
28
+
29
+ The design-time editor built on top of this object model lives in Studio, not here — see
30
+ [StudioIssues#156](https://github.com/Cratis/StudioIssues/issues/156).
31
+
32
+ ## Support
33
+
34
+ Cratis is an open community, and we are glad to help users, teams evaluating the stack, and contributors.
35
+
36
+ | Channel | Details |
37
+ |---|---|
38
+ | Discord | Join the community on [Discord](https://discord.gg/kt4AMpV8WV) for questions and discussions |
39
+ | GitHub Issues | [Report bugs or request features](https://github.com/Cratis/Scene/issues) |
40
+ | Documentation | Read the docs at [cratis.io](https://cratis.io) |
@@ -0,0 +1,67 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import type { Meta, StoryObj } from '@storybook/react';
5
+ import { Control, ExternalComponent, HorizontalAlignment, Panel, VerticalAlignment, Visibility } from '@cratis/scene.model';
6
+ import { SceneElementView } from './SceneElementView';
7
+ import { coreComponents } from './core';
8
+
9
+ function control(id: string): Control {
10
+ return {
11
+ id,
12
+ name: id,
13
+ properties: {},
14
+ visibility: Visibility.Visible,
15
+ isEnabled: true,
16
+ opacity: 1,
17
+ size: {},
18
+ zIndex: 0,
19
+ minimumSize: {},
20
+ maximumSize: {},
21
+ margin: { left: 0, top: 0, right: 0, bottom: 0 },
22
+ horizontalAlignment: HorizontalAlignment.Stretch,
23
+ verticalAlignment: VerticalAlignment.Stretch,
24
+ borderThickness: { left: 0, top: 0, right: 0, bottom: 0 },
25
+ padding: { left: 0, top: 0, right: 0, bottom: 0 },
26
+ tabIndex: 0,
27
+ };
28
+ }
29
+
30
+ function coreComponent(id: string, componentName: string, properties: Record<string, unknown> = {}): ExternalComponent {
31
+ return { ...control(id), properties, componentName, slots: {} };
32
+ }
33
+
34
+ const card: ExternalComponent = {
35
+ ...control('welcome-card'),
36
+ componentName: 'core:card',
37
+ slots: { content: [coreComponent('welcome-text', 'core:text', { text: 'Welcome to Scene' })] },
38
+ };
39
+
40
+ const root: Panel = { ...control('root'), children: [card] };
41
+
42
+ const meta = {
43
+ title: 'Scene/SceneElementView',
44
+ component: SceneElementView,
45
+ tags: ['autodocs'],
46
+ parameters: { layout: 'padded' },
47
+ } satisfies Meta<typeof SceneElementView>;
48
+
49
+ export default meta;
50
+
51
+ type Story = StoryObj<typeof meta>;
52
+
53
+ export const CoreCardWithText: Story = {
54
+ args: {
55
+ element: root,
56
+ registry: coreComponents,
57
+ resolveBinding: () => undefined,
58
+ },
59
+ };
60
+
61
+ export const UnresolvedComponent: Story = {
62
+ args: {
63
+ element: coreComponent('missing', 'vendor:fancy-widget'),
64
+ registry: coreComponents,
65
+ resolveBinding: () => undefined,
66
+ },
67
+ };
@@ -0,0 +1,22 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { useMemo } from 'react';
5
+ import { renderElement, BindingResolver } from '@cratis/scene.engine';
6
+ import { SceneElement } from '@cratis/scene.model';
7
+ import { ComponentRegistry, createReactRenderer } from './renderer';
8
+
9
+ export interface SceneElementViewProps {
10
+ element: SceneElement;
11
+ registry: ComponentRegistry;
12
+ resolveBinding: BindingResolver;
13
+ }
14
+
15
+ /**
16
+ * Renders a Scene element tree with the real `Scene.React` renderer - the WYSIWYG building block Studio's
17
+ * preview surface and Stage's shipped web bundle both consume unmodified.
18
+ */
19
+ export function SceneElementView({ element, registry, resolveBinding }: SceneElementViewProps) {
20
+ const renderer = useMemo(() => createReactRenderer(registry), [registry]);
21
+ return <>{renderElement(element, renderer, resolveBinding)}</>;
22
+ }
@@ -0,0 +1,17 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { RegisteredComponentProps } from '../renderer';
5
+
6
+ /**
7
+ * The `core:button` component - part of the minimum vocabulary a `ui profile` can always fall back to.
8
+ */
9
+ export function CoreButton({ element, slots }: RegisteredComponentProps) {
10
+ const label = typeof element.properties.label === 'string' ? element.properties.label : '';
11
+ return (
12
+ <button type="button" data-scene-id={element.id}>
13
+ {label}
14
+ {slots.content}
15
+ </button>
16
+ );
17
+ }
@@ -0,0 +1,15 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { RegisteredComponentProps } from '../renderer';
5
+
6
+ /**
7
+ * The `core:card` component - part of the minimum vocabulary a `ui profile` can always fall back to.
8
+ */
9
+ export function CoreCard({ element, slots }: RegisteredComponentProps) {
10
+ return (
11
+ <section data-scene-id={element.id} style={{ border: '1px solid #ccc', borderRadius: 4, padding: '0.75rem' }}>
12
+ {slots.content}
13
+ </section>
14
+ );
15
+ }
@@ -0,0 +1,13 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { RegisteredComponentProps } from '../renderer';
5
+
6
+ /**
7
+ * The `core:text` component - the minimum vocabulary a `ui profile` can always fall back to, regardless
8
+ * of which packages it lists, per Scene#3's `core` fallback guarantee.
9
+ */
10
+ export function CoreText({ element }: RegisteredComponentProps) {
11
+ const text = typeof element.properties.text === 'string' ? element.properties.text : '';
12
+ return <span data-scene-id={element.id}>{text}</span>;
13
+ }
@@ -0,0 +1,18 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { ComponentRegistry } from '../renderer';
5
+ import { componentRegistryKey } from '../packages';
6
+ import { CoreText } from './CoreText';
7
+ import { CoreButton } from './CoreButton';
8
+ import { CoreCard } from './CoreCard';
9
+
10
+ /**
11
+ * The `core` package's component registry - the final fallback every `ui profile` resolves against
12
+ * regardless of which other packages it lists (Scene#3).
13
+ */
14
+ export const coreComponents: ComponentRegistry = {
15
+ [componentRegistryKey('core', 'text')]: CoreText,
16
+ [componentRegistryKey('core', 'button')]: CoreButton,
17
+ [componentRegistryKey('core', 'card')]: CoreCard,
18
+ };
@@ -0,0 +1,36 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { PackageKind, ScenePackage } from '@cratis/scene.model';
5
+ import { ScenePackageBundle } from '../packages';
6
+ import { coreComponents } from './coreComponents';
7
+
8
+ /**
9
+ * The `core` package's declaration. Every other package is loaded by a host that reads its manifest;
10
+ * `core` ships inside the renderer itself, because Scene#3 guarantees a bare name always has somewhere to
11
+ * resolve regardless of which packages a profile lists. It is still declared the same way as any other -
12
+ * a package picker should be able to show it, and the dependency resolver should be able to reason about
13
+ * it, without a special case.
14
+ */
15
+ export const corePackageManifest: ScenePackage = {
16
+ name: 'core',
17
+ version: '1.0.0',
18
+ kind: PackageKind.ComponentLibrary,
19
+ dependencies: [],
20
+ components: ['text', 'button', 'card'],
21
+ layouts: [],
22
+ screenTemplates: [],
23
+ dialogTemplates: [],
24
+ themes: [],
25
+ displayName: 'Core',
26
+ description: 'The built-in fallback vocabulary every ui profile resolves against.',
27
+ module: '@cratis/scene.react',
28
+ };
29
+
30
+ /**
31
+ * The `core` package as a loadable bundle.
32
+ */
33
+ export const corePackage: ScenePackageBundle = {
34
+ manifest: corePackageManifest,
35
+ components: coreComponents,
36
+ };
package/core/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ export * from './CoreText';
5
+ export * from './CoreButton';
6
+ export * from './CoreCard';
7
+ export * from './coreComponents';
8
+ export * from './corePackage';
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var scene_engine = require('@cratis/scene.engine');
5
+ var extractNavigationItem = require('./extractNavigationItem.js');
6
+
7
+ // Copyright (c) Cratis. All rights reserved.
8
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
9
+ /**
10
+ * Renders the aggregated `Navigation` contributions from across the current element tree - part of
11
+ * Cratis/Scene#2. `NavBar` is not wired to a fixed source: pass it whatever contributions are currently
12
+ * in scope and it recomputes, the same way any contribution-point consumer does.
13
+ */ function NavBar({ contributions, renderRoute }) {
14
+ const items = scene_engine.aggregateContributions(contributions, 'Navigation').map(extractNavigationItem.extractNavigationItem).filter((item)=>item !== undefined);
15
+ const hasGroups = items.some((item)=>item.group !== undefined);
16
+ if (!hasGroups) {
17
+ return /*#__PURE__*/ jsxRuntime.jsx("nav", {
18
+ "data-scene-navbar": "",
19
+ children: items.map((item)=>/*#__PURE__*/ jsxRuntime.jsx(NavBarItem, {
20
+ item: item,
21
+ href: renderRoute(item)
22
+ }, item.targetScreen))
23
+ });
24
+ }
25
+ const groupNames = [
26
+ ...new Set(items.map((item)=>item.group ?? ''))
27
+ ];
28
+ return /*#__PURE__*/ jsxRuntime.jsx("nav", {
29
+ "data-scene-navbar": "",
30
+ children: groupNames.map((groupName)=>/*#__PURE__*/ jsxRuntime.jsx("section", {
31
+ "data-scene-navbar-group": groupName,
32
+ children: items.filter((item)=>(item.group ?? '') === groupName).map((item)=>/*#__PURE__*/ jsxRuntime.jsx(NavBarItem, {
33
+ item: item,
34
+ href: renderRoute(item)
35
+ }, item.targetScreen))
36
+ }, groupName))
37
+ });
38
+ }
39
+ function NavBarItem({ item, href }) {
40
+ return /*#__PURE__*/ jsxRuntime.jsx("a", {
41
+ href: href,
42
+ children: item.label
43
+ });
44
+ }
45
+
46
+ exports.NavBar = NavBar;
47
+ //# sourceMappingURL=NavBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavBar.js","sources":["../../../NavBar/NavBar.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Contribution, NavigationItem } from '@cratis/scene.model';\nimport { aggregateContributions } from '@cratis/scene.engine';\nimport { extractNavigationItem } from './extractNavigationItem';\n\nexport interface NavBarProps {\n /** Every contribution in scope - `NavBar` aggregates the ones targeting `Navigation` itself. */\n contributions: Contribution[];\n\n /**\n * Turns a {@link NavigationItem}'s target screen and route parameters into a concrete route - a URL\n * path, a query string, or a native deep link. Owning this as a caller-supplied function, rather than\n * a fixed URL scheme, is what makes `NavBar` usable unmodified across renderers (Cratis/Scene#2).\n */\n renderRoute: (item: NavigationItem) => string;\n}\n\n/**\n * Renders the aggregated `Navigation` contributions from across the current element tree - part of\n * Cratis/Scene#2. `NavBar` is not wired to a fixed source: pass it whatever contributions are currently\n * in scope and it recomputes, the same way any contribution-point consumer does.\n */\nexport function NavBar({ contributions, renderRoute }: NavBarProps) {\n const items = aggregateContributions(contributions, 'Navigation')\n .map(extractNavigationItem)\n .filter((item): item is NavigationItem => item !== undefined);\n\n const hasGroups = items.some(item => item.group !== undefined);\n if (!hasGroups) {\n return (\n <nav data-scene-navbar=\"\">\n {items.map(item => <NavBarItem key={item.targetScreen} item={item} href={renderRoute(item)} />)}\n </nav>\n );\n }\n\n const groupNames = [...new Set(items.map(item => item.group ?? ''))];\n return (\n <nav data-scene-navbar=\"\">\n {groupNames.map(groupName => (\n <section key={groupName} data-scene-navbar-group={groupName}>\n {items.filter(item => (item.group ?? '') === groupName).map(item => <NavBarItem key={item.targetScreen} item={item} href={renderRoute(item)} />)}\n </section>\n ))}\n </nav>\n );\n}\n\nfunction NavBarItem({ item, href }: { item: NavigationItem; href: string }) {\n return <a href={href}>{item.label}</a>;\n}\n"],"names":["NavBar","contributions","renderRoute","items","aggregateContributions","map","extractNavigationItem","filter","item","undefined","hasGroups","some","group","_jsx","nav","data-scene-navbar","NavBarItem","href","targetScreen","groupNames","Set","groupName","section","data-scene-navbar-group","a","label"],"mappings":";;;;;;AAAA;AACA;AAkBA;;;;AAIC,IACM,SAASA,MAAAA,CAAO,EAAEC,aAAa,EAAEC,WAAW,EAAe,EAAA;IAC9D,MAAMC,KAAAA,GAAQC,mCAAAA,CAAuBH,aAAAA,EAAe,YAAA,CAAA,CAC/CI,GAAG,CAACC,2CAAAA,CAAAA,CACJC,MAAM,CAAC,CAACC,IAAAA,GAAiCA,IAAAA,KAASC,SAAAA,CAAAA;IAEvD,MAAMC,SAAAA,GAAYP,MAAMQ,IAAI,CAACH,CAAAA,IAAAA,GAAQA,IAAAA,CAAKI,KAAK,KAAKH,SAAAA,CAAAA;AACpD,IAAA,IAAI,CAACC,SAAAA,EAAW;AACZ,QAAA,qBACIG,cAAA,CAACC,KAAAA,EAAAA;YAAIC,mBAAAA,EAAkB,EAAA;AAClBZ,YAAAA,QAAAA,EAAAA,KAAAA,CAAME,GAAG,CAACG,CAAAA,IAAAA,iBAAQK,cAAA,CAACG,UAAAA,EAAAA;oBAAmCR,IAAAA,EAAMA,IAAAA;AAAMS,oBAAAA,IAAAA,EAAMf,WAAAA,CAAYM,IAAAA;AAAjDA,iBAAAA,EAAAA,IAAAA,CAAKU,YAAY,CAAA;;AAGjE,IAAA;AAEA,IAAA,MAAMC,UAAAA,GAAa;WAAI,IAAIC,GAAAA,CAAIjB,MAAME,GAAG,CAACG,CAAAA,IAAAA,GAAQA,IAAAA,CAAKI,KAAK,IAAI,EAAA,CAAA;AAAK,KAAA;AACpE,IAAA,qBACIC,cAAA,CAACC,KAAAA,EAAAA;QAAIC,mBAAAA,EAAkB,EAAA;AAClBI,QAAAA,QAAAA,EAAAA,UAAAA,CAAWd,GAAG,CAACgB,CAAAA,SAAAA,iBACZR,cAAA,CAACS,SAAAA,EAAAA;gBAAwBC,yBAAAA,EAAyBF,SAAAA;AAC7ClB,gBAAAA,QAAAA,EAAAA,KAAAA,CAAMI,MAAM,CAACC,CAAAA,IAAAA,GAASA,CAAAA,IAAAA,CAAKI,KAAK,IAAI,EAAC,MAAOS,SAAAA,CAAAA,CAAWhB,GAAG,CAACG,CAAAA,qBAAQK,cAAA,CAACG,UAAAA,EAAAA;wBAAmCR,IAAAA,EAAMA,IAAAA;AAAMS,wBAAAA,IAAAA,EAAMf,WAAAA,CAAYM,IAAAA;AAAjDA,qBAAAA,EAAAA,IAAAA,CAAKU,YAAY,CAAA;AAD5FG,aAAAA,EAAAA,SAAAA,CAAAA;;AAM9B;AAEA,SAASL,UAAAA,CAAW,EAAER,IAAI,EAAES,IAAI,EAA0C,EAAA;AACtE,IAAA,qBAAOJ,cAAA,CAACW,GAAAA,EAAAA;QAAEP,IAAAA,EAAMA,IAAAA;AAAOT,QAAAA,QAAAA,EAAAA,IAAAA,CAAKiB;;AAChC;;;;"}
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ // Copyright (c) Cratis. All rights reserved.
4
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5
+ /**
6
+ * Reads a {@link NavigationItem} out of a {@link Contribution} to the built-in `Navigation` contribution
7
+ * point. `Contribution.content` carries an {@link ExternalComponent} whose open `properties` bag holds the
8
+ * navigation-specific values (`label`, `targetScreen`, `routeParameterBindings`, `order`, `group`) - the
9
+ * same names as {@link NavigationItem}'s own fields. This property-bag contract is Scene#2's own choice,
10
+ * made without a real Screenplay-to-Scene translation to confirm it against (Stage#37 hasn't been built
11
+ * yet) - whoever builds that seam should either produce contributions matching this contract or this
12
+ * function should move to match whatever Stage#37 actually emits.
13
+ *
14
+ * @param contribution The contribution to read.
15
+ * @returns The extracted {@link NavigationItem}, or `undefined` when `contribution.content` isn't an {@link ExternalComponent} or is missing a required property.
16
+ */ function extractNavigationItem(contribution) {
17
+ const content = contribution.content;
18
+ if (!content.properties) {
19
+ return undefined;
20
+ }
21
+ const { label, targetScreen, routeParameterBindings, order, group } = content.properties;
22
+ if (typeof label !== 'string' || typeof targetScreen !== 'string') {
23
+ return undefined;
24
+ }
25
+ return {
26
+ label,
27
+ targetScreen,
28
+ routeParameterBindings: isBindingExpressionRecord(routeParameterBindings) ? routeParameterBindings : {},
29
+ order: typeof order === 'number' ? order : contribution.order,
30
+ group: typeof group === 'string' ? group : undefined
31
+ };
32
+ }
33
+ function isBindingExpressionRecord(value) {
34
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
35
+ }
36
+
37
+ exports.extractNavigationItem = extractNavigationItem;
38
+ //# sourceMappingURL=extractNavigationItem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractNavigationItem.js","sources":["../../../NavBar/extractNavigationItem.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { BindingExpression, Contribution, ExternalComponent, NavigationItem } from '@cratis/scene.model';\n\n/**\n * Reads a {@link NavigationItem} out of a {@link Contribution} to the built-in `Navigation` contribution\n * point. `Contribution.content` carries an {@link ExternalComponent} whose open `properties` bag holds the\n * navigation-specific values (`label`, `targetScreen`, `routeParameterBindings`, `order`, `group`) - the\n * same names as {@link NavigationItem}'s own fields. This property-bag contract is Scene#2's own choice,\n * made without a real Screenplay-to-Scene translation to confirm it against (Stage#37 hasn't been built\n * yet) - whoever builds that seam should either produce contributions matching this contract or this\n * function should move to match whatever Stage#37 actually emits.\n *\n * @param contribution The contribution to read.\n * @returns The extracted {@link NavigationItem}, or `undefined` when `contribution.content` isn't an {@link ExternalComponent} or is missing a required property.\n */\nexport function extractNavigationItem(contribution: Contribution): NavigationItem | undefined {\n const content = contribution.content as Partial<ExternalComponent>;\n if (!content.properties) {\n return undefined;\n }\n\n const { label, targetScreen, routeParameterBindings, order, group } = content.properties as Record<string, unknown>;\n if (typeof label !== 'string' || typeof targetScreen !== 'string') {\n return undefined;\n }\n\n return {\n label,\n targetScreen,\n routeParameterBindings: isBindingExpressionRecord(routeParameterBindings) ? routeParameterBindings : {},\n order: typeof order === 'number' ? order : contribution.order,\n group: typeof group === 'string' ? group : undefined,\n };\n}\n\nfunction isBindingExpressionRecord(value: unknown): value is Record<string, BindingExpression> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"],"names":["extractNavigationItem","contribution","content","properties","undefined","label","targetScreen","routeParameterBindings","order","group","isBindingExpressionRecord","value","Array","isArray"],"mappings":";;AAAA;AACA;AAIA;;;;;;;;;;;IAYO,SAASA,qBAAAA,CAAsBC,YAA0B,EAAA;IAC5D,MAAMC,OAAAA,GAAUD,aAAaC,OAAO;IACpC,IAAI,CAACA,OAAAA,CAAQC,UAAU,EAAE;QACrB,OAAOC,SAAAA;AACX,IAAA;AAEA,IAAA,MAAM,EAAEC,KAAK,EAAEC,YAAY,EAAEC,sBAAsB,EAAEC,KAAK,EAAEC,KAAK,EAAE,GAAGP,QAAQC,UAAU;AACxF,IAAA,IAAI,OAAOE,KAAAA,KAAU,QAAA,IAAY,OAAOC,iBAAiB,QAAA,EAAU;QAC/D,OAAOF,SAAAA;AACX,IAAA;IAEA,OAAO;AACHC,QAAAA,KAAAA;AACAC,QAAAA,YAAAA;QACAC,sBAAAA,EAAwBG,yBAAAA,CAA0BH,sBAAAA,CAAAA,GAA0BA,sBAAAA,GAAyB,EAAC;AACtGC,QAAAA,KAAAA,EAAO,OAAOA,KAAAA,KAAU,QAAA,GAAWA,KAAAA,GAAQP,aAAaO,KAAK;QAC7DC,KAAAA,EAAO,OAAOA,KAAAA,KAAU,QAAA,GAAWA,KAAAA,GAAQL;AAC/C,KAAA;AACJ;AAEA,SAASM,0BAA0BC,KAAc,EAAA;IAC7C,OAAO,OAAOA,UAAU,QAAA,IAAYA,KAAAA,KAAU,QAAQ,CAACC,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA;AACzE;;;;"}
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var react = require('react');
5
+ var scene_engine = require('@cratis/scene.engine');
6
+ var createReactRenderer = require('./renderer/createReactRenderer.js');
7
+
8
+ /**
9
+ * Renders a Scene element tree with the real `Scene.React` renderer - the WYSIWYG building block Studio's
10
+ * preview surface and Stage's shipped web bundle both consume unmodified.
11
+ */ function SceneElementView({ element, registry, resolveBinding }) {
12
+ const renderer = react.useMemo(()=>createReactRenderer.createReactRenderer(registry), [
13
+ registry
14
+ ]);
15
+ return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
16
+ children: scene_engine.renderElement(element, renderer, resolveBinding)
17
+ });
18
+ }
19
+
20
+ exports.SceneElementView = SceneElementView;
21
+ //# sourceMappingURL=SceneElementView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SceneElementView.js","sources":["../../SceneElementView.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useMemo } from 'react';\nimport { renderElement, BindingResolver } from '@cratis/scene.engine';\nimport { SceneElement } from '@cratis/scene.model';\nimport { ComponentRegistry, createReactRenderer } from './renderer';\n\nexport interface SceneElementViewProps {\n element: SceneElement;\n registry: ComponentRegistry;\n resolveBinding: BindingResolver;\n}\n\n/**\n * Renders a Scene element tree with the real `Scene.React` renderer - the WYSIWYG building block Studio's\n * preview surface and Stage's shipped web bundle both consume unmodified.\n */\nexport function SceneElementView({ element, registry, resolveBinding }: SceneElementViewProps) {\n const renderer = useMemo(() => createReactRenderer(registry), [registry]);\n return <>{renderElement(element, renderer, resolveBinding)}</>;\n}\n"],"names":["SceneElementView","element","registry","resolveBinding","renderer","useMemo","createReactRenderer","_jsx","_Fragment","renderElement"],"mappings":";;;;;;;AAcA;;;IAIO,SAASA,gBAAAA,CAAiB,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAyB,EAAA;AACzF,IAAA,MAAMC,QAAAA,GAAWC,aAAAA,CAAQ,IAAMC,uCAAAA,CAAoBJ,QAAAA,CAAAA,EAAW;AAACA,QAAAA;AAAS,KAAA,CAAA;IACxE,qBAAOK,cAAA,CAAAC,mBAAA,EAAA;AAAGC,QAAAA,QAAAA,EAAAA,0BAAAA,CAAcR,SAASG,QAAAA,EAAUD,cAAAA;;AAC/C;;;;"}