@ankhorage/zora-game 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 (70) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +19 -0
  4. package/dist/ZORA_GAME_COMPONENT_META.d.ts +224 -0
  5. package/dist/ZORA_GAME_COMPONENT_META.d.ts.map +1 -0
  6. package/dist/ZORA_GAME_COMPONENT_META.js +10 -0
  7. package/dist/ZORA_GAME_COMPONENT_META.js.map +1 -0
  8. package/dist/ZORA_GAME_PLUGIN.d.ts +247 -0
  9. package/dist/ZORA_GAME_PLUGIN.d.ts.map +1 -0
  10. package/dist/ZORA_GAME_PLUGIN.js +14 -0
  11. package/dist/ZORA_GAME_PLUGIN.js.map +1 -0
  12. package/dist/ZORA_PLUGIN_METADATA.d.ts +239 -0
  13. package/dist/ZORA_PLUGIN_METADATA.d.ts.map +1 -0
  14. package/dist/ZORA_PLUGIN_METADATA.js +23 -0
  15. package/dist/ZORA_PLUGIN_METADATA.js.map +1 -0
  16. package/dist/features/game-presentation/adapters/inbound/GameEntity.d.ts +4 -0
  17. package/dist/features/game-presentation/adapters/inbound/GameEntity.d.ts.map +1 -0
  18. package/dist/features/game-presentation/adapters/inbound/GameEntity.js +39 -0
  19. package/dist/features/game-presentation/adapters/inbound/GameEntity.js.map +1 -0
  20. package/dist/features/game-presentation/adapters/inbound/GameField.d.ts +4 -0
  21. package/dist/features/game-presentation/adapters/inbound/GameField.d.ts.map +1 -0
  22. package/dist/features/game-presentation/adapters/inbound/GameField.js +28 -0
  23. package/dist/features/game-presentation/adapters/inbound/GameField.js.map +1 -0
  24. package/dist/features/game-presentation/adapters/inbound/GameOverlay.d.ts +4 -0
  25. package/dist/features/game-presentation/adapters/inbound/GameOverlay.d.ts.map +1 -0
  26. package/dist/features/game-presentation/adapters/inbound/GameOverlay.js +39 -0
  27. package/dist/features/game-presentation/adapters/inbound/GameOverlay.js.map +1 -0
  28. package/dist/features/game-presentation/meta/gameEntityMeta.d.ts +119 -0
  29. package/dist/features/game-presentation/meta/gameEntityMeta.d.ts.map +1 -0
  30. package/dist/features/game-presentation/meta/gameEntityMeta.js +90 -0
  31. package/dist/features/game-presentation/meta/gameEntityMeta.js.map +1 -0
  32. package/dist/features/game-presentation/meta/gameFieldMeta.d.ts +52 -0
  33. package/dist/features/game-presentation/meta/gameFieldMeta.d.ts.map +1 -0
  34. package/dist/features/game-presentation/meta/gameFieldMeta.js +41 -0
  35. package/dist/features/game-presentation/meta/gameFieldMeta.js.map +1 -0
  36. package/dist/features/game-presentation/meta/gameOverlayMeta.d.ts +55 -0
  37. package/dist/features/game-presentation/meta/gameOverlayMeta.d.ts.map +1 -0
  38. package/dist/features/game-presentation/meta/gameOverlayMeta.js +65 -0
  39. package/dist/features/game-presentation/meta/gameOverlayMeta.js.map +1 -0
  40. package/dist/features/game-presentation/public.d.ts +5 -0
  41. package/dist/features/game-presentation/public.d.ts.map +1 -0
  42. package/dist/features/game-presentation/public.js +4 -0
  43. package/dist/features/game-presentation/public.js.map +1 -0
  44. package/dist/index.d.ts +4 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +3 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/metadata.d.ts +3 -0
  49. package/dist/metadata.d.ts.map +1 -0
  50. package/dist/metadata.js +3 -0
  51. package/dist/metadata.js.map +1 -0
  52. package/dist/types/gamePresentation.d.ts +37 -0
  53. package/dist/types/gamePresentation.d.ts.map +1 -0
  54. package/dist/types/gamePresentation.js +2 -0
  55. package/dist/types/gamePresentation.js.map +1 -0
  56. package/package.json +95 -0
  57. package/src/ZORA_GAME_COMPONENT_META.ts +10 -0
  58. package/src/ZORA_GAME_PLUGIN.ts +14 -0
  59. package/src/ZORA_PLUGIN_METADATA.test.ts +24 -0
  60. package/src/ZORA_PLUGIN_METADATA.ts +25 -0
  61. package/src/features/game-presentation/adapters/inbound/GameEntity.tsx +88 -0
  62. package/src/features/game-presentation/adapters/inbound/GameField.tsx +44 -0
  63. package/src/features/game-presentation/adapters/inbound/GameOverlay.tsx +57 -0
  64. package/src/features/game-presentation/meta/gameEntityMeta.ts +91 -0
  65. package/src/features/game-presentation/meta/gameFieldMeta.ts +42 -0
  66. package/src/features/game-presentation/meta/gameOverlayMeta.ts +66 -0
  67. package/src/features/game-presentation/public.ts +10 -0
  68. package/src/index.ts +9 -0
  69. package/src/metadata.ts +2 -0
  70. package/src/types/gamePresentation.ts +41 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gameEntityMeta.js","sourceRoot":"","sources":["../../../../src/features/game-presentation/meta/gameEntityMeta.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,YAAY;IAClB,QAAQ,EAAE,WAAW;IACrB,WAAW,EAAE,kFAAkF;IAC/F,kBAAkB,EAAE,IAAI;IACxB,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC;IAC3D,SAAS,EAAE;QACT,KAAK,EAAE,aAAa;QACpB,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;KAC3E;IACD,KAAK,EAAE;QACL,CAAC,EAAE;YACD,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,CAAC,EAAE;YACD,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,QAAQ;YACf,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,YAAY;YACtB,KAAK,EAAE,SAAS;YAChB,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,WAAW;YACrB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,WAAW;YACrB,KAAK,EAAE,UAAU;YACjB,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,SAAS;YAChB,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,MAAM,EAAE;YACN,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,aAAa,EAAE;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,aAAa;YACvB,KAAK,EAAE,gBAAgB;YACvB,IAAI,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC;YAC9C,OAAO,EAAE,MAAM;YACf,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,kBAAkB,EAAE;YAClB,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,qBAAqB;YAC5B,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;KACF;CACmC,CAAC","sourcesContent":["import type { ZoraComponentMeta } from '@ankhorage/zora/metadata';\n\n/*** Describe one positioned generic game entity for ZORA authoring tools. */\nexport const gameEntityMeta = {\n name: 'GameEntity',\n category: 'component',\n description: 'Positions arbitrary visual content inside a GameField without owning game rules.',\n directManifestNode: true,\n allowedChildren: ['Badge', 'Icon', 'Image', 'Text', 'View'],\n blueprint: {\n label: 'Game entity',\n defaultProps: { x: 0, y: 0, opacity: 1, scale: 1, rotation: 0, zIndex: 0 },\n },\n props: {\n x: {\n type: 'number',\n category: 'Position',\n label: 'X (%)',\n default: 0,\n authoring: { authority: 'instance' },\n },\n y: {\n type: 'number',\n category: 'Position',\n label: 'Y (%)',\n default: 0,\n authoring: { authority: 'instance' },\n },\n width: {\n type: 'number',\n category: 'Size',\n label: 'Width',\n authoring: { authority: 'instance' },\n },\n height: {\n type: 'number',\n category: 'Size',\n label: 'Height',\n authoring: { authority: 'instance' },\n },\n opacity: {\n type: 'number',\n category: 'Appearance',\n label: 'Opacity',\n default: 1,\n authoring: { authority: 'instance' },\n },\n scale: {\n type: 'number',\n category: 'Transform',\n label: 'Scale',\n default: 1,\n authoring: { authority: 'instance' },\n },\n rotation: {\n type: 'number',\n category: 'Transform',\n label: 'Rotation',\n default: 0,\n authoring: { authority: 'instance' },\n },\n zIndex: {\n type: 'number',\n category: 'Position',\n label: 'Z index',\n default: 0,\n authoring: { authority: 'instance' },\n },\n hidden: {\n type: 'boolean',\n category: 'State',\n label: 'Hidden',\n default: false,\n authoring: { authority: 'instance' },\n },\n pointerEvents: {\n type: 'enum',\n category: 'Interaction',\n label: 'Pointer events',\n enum: ['auto', 'box-none', 'box-only', 'none'],\n default: 'auto',\n authoring: { authority: 'instance' },\n },\n accessibilityLabel: {\n type: 'string',\n category: 'Accessibility',\n label: 'Accessibility label',\n authoring: { authority: 'instance' },\n },\n },\n} as const satisfies ZoraComponentMeta;\n"]}
