@wp-typia/project-tools 0.11.1

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 (187) hide show
  1. package/README.md +32 -0
  2. package/dist/runtime/cli-add.d.ts +38 -0
  3. package/dist/runtime/cli-add.js +561 -0
  4. package/dist/runtime/cli-core.d.ts +25 -0
  5. package/dist/runtime/cli-core.js +25 -0
  6. package/dist/runtime/cli-doctor.d.ts +34 -0
  7. package/dist/runtime/cli-doctor.js +131 -0
  8. package/dist/runtime/cli-help.d.ts +9 -0
  9. package/dist/runtime/cli-help.js +37 -0
  10. package/dist/runtime/cli-prompt.d.ts +21 -0
  11. package/dist/runtime/cli-prompt.js +53 -0
  12. package/dist/runtime/cli-scaffold.d.ts +79 -0
  13. package/dist/runtime/cli-scaffold.js +206 -0
  14. package/dist/runtime/cli-templates.d.ts +30 -0
  15. package/dist/runtime/cli-templates.js +61 -0
  16. package/dist/runtime/index.d.ts +9 -0
  17. package/dist/runtime/index.js +7 -0
  18. package/dist/runtime/json-utils.d.ts +10 -0
  19. package/dist/runtime/json-utils.js +12 -0
  20. package/dist/runtime/local-dev-presets.d.ts +26 -0
  21. package/dist/runtime/local-dev-presets.js +132 -0
  22. package/dist/runtime/metadata-analysis.d.ts +11 -0
  23. package/dist/runtime/metadata-analysis.js +285 -0
  24. package/dist/runtime/metadata-model.d.ts +84 -0
  25. package/dist/runtime/metadata-model.js +59 -0
  26. package/dist/runtime/metadata-parser.d.ts +53 -0
  27. package/dist/runtime/metadata-parser.js +794 -0
  28. package/dist/runtime/metadata-php-render.d.ts +29 -0
  29. package/dist/runtime/metadata-php-render.js +549 -0
  30. package/dist/runtime/metadata-projection.d.ts +7 -0
  31. package/dist/runtime/metadata-projection.js +233 -0
  32. package/dist/runtime/migration-constants.d.ts +15 -0
  33. package/dist/runtime/migration-constants.js +16 -0
  34. package/dist/runtime/migration-diff.d.ts +2 -0
  35. package/dist/runtime/migration-diff.js +537 -0
  36. package/dist/runtime/migration-fixtures.d.ts +8 -0
  37. package/dist/runtime/migration-fixtures.js +94 -0
  38. package/dist/runtime/migration-fuzz-plan.d.ts +2 -0
  39. package/dist/runtime/migration-fuzz-plan.js +50 -0
  40. package/dist/runtime/migration-manifest.d.ts +19 -0
  41. package/dist/runtime/migration-manifest.js +129 -0
  42. package/dist/runtime/migration-project.d.ts +94 -0
  43. package/dist/runtime/migration-project.js +1101 -0
  44. package/dist/runtime/migration-render.d.ts +11 -0
  45. package/dist/runtime/migration-render.js +741 -0
  46. package/dist/runtime/migration-risk.d.ts +4 -0
  47. package/dist/runtime/migration-risk.js +52 -0
  48. package/dist/runtime/migration-types.d.ts +249 -0
  49. package/dist/runtime/migration-types.js +1 -0
  50. package/dist/runtime/migration-ui-capability.d.ts +17 -0
  51. package/dist/runtime/migration-ui-capability.js +190 -0
  52. package/dist/runtime/migration-utils.d.ts +69 -0
  53. package/dist/runtime/migration-utils.js +246 -0
  54. package/dist/runtime/migrations.d.ts +249 -0
  55. package/dist/runtime/migrations.js +1061 -0
  56. package/dist/runtime/object-utils.d.ts +12 -0
  57. package/dist/runtime/object-utils.js +14 -0
  58. package/dist/runtime/package-managers.d.ts +28 -0
  59. package/dist/runtime/package-managers.js +156 -0
  60. package/dist/runtime/package-versions.d.ts +10 -0
  61. package/dist/runtime/package-versions.js +68 -0
  62. package/dist/runtime/scaffold-onboarding.d.ts +32 -0
  63. package/dist/runtime/scaffold-onboarding.js +99 -0
  64. package/dist/runtime/scaffold.d.ts +146 -0
  65. package/dist/runtime/scaffold.js +612 -0
  66. package/dist/runtime/schema-core.d.ts +267 -0
  67. package/dist/runtime/schema-core.js +597 -0
  68. package/dist/runtime/starter-manifests.d.ts +25 -0
  69. package/dist/runtime/starter-manifests.js +383 -0
  70. package/dist/runtime/string-case.d.ts +36 -0
  71. package/dist/runtime/string-case.js +69 -0
  72. package/dist/runtime/template-builtins.d.ts +38 -0
  73. package/dist/runtime/template-builtins.js +72 -0
  74. package/dist/runtime/template-defaults.d.ts +75 -0
  75. package/dist/runtime/template-defaults.js +65 -0
  76. package/dist/runtime/template-registry.d.ts +36 -0
  77. package/dist/runtime/template-registry.js +94 -0
  78. package/dist/runtime/template-render.d.ts +24 -0
  79. package/dist/runtime/template-render.js +113 -0
  80. package/dist/runtime/template-source.d.ts +71 -0
  81. package/dist/runtime/template-source.js +821 -0
  82. package/dist/runtime/typia-tags.d.ts +1 -0
  83. package/dist/runtime/typia-tags.js +1 -0
  84. package/package.json +79 -0
  85. package/templates/_shared/base/languages/.gitkeep +1 -0
  86. package/templates/_shared/base/package.json.mustache +41 -0
  87. package/templates/_shared/base/scripts/sync-types-to-block-json.ts.mustache +118 -0
  88. package/templates/_shared/base/src/hooks.ts.mustache +19 -0
  89. package/templates/_shared/base/src/validator-toolkit.ts.mustache +31 -0
  90. package/templates/_shared/base/tsconfig.json.mustache +21 -0
  91. package/templates/_shared/base/webpack.config.js.mustache +99 -0
  92. package/templates/_shared/base/{{slugKebabCase}}.php.mustache +53 -0
  93. package/templates/_shared/compound/core/package.json.mustache +45 -0
  94. package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +559 -0
  95. package/templates/_shared/compound/core/scripts/block-config.ts.mustache +13 -0
  96. package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +53 -0
  97. package/templates/_shared/compound/core/webpack.config.js.mustache +141 -0
  98. package/templates/_shared/compound/core/{{slugKebabCase}}.php.mustache +51 -0
  99. package/templates/_shared/compound/persistence/package.json.mustache +50 -0
  100. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +59 -0
  101. package/templates/_shared/compound/persistence/scripts/sync-rest-contracts.ts.mustache +101 -0
  102. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-types.ts.mustache +21 -0
  103. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-validators.ts.mustache +32 -0
  104. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api.ts.mustache +68 -0
  105. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/block.json.mustache +52 -0
  106. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/data.ts.mustache +192 -0
  107. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +123 -0
  108. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +11 -0
  109. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/interactivity.ts.mustache +132 -0
  110. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/render.php.mustache +158 -0
  111. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/save.tsx.mustache +3 -0
  112. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/types.ts.mustache +56 -0
  113. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/validators.ts.mustache +32 -0
  114. package/templates/_shared/compound/persistence-auth/{{slugKebabCase}}.php.mustache +294 -0
  115. package/templates/_shared/compound/persistence-public/{{slugKebabCase}}.php.mustache +312 -0
  116. package/templates/_shared/migration-ui/common/src/admin/migration-dashboard.tsx +394 -0
  117. package/templates/_shared/migration-ui/common/src/migration-detector.ts +9 -0
  118. package/templates/_shared/migration-ui/common/src/migrations/helpers.ts +490 -0
  119. package/templates/_shared/migration-ui/common/src/migrations/index.ts +886 -0
  120. package/templates/_shared/persistence/auth/{{slugKebabCase}}.php.mustache +290 -0
  121. package/templates/_shared/persistence/core/package.json.mustache +46 -0
  122. package/templates/_shared/persistence/core/scripts/sync-rest-contracts.ts.mustache +113 -0
  123. package/templates/_shared/persistence/core/scripts/sync-types-to-block-json.ts.mustache +125 -0
  124. package/templates/_shared/persistence/core/src/api-types.ts.mustache +21 -0
  125. package/templates/_shared/persistence/core/src/api-validators.ts.mustache +32 -0
  126. package/templates/_shared/persistence/core/src/api.ts.mustache +68 -0
  127. package/templates/_shared/persistence/core/src/data.ts.mustache +192 -0
  128. package/templates/_shared/persistence/core/src/index.tsx.mustache +25 -0
  129. package/templates/_shared/persistence/core/src/interactivity.ts.mustache +134 -0
  130. package/templates/_shared/persistence/core/src/save.tsx.mustache +5 -0
  131. package/templates/_shared/persistence/core/src/validators.ts.mustache +32 -0
  132. package/templates/_shared/persistence/core/{{slugKebabCase}}.php.mustache +336 -0
  133. package/templates/_shared/persistence/public/{{slugKebabCase}}.php.mustache +308 -0
  134. package/templates/_shared/presets/test-preset/.wp-env.test.json.mustache +16 -0
  135. package/templates/_shared/presets/test-preset/playwright.config.ts.mustache +22 -0
  136. package/templates/_shared/presets/test-preset/scripts/wait-for-wp-env.mjs.mustache +102 -0
  137. package/templates/_shared/presets/test-preset/scripts/wp-env-utils.cjs.mustache +32 -0
  138. package/templates/_shared/presets/test-preset/tests/e2e/smoke.spec.ts.mustache +34 -0
  139. package/templates/_shared/presets/wp-env/.wp-env.json.mustache +16 -0
  140. package/templates/_shared/rest-helpers/auth/inc/rest-auth.php.mustache +37 -0
  141. package/templates/_shared/rest-helpers/public/inc/rest-public.php.mustache +314 -0
  142. package/templates/_shared/rest-helpers/shared/inc/rest-shared.php.mustache +58 -0
  143. package/templates/_shared/workspace/persistence-auth/inc/rest-auth.php.mustache +36 -0
  144. package/templates/_shared/workspace/persistence-auth/inc/rest-shared.php.mustache +55 -0
  145. package/templates/_shared/workspace/persistence-auth/server.php.mustache +237 -0
  146. package/templates/_shared/workspace/persistence-public/inc/rest-public.php.mustache +273 -0
  147. package/templates/_shared/workspace/persistence-public/inc/rest-shared.php.mustache +55 -0
  148. package/templates/_shared/workspace/persistence-public/server.php.mustache +252 -0
  149. package/templates/basic/src/block.json.mustache +51 -0
  150. package/templates/basic/src/edit.tsx.mustache +128 -0
  151. package/templates/basic/src/editor.scss.mustache +8 -0
  152. package/templates/basic/src/hooks.ts.mustache +18 -0
  153. package/templates/basic/src/index.tsx.mustache +45 -0
  154. package/templates/basic/src/save.tsx.mustache +30 -0
  155. package/templates/basic/src/style.scss.mustache +40 -0
  156. package/templates/basic/src/types.ts.mustache +56 -0
  157. package/templates/basic/src/validators.ts.mustache +26 -0
  158. package/templates/compound/src/blocks/{{slugKebabCase}}/block.json.mustache +37 -0
  159. package/templates/compound/src/blocks/{{slugKebabCase}}/children.ts.mustache +25 -0
  160. package/templates/compound/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +93 -0
  161. package/templates/compound/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +11 -0
  162. package/templates/compound/src/blocks/{{slugKebabCase}}/index.tsx.mustache +25 -0
  163. package/templates/compound/src/blocks/{{slugKebabCase}}/save.tsx.mustache +32 -0
  164. package/templates/compound/src/blocks/{{slugKebabCase}}/style.scss.mustache +31 -0
  165. package/templates/compound/src/blocks/{{slugKebabCase}}/types.ts.mustache +13 -0
  166. package/templates/compound/src/blocks/{{slugKebabCase}}/validators.ts.mustache +17 -0
  167. package/templates/compound/src/blocks/{{slugKebabCase}}-item/block.json.mustache +35 -0
  168. package/templates/compound/src/blocks/{{slugKebabCase}}-item/edit.tsx.mustache +50 -0
  169. package/templates/compound/src/blocks/{{slugKebabCase}}-item/hooks.ts.mustache +11 -0
  170. package/templates/compound/src/blocks/{{slugKebabCase}}-item/index.tsx.mustache +25 -0
  171. package/templates/compound/src/blocks/{{slugKebabCase}}-item/save.tsx.mustache +24 -0
  172. package/templates/compound/src/blocks/{{slugKebabCase}}-item/types.ts.mustache +12 -0
  173. package/templates/compound/src/blocks/{{slugKebabCase}}-item/validators.ts.mustache +17 -0
  174. package/templates/interactivity/package.json.mustache +42 -0
  175. package/templates/interactivity/src/block.json.mustache +73 -0
  176. package/templates/interactivity/src/edit.tsx.mustache +270 -0
  177. package/templates/interactivity/src/index.tsx.mustache +32 -0
  178. package/templates/interactivity/src/interactivity.ts.mustache +152 -0
  179. package/templates/interactivity/src/save.tsx.mustache +101 -0
  180. package/templates/interactivity/src/style.scss.mustache +60 -0
  181. package/templates/interactivity/src/types.ts.mustache +32 -0
  182. package/templates/interactivity/src/validators.ts.mustache +36 -0
  183. package/templates/persistence/src/block.json.mustache +52 -0
  184. package/templates/persistence/src/edit.tsx.mustache +165 -0
  185. package/templates/persistence/src/render.php.mustache +126 -0
  186. package/templates/persistence/src/style.scss.mustache +46 -0
  187. package/templates/persistence/src/types.ts.mustache +55 -0