@@ -0,0 +1,52 @@
1
+ /*** Describe the embeddable game field for ZORA authoring tools. */
2
+ export declare const gameFieldMeta: {
3
+ readonly name: "GameField";
4
+ readonly category: "layout";
5
+ readonly description: "Relative positioning surface for embeddable game presentation content.";
6
+ readonly directManifestNode: true;
7
+ readonly allowedChildren: readonly ["GameEntity", "GameOverlay", "Gradient", "Image", "View"];
8
+ readonly blueprint: {
9
+ readonly label: "Game field";
10
+ readonly defaultProps: {
11
+ readonly minHeight: 320;
12
+ readonly clip: true;
13
+ };
14
+ };
15
+ readonly props: {
16
+ readonly aspectRatio: {
17
+ readonly type: "number";
18
+ readonly category: "Layout";
19
+ readonly label: "Aspect ratio";
20
+ readonly authoring: {
21
+ readonly authority: "instance";
22
+ };
23
+ };
24
+ readonly minHeight: {
25
+ readonly type: "number";
26
+ readonly category: "Layout";
27
+ readonly label: "Minimum height";
28
+ readonly default: 320;
29
+ readonly authoring: {
30
+ readonly authority: "instance";
31
+ };
32
+ };
33
+ readonly clip: {
34
+ readonly type: "boolean";
35
+ readonly category: "Layout";
36
+ readonly label: "Clip overflow";
37
+ readonly default: true;
38
+ readonly authoring: {
39
+ readonly authority: "instance";
40
+ };
41
+ };
42
+ readonly accessibilityLabel: {
43
+ readonly type: "string";
44
+ readonly category: "Accessibility";
45
+ readonly label: "Accessibility label";
46
+ readonly authoring: {
47
+ readonly authority: "instance";
48
+ };
49
+ };
50
+ };
51
+ };
52
+ //# sourceMappingURL=gameFieldMeta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gameFieldMeta.d.ts","sourceRoot":"","sources":["../../../../src/features/game-presentation/meta/gameFieldMeta.ts"],"names":[],"mappings":"AAEA,oEAAoE;AACpE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCY,CAAC"}
@@ -0,0 +1,41 @@
1
+ /*** Describe the embeddable game field for ZORA authoring tools. */
2
+ export const gameFieldMeta = {
3
+ name: 'GameField',
4
+ category: 'layout',
5
+ description: 'Relative positioning surface for embeddable game presentation content.',
6
+ directManifestNode: true,
7
+ allowedChildren: ['GameEntity', 'GameOverlay', 'Gradient', 'Image', 'View'],
8
+ blueprint: {
9
+ label: 'Game field',
10
+ defaultProps: { minHeight: 320, clip: true },
11
+ },
12
+ props: {
13
+ aspectRatio: {
14
+ type: 'number',
15
+ category: 'Layout',
16
+ label: 'Aspect ratio',
17
+ authoring: { authority: 'instance' },
18
+ },
19
+ minHeight: {
20
+ type: 'number',
21
+ category: 'Layout',
22
+ label: 'Minimum height',
23
+ default: 320,
24
+ authoring: { authority: 'instance' },
25
+ },
26
+ clip: {
27
+ type: 'boolean',
28
+ category: 'Layout',
29
+ label: 'Clip overflow',
30
+ default: true,
31
+ authoring: { authority: 'instance' },
32
+ },
33
+ accessibilityLabel: {
34
+ type: 'string',
35
+ category: 'Accessibility',
36
+ label: 'Accessibility label',
37
+ authoring: { authority: 'instance' },
38
+ },
39
+ },
40
+ };
41
+ //# sourceMappingURL=gameFieldMeta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gameFieldMeta.js","sourceRoot":"","sources":["../../../../src/features/game-presentation/meta/gameFieldMeta.ts"],"names":[],"mappings":"AAEA,oEAAoE;AACpE,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,WAAW;IACjB,QAAQ,EAAE,QAAQ;IAClB,WAAW,EAAE,wEAAwE;IACrF,kBAAkB,EAAE,IAAI;IACxB,eAAe,EAAE,CAAC,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC;IAC3E,SAAS,EAAE;QACT,KAAK,EAAE,YAAY;QACnB,YAAY,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE;KAC7C;IACD,KAAK,EAAE;QACL,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,SAAS,EAAE;YACT,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,gBAAgB;YACvB,OAAO,EAAE,GAAG;YACZ,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,eAAe;YACtB,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,kBAAkB,EAAE;YAClB,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,qBAAqB;YAC5B,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;KACF;CACmC,CAAC","sourcesContent":["import type { ZoraComponentMeta } from '@ankhorage/zora/metadata';\n\n/*** Describe the embeddable game field for ZORA authoring tools. */\nexport const gameFieldMeta = {\n name: 'GameField',\n category: 'layout',\n description: 'Relative positioning surface for embeddable game presentation content.',\n directManifestNode: true,\n allowedChildren: ['GameEntity', 'GameOverlay', 'Gradient', 'Image', 'View'],\n blueprint: {\n label: 'Game field',\n defaultProps: { minHeight: 320, clip: true },\n },\n props: {\n aspectRatio: {\n type: 'number',\n category: 'Layout',\n label: 'Aspect ratio',\n authoring: { authority: 'instance' },\n },\n minHeight: {\n type: 'number',\n category: 'Layout',\n label: 'Minimum height',\n default: 320,\n authoring: { authority: 'instance' },\n },\n clip: {\n type: 'boolean',\n category: 'Layout',\n label: 'Clip overflow',\n default: true,\n authoring: { authority: 'instance' },\n },\n accessibilityLabel: {\n type: 'string',\n category: 'Accessibility',\n label: 'Accessibility label',\n authoring: { authority: 'instance' },\n },\n },\n} as const satisfies ZoraComponentMeta;\n"]}
@@ -0,0 +1,55 @@
1
+ /*** Describe a generic absolute game layer for HUD, feedback, and phase content. */
2
+ export declare const gameOverlayMeta: {
3
+ readonly name: "GameOverlay";
4
+ readonly category: "layout";
5
+ readonly description: "Absolute presentation layer for HUD, feedback, controls, and phase content.";
6
+ readonly directManifestNode: true;
7
+ readonly allowedChildren: readonly ["Badge", "Button", "ButtonGroup", "Card", "GameEntity", "Heading", "Icon", "Image", "Progress", "ProgressRing", "Text", "View"];
8
+ readonly blueprint: {
9
+ readonly label: "Game overlay";
10
+ readonly defaultProps: {
11
+ readonly placement: "fill";
12
+ readonly blocking: false;
13
+ readonly padding: 0;
14
+ };
15
+ };
16
+ readonly props: {
17
+ readonly placement: {
18
+ readonly type: "enum";
19
+ readonly category: "Layout";
20
+ readonly label: "Placement";
21
+ readonly enum: readonly ["fill", "center", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right"];
22
+ readonly default: "fill";
23
+ readonly authoring: {
24
+ readonly authority: "instance";
25
+ };
26
+ };
27
+ readonly blocking: {
28
+ readonly type: "boolean";
29
+ readonly category: "Interaction";
30
+ readonly label: "Block pointer input";
31
+ readonly default: false;
32
+ readonly authoring: {
33
+ readonly authority: "instance";
34
+ };
35
+ };
36
+ readonly padding: {
37
+ readonly type: "number";
38
+ readonly category: "Layout";
39
+ readonly label: "Padding";
40
+ readonly default: 0;
41
+ readonly authoring: {
42
+ readonly authority: "instance";
43
+ };
44
+ };
45
+ readonly accessibilityLabel: {
46
+ readonly type: "string";
47
+ readonly category: "Accessibility";
48
+ readonly label: "Accessibility label";
49
+ readonly authoring: {
50
+ readonly authority: "instance";
51
+ };
52
+ };
53
+ };
54
+ };
55
+ //# sourceMappingURL=gameOverlayMeta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gameOverlayMeta.d.ts","sourceRoot":"","sources":["../../../../src/features/game-presentation/meta/gameOverlayMeta.ts"],"names":[],"mappings":"AAEA,oFAAoF;AACpF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8DU,CAAC"}
@@ -0,0 +1,65 @@
1
+ /*** Describe a generic absolute game layer for HUD, feedback, and phase content. */
2
+ export const gameOverlayMeta = {
3
+ name: 'GameOverlay',
4
+ category: 'layout',
5
+ description: 'Absolute presentation layer for HUD, feedback, controls, and phase content.',
6
+ directManifestNode: true,
7
+ allowedChildren: [
8
+ 'Badge',
9
+ 'Button',
10
+ 'ButtonGroup',
11
+ 'Card',
12
+ 'GameEntity',
13
+ 'Heading',
14
+ 'Icon',
15
+ 'Image',
16
+ 'Progress',
17
+ 'ProgressRing',
18
+ 'Text',
19
+ 'View',
20
+ ],
21
+ blueprint: {
22
+ label: 'Game overlay',
23
+ defaultProps: { placement: 'fill', blocking: false, padding: 0 },
24
+ },
25
+ props: {
26
+ placement: {
27
+ type: 'enum',
28
+ category: 'Layout',
29
+ label: 'Placement',
30
+ enum: [
31
+ 'fill',
32
+ 'center',
33
+ 'top',
34
+ 'bottom',
35
+ 'top-left',
36
+ 'top-right',
37
+ 'bottom-left',
38
+ 'bottom-right',
39
+ ],
40
+ default: 'fill',
41
+ authoring: { authority: 'instance' },
42
+ },
43
+ blocking: {
44
+ type: 'boolean',
45
+ category: 'Interaction',
46
+ label: 'Block pointer input',
47
+ default: false,
48
+ authoring: { authority: 'instance' },
49
+ },
50
+ padding: {
51
+ type: 'number',
52
+ category: 'Layout',
53
+ label: 'Padding',
54
+ default: 0,
55
+ authoring: { authority: 'instance' },
56
+ },
57
+ accessibilityLabel: {
58
+ type: 'string',
59
+ category: 'Accessibility',
60
+ label: 'Accessibility label',
61
+ authoring: { authority: 'instance' },
62
+ },
63
+ },
64
+ };
65
+ //# sourceMappingURL=gameOverlayMeta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gameOverlayMeta.js","sourceRoot":"","sources":["../../../../src/features/game-presentation/meta/gameOverlayMeta.ts"],"names":[],"mappings":"AAEA,oFAAoF;AACpF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,aAAa;IACnB,QAAQ,EAAE,QAAQ;IAClB,WAAW,EAAE,6EAA6E;IAC1F,kBAAkB,EAAE,IAAI;IACxB,eAAe,EAAE;QACf,OAAO;QACP,QAAQ;QACR,aAAa;QACb,MAAM;QACN,YAAY;QACZ,SAAS;QACT,MAAM;QACN,OAAO;QACP,UAAU;QACV,cAAc;QACd,MAAM;QACN,MAAM;KACP;IACD,SAAS,EAAE;QACT,KAAK,EAAE,cAAc;QACrB,YAAY,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE;KACjE;IACD,KAAK,EAAE;QACL,SAAS,EAAE;YACT,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,WAAW;YAClB,IAAI,EAAE;gBACJ,MAAM;gBACN,QAAQ;gBACR,KAAK;gBACL,QAAQ;gBACR,UAAU;gBACV,WAAW;gBACX,aAAa;gBACb,cAAc;aACf;YACD,OAAO,EAAE,MAAM;YACf,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,aAAa;YACvB,KAAK,EAAE,qBAAqB;YAC5B,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,SAAS;YAChB,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;QACD,kBAAkB,EAAE;YAClB,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,qBAAqB;YAC5B,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SACrC;KACF;CACmC,CAAC","sourcesContent":["import type { ZoraComponentMeta } from '@ankhorage/zora/metadata';\n\n/*** Describe a generic absolute game layer for HUD, feedback, and phase content. */\nexport const gameOverlayMeta = {\n name: 'GameOverlay',\n category: 'layout',\n description: 'Absolute presentation layer for HUD, feedback, controls, and phase content.',\n directManifestNode: true,\n allowedChildren: [\n 'Badge',\n 'Button',\n 'ButtonGroup',\n 'Card',\n 'GameEntity',\n 'Heading',\n 'Icon',\n 'Image',\n 'Progress',\n 'ProgressRing',\n 'Text',\n 'View',\n ],\n blueprint: {\n label: 'Game overlay',\n defaultProps: { placement: 'fill', blocking: false, padding: 0 },\n },\n props: {\n placement: {\n type: 'enum',\n category: 'Layout',\n label: 'Placement',\n enum: [\n 'fill',\n 'center',\n 'top',\n 'bottom',\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n ],\n default: 'fill',\n authoring: { authority: 'instance' },\n },\n blocking: {\n type: 'boolean',\n category: 'Interaction',\n label: 'Block pointer input',\n default: false,\n authoring: { authority: 'instance' },\n },\n padding: {\n type: 'number',\n category: 'Layout',\n label: 'Padding',\n default: 0,\n authoring: { authority: 'instance' },\n },\n accessibilityLabel: {\n type: 'string',\n category: 'Accessibility',\n label: 'Accessibility label',\n authoring: { authority: 'instance' },\n },\n },\n} as const satisfies ZoraComponentMeta;\n"]}
@@ -0,0 +1,5 @@
1
+ export type { GameEntityProps, GameFieldProps, GameOverlayPlacement, GameOverlayProps, GamePointerEvents, } from '../../types/gamePresentation';
2
+ export { GameEntity } from './adapters/inbound/GameEntity';
3
+ export { GameField } from './adapters/inbound/GameField';
4
+ export { GameOverlay } from './adapters/inbound/GameOverlay';
5
+ //# sourceMappingURL=public.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/features/game-presentation/public.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { GameEntity } from './adapters/inbound/GameEntity';
2
+ export { GameField } from './adapters/inbound/GameField';
3
+ export { GameOverlay } from './adapters/inbound/GameOverlay';
4
+ //# sourceMappingURL=public.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.js","sourceRoot":"","sources":["../../../src/features/game-presentation/public.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC","sourcesContent":["export type {\n GameEntityProps,\n GameFieldProps,\n GameOverlayPlacement,\n GameOverlayProps,\n GamePointerEvents,\n} from '../../types/gamePresentation';\nexport { GameEntity } from './adapters/inbound/GameEntity';\nexport { GameField } from './adapters/inbound/GameField';\nexport { GameOverlay } from './adapters/inbound/GameOverlay';\n"]}
@@ -0,0 +1,4 @@
1
+ export type { GameEntityProps, GameFieldProps, GameOverlayPlacement, GameOverlayProps, GamePointerEvents, } from './features/game-presentation/public';
2
+ export { GameEntity, GameField, GameOverlay } from './features/game-presentation/public';
3
+ export { ZORA_GAME_PLUGIN } from './ZORA_GAME_PLUGIN';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { GameEntity, GameField, GameOverlay } from './features/game-presentation/public';
2
+ export { ZORA_GAME_PLUGIN } from './ZORA_GAME_PLUGIN';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export type {\n GameEntityProps,\n GameFieldProps,\n GameOverlayPlacement,\n GameOverlayProps,\n GamePointerEvents,\n} from './features/game-presentation/public';\nexport { GameEntity, GameField, GameOverlay } from './features/game-presentation/public';\nexport { ZORA_GAME_PLUGIN } from './ZORA_GAME_PLUGIN';\n"]}
@@ -0,0 +1,3 @@
1
+ export { ZORA_GAME_COMPONENT_META } from './ZORA_GAME_COMPONENT_META';
2
+ export { ZORA_PLUGIN_METADATA } from './ZORA_PLUGIN_METADATA';
3
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { ZORA_GAME_COMPONENT_META } from './ZORA_GAME_COMPONENT_META';
2
+ export { ZORA_PLUGIN_METADATA } from './ZORA_PLUGIN_METADATA';
3
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC","sourcesContent":["export { ZORA_GAME_COMPONENT_META } from './ZORA_GAME_COMPONENT_META';\nexport { ZORA_PLUGIN_METADATA } from './ZORA_PLUGIN_METADATA';\n"]}
@@ -0,0 +1,37 @@
1
+ import type React from 'react';
2
+ type GameOverlayCornerPlacement = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
3
+ export type GamePointerEvents = 'auto' | 'box-none' | 'box-only' | 'none';
4
+ export type GameOverlayPlacement = 'fill' | 'center' | 'top' | 'bottom' | GameOverlayCornerPlacement;
5
+ export interface GameFieldProps {
6
+ readonly children?: React.ReactNode;
7
+ readonly aspectRatio?: number;
8
+ readonly minHeight?: number;
9
+ readonly clip?: boolean;
10
+ readonly accessibilityLabel?: string;
11
+ readonly testID?: string;
12
+ }
13
+ export interface GameEntityProps {
14
+ readonly children?: React.ReactNode;
15
+ readonly x?: number;
16
+ readonly y?: number;
17
+ readonly width?: number;
18
+ readonly height?: number;
19
+ readonly opacity?: number;
20
+ readonly scale?: number;
21
+ readonly rotation?: number;
22
+ readonly zIndex?: number;
23
+ readonly hidden?: boolean;
24
+ readonly pointerEvents?: GamePointerEvents;
25
+ readonly accessibilityLabel?: string;
26
+ readonly testID?: string;
27
+ }
28
+ export interface GameOverlayProps {
29
+ readonly children?: React.ReactNode;
30
+ readonly placement?: GameOverlayPlacement;
31
+ readonly blocking?: boolean;
32
+ readonly padding?: number;
33
+ readonly accessibilityLabel?: string;
34
+ readonly testID?: string;
35
+ }
36
+ export {};
37
+ //# sourceMappingURL=gamePresentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gamePresentation.d.ts","sourceRoot":"","sources":["../../src/types/gamePresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,KAAK,0BAA0B,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;AAE5F,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AAC1E,MAAM,MAAM,oBAAoB,GAC9B,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,0BAA0B,CAAC;AAEpE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAC3C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,oBAAoB,CAAC;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gamePresentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gamePresentation.js","sourceRoot":"","sources":["../../src/types/gamePresentation.ts"],"names":[],"mappings":"","sourcesContent":["import type React from 'react';\n\ntype GameOverlayCornerPlacement = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n\nexport type GamePointerEvents = 'auto' | 'box-none' | 'box-only' | 'none';\nexport type GameOverlayPlacement =\n 'fill' | 'center' | 'top' | 'bottom' | GameOverlayCornerPlacement;\n\nexport interface GameFieldProps {\n readonly children?: React.ReactNode;\n readonly aspectRatio?: number;\n readonly minHeight?: number;\n readonly clip?: boolean;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameEntityProps {\n readonly children?: React.ReactNode;\n readonly x?: number;\n readonly y?: number;\n readonly width?: number;\n readonly height?: number;\n readonly opacity?: number;\n readonly scale?: number;\n readonly rotation?: number;\n readonly zIndex?: number;\n readonly hidden?: boolean;\n readonly pointerEvents?: GamePointerEvents;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n\nexport interface GameOverlayProps {\n readonly children?: React.ReactNode;\n readonly placement?: GameOverlayPlacement;\n readonly blocking?: boolean;\n readonly padding?: number;\n readonly accessibilityLabel?: string;\n readonly testID?: string;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@ankhorage/zora-game",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Generic game presentation primitives for React Native and React Native Web apps built on ZORA.",
6
+ "homepage": "https://github.com/ankhorage/zora-game#readme",
7
+ "bugs": {
8
+ "url": "https://github.com/ankhorage/zora-game/issues"
9
+ },
10
+ "license": "MIT",
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "sideEffects": false,
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/ankhorage/zora-game.git"
17
+ },
18
+ "keywords": [
19
+ "ankhorage",
20
+ "expo",
21
+ "game",
22
+ "game-ui",
23
+ "react-native",
24
+ "react-native-web",
25
+ "ui-kit",
26
+ "zora"
27
+ ],
28
+ "files": [
29
+ "dist",
30
+ "src",
31
+ "package.json",
32
+ "README.md",
33
+ "CHANGELOG.md",
34
+ "LICENSE"
35
+ ],
36
+ "exports": {
37
+ ".": {
38
+ "react-native": "./src/index.ts",
39
+ "browser": "./src/index.ts",
40
+ "types": "./dist/index.d.ts",
41
+ "import": "./dist/index.js",
42
+ "default": "./dist/index.js"
43
+ },
44
+ "./metadata": {
45
+ "react-native": "./src/metadata.ts",
46
+ "browser": "./src/metadata.ts",
47
+ "types": "./dist/metadata.d.ts",
48
+ "import": "./dist/metadata.js",
49
+ "default": "./dist/metadata.js"
50
+ },
51
+ "./package.json": "./package.json"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
56
+ "engines": {
57
+ "node": ">=24"
58
+ },
59
+ "peerDependencies": {
60
+ "@ankhorage/zora": ">=19.0.1",
61
+ "react": "19.2.3",
62
+ "react-native": "0.86.x"
63
+ },
64
+ "scripts": {
65
+ "build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.build.json",
66
+ "check-types": "tsc --noEmit -p tsconfig.json",
67
+ "typecheck": "bun run check-types",
68
+ "test": "bun test src",
69
+ "docs": "paradox",
70
+ "changeset": "ankhorage-changeset",
71
+ "changeset:status": "ankhorage-changeset status --since=origin/main",
72
+ "prepack": "bun run build",
73
+ "lint": "ankhorage-eslint . --max-warnings=0",
74
+ "lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
75
+ "format": "ankhorage-prettier --write .",
76
+ "format:check": "ankhorage-prettier --check .",
77
+ "knip:check": "ankhorage-knip",
78
+ "version-packages": "ankhorage-changeset version"
79
+ },
80
+ "devDependencies": {
81
+ "@ankhorage/devtools": "^1.15.3",
82
+ "@ankhorage/paradox": "^0.1.26",
83
+ "@ankhorage/zora": "^19.0.1",
84
+ "@types/bun": "^1.4.1",
85
+ "@types/node": "^26.6.1",
86
+ "@types/react": "^19.2.18",
87
+ "@types/react-dom": "^19.2.4",
88
+ "react": "19.2.3",
89
+ "react-dom": "19.2.3",
90
+ "react-native": "0.86.3",
91
+ "react-native-web": "~0.21.0",
92
+ "typescript": "~6.0.3"
93
+ },
94
+ "packageManager": "bun@1.4.2"
95
+ }
@@ -0,0 +1,10 @@
1
+ import { gameEntityMeta } from './features/game-presentation/meta/gameEntityMeta';
2
+ import { gameFieldMeta } from './features/game-presentation/meta/gameFieldMeta';
3
+ import { gameOverlayMeta } from './features/game-presentation/meta/gameOverlayMeta';
4
+
5
+ /*** Register the generic game presentation metadata owned by this package. */
6
+ export const ZORA_GAME_COMPONENT_META = {
7
+ GameEntity: gameEntityMeta,
8
+ GameField: gameFieldMeta,
9
+ GameOverlay: gameOverlayMeta,
10
+ } as const;
@@ -0,0 +1,14 @@
1
+ import { GameEntity } from './features/game-presentation/adapters/inbound/GameEntity';
2
+ import { GameField } from './features/game-presentation/adapters/inbound/GameField';
3
+ import { GameOverlay } from './features/game-presentation/adapters/inbound/GameOverlay';
4
+ import { ZORA_PLUGIN_METADATA } from './ZORA_PLUGIN_METADATA';
5
+
6
+ /*** Expose the runtime component registry together with metadata for ZORA plugin consumers. */
7
+ export const ZORA_GAME_PLUGIN = {
8
+ ...ZORA_PLUGIN_METADATA,
9
+ componentRegistry: {
10
+ GameEntity,
11
+ GameField,
12
+ GameOverlay,
13
+ },
14
+ } as const;
@@ -0,0 +1,24 @@
1
+ import { composeZoraPluginMetadata, ZORA_CORE_PLUGIN_METADATA } from '@ankhorage/zora/metadata';
2
+ import { describe, expect, test } from 'bun:test';
3
+
4
+ import { ZORA_PLUGIN_METADATA } from './ZORA_PLUGIN_METADATA';
5
+
6
+ describe('ZORA game plugin metadata', () => {
7
+ test('makes GameField embeddable in ordinary ZORA containers', () => {
8
+ const catalog = composeZoraPluginMetadata([ZORA_CORE_PLUGIN_METADATA, ZORA_PLUGIN_METADATA]);
9
+
10
+ expect(catalog.componentMeta.Screen?.allowedChildren).toContain('GameField');
11
+ expect(catalog.componentMeta.View?.allowedChildren).toContain('GameField');
12
+ expect(catalog.componentMeta.GameField?.allowedChildren).toContain('GameEntity');
13
+ expect(catalog.componentMeta.GameField?.allowedChildren).toContain('GameOverlay');
14
+ });
15
+
16
+ test('keeps the generic presentation vocabulary free of product-specific concepts', () => {
17
+ const serialized = JSON.stringify(ZORA_PLUGIN_METADATA).toLowerCase();
18
+
19
+ expect(serialized).not.toContain('palabichos');
20
+ expect(serialized).not.toContain('vocabulary');
21
+ expect(serialized).not.toContain('wordcreature');
22
+ expect(serialized).not.toContain('letterprojectile');
23
+ });
24
+ });
@@ -0,0 +1,25 @@
1
+ import type { ZoraPluginMetadata } from '@ankhorage/zora/metadata';
2
+
3
+ import { ZORA_GAME_COMPONENT_META } from './ZORA_GAME_COMPONENT_META';
4
+
5
+ /*** Describe generic game presentation nodes and their valid ZORA extension hosts. */
6
+ export const ZORA_PLUGIN_METADATA = {
7
+ packageName: '@ankhorage/zora-game',
8
+ displayName: 'ZORA Game',
9
+ componentMeta: ZORA_GAME_COMPONENT_META,
10
+ extensionHosts: ['GameField', 'GameEntity', 'GameOverlay'],
11
+ placements: [
12
+ {
13
+ child: 'GameField',
14
+ parents: ['Card', 'Grid', 'Screen', 'ScreenSection', 'View'],
15
+ },
16
+ {
17
+ child: 'GameEntity',
18
+ parents: ['GameField', 'GameOverlay'],
19
+ },
20
+ {
21
+ child: 'GameOverlay',
22
+ parents: ['GameField'],
23
+ },
24
+ ],
25
+ } as const satisfies ZoraPluginMetadata;
@@ -0,0 +1,88 @@
1
+ import { View } from '@ankhorage/zora';
2
+ import { StyleSheet, type ViewStyle } from 'react-native';
3
+
4
+ import type { GameEntityProps } from '../../../../types/gamePresentation';
5
+
6
+ /*** Render one generic positioned game entity without owning gameplay semantics. */
7
+ export function GameEntity({
8
+ children,
9
+ x = 0,
10
+ y = 0,
11
+ width,
12
+ height,
13
+ opacity = 1,
14
+ scale = 1,
15
+ rotation = 0,
16
+ zIndex = 0,
17
+ hidden = false,
18
+ pointerEvents = 'auto',
19
+ accessibilityLabel,
20
+ testID,
21
+ }: GameEntityProps) {
22
+ return (
23
+ <View
24
+ {...(accessibilityLabel === undefined ? {} : { accessibilityLabel })}
25
+ {...(testID === undefined ? {} : { testID })}
26
+ pointerEvents={pointerEvents}
27
+ style={[
28
+ styles.root,
29
+ createEntityStyle({ x, y, width, height, opacity, scale, rotation, zIndex, hidden }),
30
+ ]}
31
+ >
32
+ {children}
33
+ </View>
34
+ );
35
+ }
36
+
37
+ interface GameEntityStyleInput {
38
+ readonly x: number;
39
+ readonly y: number;
40
+ readonly width: number | undefined;
41
+ readonly height: number | undefined;
42
+ readonly opacity: number;
43
+ readonly scale: number;
44
+ readonly rotation: number;
45
+ readonly zIndex: number;
46
+ readonly hidden: boolean;
47
+ }
48
+
49
+ /*** Convert serializable entity presentation props into a React Native view style. */
50
+ function createEntityStyle({
51
+ x,
52
+ y,
53
+ width,
54
+ height,
55
+ opacity,
56
+ scale,
57
+ rotation,
58
+ zIndex,
59
+ hidden,
60
+ }: GameEntityStyleInput): ViewStyle {
61
+ return {
62
+ display: hidden ? 'none' : 'flex',
63
+ left: toPercent(x),
64
+ opacity: clamp(opacity, 0, 1),
65
+ position: 'absolute',
66
+ top: toPercent(y),
67
+ transform: [{ scale }, { rotate: `${rotation}deg` }],
68
+ zIndex,
69
+ ...(height === undefined ? {} : { height }),
70
+ ...(width === undefined ? {} : { width }),
71
+ };
72
+ }
73
+
74
+ /*** Convert a bounded percentage number to React Native percentage syntax. */
75
+ function toPercent(value: number): `${number}%` {
76
+ return `${clamp(value, 0, 100)}%`;
77
+ }
78
+
79
+ /*** Clamp one numeric presentation value to an inclusive range. */
80
+ function clamp(value: number, minimum: number, maximum: number): number {
81
+ return Math.min(maximum, Math.max(minimum, value));
82
+ }
83
+
84
+ const styles = StyleSheet.create({
85
+ root: {
86
+ position: 'absolute',
87
+ },
88
+ });