@@ -0,0 +1,52 @@
1
+ {
2
+ "$schema": "https://schemas.wp.org/trunk/block.json",
3
+ "apiVersion": 3,
4
+ "name": "{{namespace}}/{{slugKebabCase}}",
5
+ "version": "{{blockMetadataVersion}}",
6
+ "title": "{{title}}",
7
+ "category": "{{category}}",
8
+ "icon": "{{icon}}",
9
+ "description": "{{description}}",
10
+ "example": {},
11
+ "supports": {
12
+ "html": false,
13
+ "align": true,
14
+ "anchor": true,
15
+ "className": true,
16
+ "interactivity": true
17
+ },
18
+ "attributes": {
19
+ "content": {
20
+ "type": "string",
21
+ "source": "html",
22
+ "selector": ".{{cssClassName}}__content",
23
+ "default": "{{title}} persistence block"
24
+ },
25
+ "alignment": {
26
+ "type": "string",
27
+ "enum": ["left", "center", "right"],
28
+ "default": "left"
29
+ },
30
+ "isVisible": {
31
+ "type": "boolean",
32
+ "default": true
33
+ },
34
+ "showCount": {
35
+ "type": "boolean",
36
+ "default": true
37
+ },
38
+ "buttonLabel": {
39
+ "type": "string",
40
+ "default": "Persist Count"
41
+ },
42
+ "resourceKey": {
43
+ "type": "string",
44
+ "default": ""
45
+ }
46
+ },
47
+ "textdomain": "{{textDomain}}",
48
+ "editorScript": "file:./index.js",
49
+ "style": "file:./style-index.css",
50
+ "viewScriptModule": "file:./interactivity.js",
51
+ "render": "file:./render.php"
52
+ }
@@ -0,0 +1,165 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import {
3
+ AlignmentToolbar,
4
+ BlockControls,
5
+ InspectorControls,
6
+ RichText,
7
+ useBlockProps,
8
+ } from '@wordpress/block-editor';
9
+ import {
10
+ Notice,
11
+ PanelBody,
12
+ TextControl,
13
+ } from '@wordpress/components';
14
+ import currentManifest from './typia.manifest.json';
15
+ import {
16
+ InspectorFromManifest,
17
+ type ManifestDocument,
18
+ useEditorFields,
19
+ useTypedAttributeUpdater,
20
+ } from '@wp-typia/block-runtime/inspector';
21
+ import type { {{pascalCase}}Attributes } from './types';
22
+ import {
23
+ sanitize{{pascalCase}}Attributes,
24
+ validate{{pascalCase}}Attributes,
25
+ } from './validators';
26
+ import { useTypiaValidation } from './hooks';
27
+
28
+ export default function Edit( {
29
+ attributes,
30
+ setAttributes,
31
+ }: {
32
+ attributes: {{pascalCase}}Attributes;
33
+ setAttributes: ( attrs: Partial< {{pascalCase}}Attributes > ) => void;
34
+ } ) {
35
+ const editorFields = useEditorFields(
36
+ currentManifest as ManifestDocument,
37
+ {
38
+ manual: [ 'content', 'resourceKey' ],
39
+ labels: {
40
+ buttonLabel: __( 'Button Label', '{{textDomain}}' ),
41
+ resourceKey: __( 'Resource Key', '{{textDomain}}' ),
42
+ showCount: __( 'Show Count', '{{textDomain}}' ),
43
+ },
44
+ }
45
+ );
46
+ const { errorMessages, isValid } = useTypiaValidation(
47
+ attributes,
48
+ validate{{pascalCase}}Attributes
49
+ );
50
+ const validateEditorUpdate = (
51
+ nextAttributes: {{pascalCase}}Attributes
52
+ ) => {
53
+ try {
54
+ return {
55
+ data: sanitize{{pascalCase}}Attributes( nextAttributes ),
56
+ errors: [],
57
+ isValid: true as const,
58
+ };
59
+ } catch {
60
+ return validate{{pascalCase}}Attributes( nextAttributes );
61
+ }
62
+ };
63
+ const { updateField } = useTypedAttributeUpdater(
64
+ attributes,
65
+ setAttributes,
66
+ validateEditorUpdate
67
+ );
68
+ const alignmentValue = editorFields.getStringValue(
69
+ attributes,
70
+ 'alignment',
71
+ 'left'
72
+ );
73
+ const persistencePolicy = '{{persistencePolicy}}';
74
+ const persistencePolicyDescription =
75
+ persistencePolicy === 'authenticated'
76
+ ? __( 'Writes require a logged-in user and a valid REST nonce.', '{{textDomain}}' )
77
+ : __( 'Anonymous writes use signed short-lived public tokens, per-request ids, and coarse rate limiting.', '{{textDomain}}' );
78
+
79
+ return (
80
+ <>
81
+ <BlockControls>
82
+ <AlignmentToolbar
83
+ value={ alignmentValue }
84
+ onChange={ ( value ) =>
85
+ updateField(
86
+ 'alignment',
87
+ ( value || alignmentValue ) as NonNullable< {{pascalCase}}Attributes[ 'alignment' ] >
88
+ )
89
+ }
90
+ />
91
+ </BlockControls>
92
+ <InspectorControls>
93
+ <InspectorFromManifest
94
+ attributes={ attributes }
95
+ fieldLookup={ editorFields }
96
+ onChange={ updateField }
97
+ paths={ [ 'alignment', 'isVisible', 'showCount', 'buttonLabel' ] }
98
+ title={ __( 'Persistence Settings', '{{textDomain}}' ) }
99
+ >
100
+ <TextControl
101
+ label={ __( 'Resource Key', '{{textDomain}}' ) }
102
+ value={ attributes.resourceKey ?? '' }
103
+ onChange={ ( value ) => updateField( 'resourceKey', value ) }
104
+ help={ __( 'Stable persisted identifier used by the storage-backed counter endpoint.', '{{textDomain}}' ) }
105
+ />
106
+ <Notice status="info" isDismissible={ false }>
107
+ { __( 'Storage mode: {{dataStorageMode}}', '{{textDomain}}' ) }
108
+ </Notice>
109
+ <Notice status="info" isDismissible={ false }>
110
+ { __( 'Persistence policy: {{persistencePolicy}}', '{{textDomain}}' ) }
111
+ <br />
112
+ { persistencePolicyDescription }
113
+ </Notice>
114
+ <Notice status="info" isDismissible={ false }>
115
+ { __( 'Render mode: dynamic. `render.php` bootstraps post context, storage-backed state, and write-policy data before hydration.', '{{textDomain}}' ) }
116
+ </Notice>
117
+ </InspectorFromManifest>
118
+ { ! isValid && (
119
+ <PanelBody
120
+ title={ __( 'Validation Errors', '{{textDomain}}' ) }
121
+ initialOpen
122
+ >
123
+ { errorMessages.map( ( error, index ) => (
124
+ <Notice key={ index } status="error" isDismissible={ false }>
125
+ { error }
126
+ </Notice>
127
+ ) ) }
128
+ </PanelBody>
129
+ ) }
130
+ </InspectorControls>
131
+ <div
132
+ { ...useBlockProps( {
133
+ className: '{{cssClassName}}',
134
+ style: {
135
+ textAlign:
136
+ alignmentValue as NonNullable< {{pascalCase}}Attributes[ 'alignment' ] >,
137
+ },
138
+ } ) }
139
+ >
140
+ <RichText
141
+ tagName="p"
142
+ value={ attributes.content }
143
+ onChange={ ( value ) => updateField( 'content', value ) }
144
+ placeholder={ __( '{{title}} persistence block', '{{textDomain}}' ) }
145
+ />
146
+ <p className="{{cssClassName}}__meta">
147
+ { __( 'Resource key:', '{{textDomain}}' ) } { attributes.resourceKey || '—' }
148
+ </p>
149
+ <p className="{{cssClassName}}__meta">
150
+ { __( 'Storage mode:', '{{textDomain}}' ) } {{dataStorageMode}}
151
+ </p>
152
+ <p className="{{cssClassName}}__meta">
153
+ { __( 'Persistence policy:', '{{textDomain}}' ) } {{persistencePolicy}}
154
+ </p>
155
+ { ! isValid && (
156
+ <Notice status="error" isDismissible={ false }>
157
+ <ul>
158
+ { errorMessages.map( ( error, index ) => <li key={ index }>{ error }</li> ) }
159
+ </ul>
160
+ </Notice>
161
+ ) }
162
+ </div>
163
+ </>
164
+ );
165
+ }
@@ -0,0 +1,126 @@
1
+ <?php
2
+ /**
3
+ * Dynamic render entry for the {{title}} block.
4
+ *
5
+ * @package {{pascalCase}}
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
+
12
+ $validator_path = __DIR__ . '/typia-validator.php';
13
+ if ( ! file_exists( $validator_path ) ) {
14
+ return '';
15
+ }
16
+
17
+ $validator = require $validator_path;
18
+ if ( ! is_object( $validator ) || ! method_exists( $validator, 'apply_defaults' ) || ! method_exists( $validator, 'validate' ) ) {
19
+ return '';
20
+ }
21
+
22
+ $normalized = $validator->apply_defaults( is_array( $attributes ) ? $attributes : array() );
23
+ $validation = $validator->validate( $normalized );
24
+ $resource_key = isset( $normalized['resourceKey'] ) ? (string) $normalized['resourceKey'] : '';
25
+
26
+ if ( empty( $validation['valid'] ) || '' === $resource_key ) {
27
+ return '';
28
+ }
29
+
30
+ $alignment = isset( $normalized['alignment'] ) ? (string) $normalized['alignment'] : 'left';
31
+ $button_label = isset( $normalized['buttonLabel'] ) ? (string) $normalized['buttonLabel'] : 'Persist Count';
32
+ $content = isset( $normalized['content'] ) ? (string) $normalized['content'] : '';
33
+ $post_id = is_object( $block ) && isset( $block->context['postId'] )
34
+ ? (int) $block->context['postId']
35
+ : (int) get_queried_object_id();
36
+ $storage_mode = '{{dataStorageMode}}';
37
+ $persistence_policy = '{{persistencePolicy}}';
38
+ $can_write = false;
39
+ $notice_message = 'authenticated' === $persistence_policy
40
+ ? __( 'Sign in to persist this counter.', '{{textDomain}}' )
41
+ : __( 'Reload the page to refresh this write token.', '{{textDomain}}' );
42
+ $context = array(
43
+ 'buttonLabel' => $button_label,
44
+ 'canWrite' => false,
45
+ 'count' => 0,
46
+ 'isVisible' => ! empty( $normalized['isVisible'] ),
47
+ 'persistencePolicy' => $persistence_policy,
48
+ 'postId' => (int) $post_id,
49
+ 'resourceKey' => $resource_key,
50
+ 'storage' => $storage_mode,
51
+ );
52
+
53
+ if ( 'authenticated' === $persistence_policy ) {
54
+ $can_write = $post_id > 0 && is_user_logged_in();
55
+ if ( $can_write ) {
56
+ $context['restNonce'] = wp_create_nonce( 'wp_rest' );
57
+ }
58
+ } elseif ( $post_id > 0 && function_exists( '{{phpPrefix}}_create_public_write_token' ) ) {
59
+ $public_write = {{phpPrefix}}_create_public_write_token( (int) $post_id, $resource_key );
60
+ if ( is_array( $public_write ) ) {
61
+ $token = isset( $public_write['token'] ) ? (string) $public_write['token'] : '';
62
+ $expires_at = isset( $public_write['expiresAt'] ) ? (int) $public_write['expiresAt'] : 0;
63
+
64
+ if ( '' !== $token ) {
65
+ $context['publicWriteToken'] = $token;
66
+ $can_write = true;
67
+ }
68
+
69
+ if ( $expires_at > 0 ) {
70
+ $context['publicWriteExpiresAt'] = $expires_at;
71
+ }
72
+ }
73
+ }
74
+
75
+ $context['canWrite'] = $can_write;
76
+
77
+ $wrapper_attributes = get_block_wrapper_attributes(
78
+ array(
79
+ 'data-wp-context' => wp_json_encode( $context ),
80
+ 'data-wp-interactive' => '{{slugKebabCase}}',
81
+ 'data-wp-init' => 'callbacks.init',
82
+ 'data-wp-run--mounted' => 'callbacks.mounted',
83
+ )
84
+ );
85
+ ?>
86
+
87
+ <div <?php echo $wrapper_attributes; ?>>
88
+ <div class="{{frontendCssClassName}}">
89
+ <p class="{{frontendCssClassName}}__content" style="<?php echo esc_attr( 'text-align:' . $alignment ); ?>">
90
+ <?php echo esc_html( $content ); ?>
91
+ </p>
92
+ <?php if ( ! $can_write ) : ?>
93
+ <p class="{{frontendCssClassName}}__notice">
94
+ <?php echo esc_html( $notice_message ); ?>
95
+ </p>
96
+ <?php endif; ?>
97
+ <p
98
+ class="{{frontendCssClassName}}__error"
99
+ role="status"
100
+ aria-live="polite"
101
+ aria-atomic="true"
102
+ data-wp-bind--hidden="!state.error"
103
+ data-wp-text="state.error"
104
+ hidden
105
+ ></p>
106
+ <?php if ( ! empty( $normalized['showCount'] ) ) : ?>
107
+ <span
108
+ class="{{frontendCssClassName}}__count"
109
+ role="status"
110
+ aria-live="polite"
111
+ aria-atomic="true"
112
+ data-wp-text="state.count"
113
+ >
114
+ 0
115
+ </span>
116
+ <?php endif; ?>
117
+ <button
118
+ type="button"
119
+ <?php echo $can_write ? '' : 'disabled'; ?>
120
+ data-wp-bind--disabled="!context.canWrite"
121
+ data-wp-on--click="actions.increment"
122
+ >
123
+ <?php echo esc_html( $button_label ); ?>
124
+ </button>
125
+ </div>
126
+ </div>
@@ -0,0 +1,46 @@
1
+ .{{cssClassName}} {
2
+ border: 1px solid #dcdcde;
3
+ border-radius: 12px;
4
+ padding: 16px;
5
+ background: #fff;
6
+
7
+ &__meta {
8
+ color: #50575e;
9
+ font-size: 13px;
10
+ margin: 8px 0 0;
11
+ }
12
+ }
13
+
14
+ .{{frontendCssClassName}} {
15
+ display: grid;
16
+ gap: 12px;
17
+ border: 1px solid #dcdcde;
18
+ border-radius: 12px;
19
+ padding: 16px;
20
+ background: #f6f7f7;
21
+
22
+ &__count {
23
+ display: inline-flex;
24
+ min-width: 3rem;
25
+ justify-content: center;
26
+ font-weight: 700;
27
+ }
28
+
29
+ &__notice,
30
+ &__error {
31
+ margin: 0;
32
+ font-size: 13px;
33
+ }
34
+
35
+ &__notice {
36
+ color: #50575e;
37
+ }
38
+
39
+ &__error {
40
+ color: #b32d2e;
41
+ }
42
+
43
+ button {
44
+ width: fit-content;
45
+ }
46
+ }
@@ -0,0 +1,55 @@
1
+ import type { TextAlignment } from '@wp-typia/block-types/block-editor/alignment';
2
+ import type {
3
+ TypiaValidationError,
4
+ ValidationResult,
5
+ } from '@wp-typia/block-runtime/validation';
6
+ import { tags } from 'typia';
7
+
8
+ export type {
9
+ TypiaValidationError,
10
+ ValidationResult,
11
+ } from '@wp-typia/block-runtime/validation';
12
+
13
+ export interface {{pascalCase}}Attributes {
14
+ content: string &
15
+ tags.MinLength< 1 > &
16
+ tags.MaxLength< 250 > &
17
+ tags.Default< '{{title}} persistence block' >;
18
+ alignment?: TextAlignment & tags.Default< 'left' >;
19
+ isVisible?: boolean & tags.Default< true >;
20
+ showCount?: boolean & tags.Default< true >;
21
+ buttonLabel?: string &
22
+ tags.MinLength< 1 > &
23
+ tags.MaxLength< 40 > &
24
+ tags.Default< 'Persist Count' >;
25
+ resourceKey?: string &
26
+ tags.MinLength< 1 > &
27
+ tags.MaxLength< 100 > &
28
+ tags.Default< 'primary' >;
29
+ }
30
+
31
+ export interface {{pascalCase}}Context {
32
+ buttonLabel: string;
33
+ canWrite: boolean;
34
+ count: number;
35
+ persistencePolicy: 'authenticated' | 'public';
36
+ postId: number;
37
+ publicWriteExpiresAt?: number;
38
+ publicWriteToken?: string;
39
+ resourceKey: string;
40
+ restNonce?: string;
41
+ storage: 'post-meta' | 'custom-table';
42
+ isVisible: boolean;
43
+ }
44
+
45
+ export interface {{pascalCase}}State {
46
+ canWrite: boolean;
47
+ count: number;
48
+ error?: string;
49
+ isHydrated: boolean;
50
+ isLoading: boolean;
51
+ isSaving: boolean;
52
+ isVisible: boolean;
53
+ }
54
+
55
+ export type {{pascalCase}}ValidationResult = ValidationResult< {{pascalCase}}Attributes >;