@wp-typia/project-tools 0.16.2 → 0.16.4

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 (64) hide show
  1. package/README.md +11 -0
  2. package/dist/runtime/block-generator-service.d.ts +102 -0
  3. package/dist/runtime/block-generator-service.js +268 -0
  4. package/dist/runtime/built-in-block-artifacts.d.ts +37 -0
  5. package/dist/runtime/built-in-block-artifacts.js +1203 -0
  6. package/dist/runtime/built-in-block-code-artifacts.d.ts +30 -0
  7. package/dist/runtime/built-in-block-code-artifacts.js +122 -0
  8. package/dist/runtime/index.d.ts +2 -0
  9. package/dist/runtime/index.js +1 -0
  10. package/dist/runtime/scaffold-apply-utils.d.ts +47 -0
  11. package/dist/runtime/scaffold-apply-utils.js +405 -0
  12. package/dist/runtime/scaffold-identifiers.d.ts +34 -0
  13. package/dist/runtime/scaffold-identifiers.js +82 -0
  14. package/dist/runtime/scaffold.js +33 -0
  15. package/dist/runtime/starter-manifests.d.ts +3 -2
  16. package/dist/runtime/starter-manifests.js +15 -365
  17. package/dist/runtime/template-render.d.ts +5 -0
  18. package/dist/runtime/template-render.js +13 -3
  19. package/package.json +2 -2
  20. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +4 -4
  21. package/templates/_shared/persistence/core/scripts/sync-rest-contracts.ts.mustache +4 -4
  22. package/templates/_shared/base/src/hooks.ts.mustache +0 -19
  23. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/block.json.mustache +0 -52
  24. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +0 -123
  25. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +0 -11
  26. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/interactivity.ts.mustache +0 -305
  27. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/save.tsx.mustache +0 -3
  28. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/types.ts.mustache +0 -61
  29. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/validators.ts.mustache +0 -43
  30. package/templates/_shared/persistence/core/src/index.tsx.mustache +0 -25
  31. package/templates/_shared/persistence/core/src/interactivity.ts.mustache +0 -308
  32. package/templates/_shared/persistence/core/src/save.tsx.mustache +0 -5
  33. package/templates/_shared/persistence/core/src/validators.ts.mustache +0 -43
  34. package/templates/basic/src/block.json.mustache +0 -51
  35. package/templates/basic/src/edit.tsx.mustache +0 -128
  36. package/templates/basic/src/index.tsx.mustache +0 -45
  37. package/templates/basic/src/save.tsx.mustache +0 -30
  38. package/templates/basic/src/types.ts.mustache +0 -56
  39. package/templates/basic/src/validators.ts.mustache +0 -37
  40. package/templates/compound/src/blocks/{{slugKebabCase}}/block.json.mustache +0 -37
  41. package/templates/compound/src/blocks/{{slugKebabCase}}/children.ts.mustache +0 -25
  42. package/templates/compound/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +0 -93
  43. package/templates/compound/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +0 -11
  44. package/templates/compound/src/blocks/{{slugKebabCase}}/index.tsx.mustache +0 -25
  45. package/templates/compound/src/blocks/{{slugKebabCase}}/save.tsx.mustache +0 -32
  46. package/templates/compound/src/blocks/{{slugKebabCase}}/types.ts.mustache +0 -18
  47. package/templates/compound/src/blocks/{{slugKebabCase}}/validators.ts.mustache +0 -35
  48. package/templates/compound/src/blocks/{{slugKebabCase}}-item/block.json.mustache +0 -35
  49. package/templates/compound/src/blocks/{{slugKebabCase}}-item/edit.tsx.mustache +0 -50
  50. package/templates/compound/src/blocks/{{slugKebabCase}}-item/hooks.ts.mustache +0 -11
  51. package/templates/compound/src/blocks/{{slugKebabCase}}-item/index.tsx.mustache +0 -25
  52. package/templates/compound/src/blocks/{{slugKebabCase}}-item/save.tsx.mustache +0 -24
  53. package/templates/compound/src/blocks/{{slugKebabCase}}-item/types.ts.mustache +0 -17
  54. package/templates/compound/src/blocks/{{slugKebabCase}}-item/validators.ts.mustache +0 -35
  55. package/templates/interactivity/src/block.json.mustache +0 -74
  56. package/templates/interactivity/src/edit.tsx.mustache +0 -270
  57. package/templates/interactivity/src/index.tsx.mustache +0 -33
  58. package/templates/interactivity/src/interactivity.ts.mustache +0 -152
  59. package/templates/interactivity/src/save.tsx.mustache +0 -101
  60. package/templates/interactivity/src/types.ts.mustache +0 -32
  61. package/templates/interactivity/src/validators.ts.mustache +0 -47
  62. package/templates/persistence/src/block.json.mustache +0 -52
  63. package/templates/persistence/src/edit.tsx.mustache +0 -165
  64. package/templates/persistence/src/types.ts.mustache +0 -59
@@ -1,270 +0,0 @@
1
- import { __ } from '@wordpress/i18n';
2
- import { useBlockProps, InspectorControls, RichText, BlockControls, AlignmentToolbar } from '@wordpress/block-editor';
3
- import { PanelBody, RangeControl, TextControl, Button, Notice } from '@wordpress/components';
4
- import { useState } from '@wordpress/element';
5
- import currentManifest from './typia.manifest.json';
6
- import {
7
- InspectorFromManifest,
8
- type ManifestDocument,
9
- useEditorFields,
10
- useTypedAttributeUpdater,
11
- } from '@wp-typia/block-runtime/inspector';
12
- import type { {{pascalCase}}Attributes } from './types';
13
- import {
14
- sanitize{{pascalCase}}Attributes,
15
- validate{{pascalCase}}Attributes,
16
- } from './validators';
17
- import { useTypiaValidation } from './hooks';
18
-
19
- export default function Edit({ attributes, setAttributes, isSelected }: {
20
- attributes: {{pascalCase}}Attributes;
21
- setAttributes: (attrs: Partial<{{pascalCase}}Attributes>) => void;
22
- isSelected: boolean;
23
- }) {
24
- const [isPreviewing, setIsPreviewing] = useState(false);
25
- const editorFields = useEditorFields(currentManifest as ManifestDocument, {
26
- manual: ['content', 'clickCount', 'maxClicks', 'autoPlayInterval', 'uniqueId'],
27
- labels: {
28
- alignment: __('Alignment', '{{textDomain}}'),
29
- animation: __('Animation', '{{textDomain}}'),
30
- interactiveMode: __('Interactive Mode', '{{textDomain}}'),
31
- isVisible: __('Visible', '{{textDomain}}'),
32
- showCounter: __('Show Counter', '{{textDomain}}'),
33
- },
34
- });
35
- const { errorMessages, isValid } = useTypiaValidation(
36
- attributes,
37
- validate{{pascalCase}}Attributes,
38
- );
39
- const validateEditorUpdate = (nextAttributes: {{pascalCase}}Attributes) => {
40
- try {
41
- return {
42
- data: sanitize{{pascalCase}}Attributes(nextAttributes),
43
- errors: [],
44
- isValid: true as const,
45
- };
46
- } catch {
47
- return validate{{pascalCase}}Attributes(nextAttributes);
48
- }
49
- };
50
- const { updateField } = useTypedAttributeUpdater(
51
- attributes,
52
- setAttributes,
53
- validateEditorUpdate
54
- );
55
- const alignmentValue = editorFields.getStringValue(
56
- attributes,
57
- 'alignment',
58
- 'left'
59
- );
60
- const isVisible = editorFields.getBooleanValue(
61
- attributes,
62
- 'isVisible',
63
- true
64
- );
65
- const showCounter = editorFields.getBooleanValue(
66
- attributes,
67
- 'showCounter',
68
- true
69
- );
70
- const interactiveMode = editorFields.getStringValue(
71
- attributes,
72
- 'interactiveMode',
73
- 'click'
74
- );
75
- const animation = editorFields.getStringValue(
76
- attributes,
77
- 'animation',
78
- 'none'
79
- );
80
-
81
- const blockProps = useBlockProps({
82
- className: `{{cssClassName}} {{cssClassName}}--${interactiveMode}`,
83
- 'data-wp-interactive': '{{slugKebabCase}}',
84
- 'data-wp-context': JSON.stringify({
85
- clicks: attributes.clickCount,
86
- isAnimating: attributes.isAnimating,
87
- isVisible,
88
- lastInteraction: '',
89
- animation,
90
- interactiveMode,
91
- maxClicks: attributes.maxClicks,
92
- autoPlayInterval: attributes.autoPlayInterval
93
- })
94
- });
95
-
96
- const resetCounter = () => {
97
- updateField('clickCount', 0);
98
- updateField('isAnimating', false);
99
- };
100
-
101
- const testAnimation = () => {
102
- updateField('isAnimating', true);
103
- setTimeout(() => {
104
- updateField('isAnimating', false);
105
- }, 1000);
106
- };
107
-
108
- return (
109
- <>
110
- <BlockControls>
111
- <AlignmentToolbar
112
- value={alignmentValue}
113
- onChange={(value) => updateField('alignment', (value || alignmentValue) as NonNullable<{{pascalCase}}Attributes['alignment']>)}
114
- />
115
- </BlockControls>
116
-
117
- <InspectorControls>
118
- <InspectorFromManifest
119
- attributes={attributes}
120
- fieldLookup={editorFields}
121
- onChange={updateField}
122
- paths={['alignment', 'interactiveMode', 'animation', 'showCounter', 'isVisible']}
123
- title={__('Interactive Settings', '{{textDomain}}')}
124
- />
125
-
126
- <PanelBody title={__('Counter Settings', '{{textDomain}}')}>
127
- <RangeControl
128
- label={__('Max Clicks', '{{textDomain}}')}
129
- value={attributes.maxClicks}
130
- onChange={(value) => updateField('maxClicks', value ?? attributes.maxClicks ?? 0)}
131
- min={0}
132
- max={100}
133
- help={__('Set to 0 for unlimited clicks', '{{textDomain}}')}
134
- />
135
-
136
- {interactiveMode === 'auto' && (
137
- <RangeControl
138
- label={__('Auto Play Interval (ms)', '{{textDomain}}')}
139
- value={attributes.autoPlayInterval}
140
- onChange={(value) => updateField('autoPlayInterval', value ?? attributes.autoPlayInterval ?? 0)}
141
- min={100}
142
- max={5000}
143
- step={100}
144
- help={__('Interval between automatic animations', '{{textDomain}}')}
145
- />
146
- )}
147
-
148
- <div style={{ display: 'flex', gap: '8px', marginTop: '16px' }}>
149
- <Button
150
- variant="secondary"
151
- onClick={resetCounter}
152
- isSmall
153
- >
154
- {__('Reset Counter', '{{textDomain}}')}
155
- </Button>
156
- <Button
157
- variant="secondary"
158
- onClick={testAnimation}
159
- isSmall
160
- >
161
- {__('Test Animation', '{{textDomain}}')}
162
- </Button>
163
- </div>
164
-
165
- <TextControl
166
- label={__('Unique ID', '{{textDomain}}')}
167
- value={attributes.uniqueId}
168
- onChange={(value) => updateField('uniqueId', value)}
169
- help={__('Optional unique identifier for this block instance', '{{textDomain}}')}
170
- />
171
- </PanelBody>
172
-
173
- {!isValid && (
174
- <PanelBody title={__('Validation Errors', '{{textDomain}}')} initialOpen>
175
- {errorMessages.map((error, index) => (
176
- <Notice key={index} status="error" isDismissible={false}>
177
- {error}
178
- </Notice>
179
- ))}
180
- </PanelBody>
181
- )}
182
-
183
- {isSelected && (
184
- <PanelBody title={__('Preview', '{{textDomain}}')}>
185
- <Button
186
- variant={isPreviewing ? 'primary' : 'secondary'}
187
- onClick={() => setIsPreviewing(!isPreviewing)}
188
- aria-pressed={isPreviewing}
189
- isSmall
190
- >
191
- {isPreviewing
192
- ? __('Disable Preview Mode', '{{textDomain}}')
193
- : __('Enable Preview Mode', '{{textDomain}}')}
194
- </Button>
195
-
196
- {attributes.clickCount > 0 && (
197
- <Notice status="info" isDismissible={false}>
198
- {__('Current clicks:', '{{textDomain}}')} {attributes.clickCount}
199
- {attributes.maxClicks > 0 && ` / ${attributes.maxClicks}`}
200
- </Notice>
201
- )}
202
- </PanelBody>
203
- )}
204
- </InspectorControls>
205
-
206
- <div {...blockProps}>
207
- <div
208
- className={`{{cssClassName}}__content ${attributes.isAnimating ? 'is-animating' : ''}`}
209
- style={{ textAlign: alignmentValue }}
210
- data-wp-on--click={isPreviewing ? 'actions.handleClick' : undefined}
211
- data-wp-on--mouseenter={isPreviewing && interactiveMode === 'hover' ? 'actions.handleMouseEnter' : undefined}
212
- data-wp-on--mouseleave={isPreviewing && interactiveMode === 'hover' ? 'actions.handleMouseLeave' : undefined}
213
- >
214
- <RichText
215
- tagName="p"
216
- value={attributes.content}
217
- onChange={(value) => updateField('content', value)}
218
- placeholder={__('{{title}} – click me to interact!', '{{textDomain}}')}
219
- />
220
-
221
- {!isValid && (
222
- <Notice status="error" isDismissible={false}>
223
- <p>
224
- <strong>{__('Validation Errors', '{{textDomain}}')}</strong>
225
- </p>
226
- <ul style={{ margin: 0, paddingLeft: '1em' }}>
227
- {errorMessages.map((error, index) => (
228
- <li key={index}>{error}</li>
229
- ))}
230
- </ul>
231
- </Notice>
232
- )}
233
-
234
- {showCounter && (
235
- <div className="{{cssClassName}}__counter">
236
- <span className="{{cssClassName}}__counter-label">
237
- {__('Clicks:', '{{textDomain}}')}
238
- </span>
239
- <span
240
- className="{{cssClassName}}__counter-value"
241
- data-wp-text="state.clicks"
242
- >
243
- {attributes.clickCount}
244
- </span>
245
- </div>
246
- )}
247
-
248
- {attributes.maxClicks > 0 && (
249
- <div className="{{cssClassName}}__progress">
250
- <div
251
- className="{{cssClassName}}__progress-bar"
252
- style={{ width: `${(attributes.clickCount / attributes.maxClicks) * 100}%` }}
253
- data-wp-style--width="state.progress + '%'"
254
- />
255
- </div>
256
- )}
257
-
258
- {animation !== 'none' && (
259
- <div
260
- className={`{{cssClassName}}__animation ${attributes.isAnimating ? 'is-active' : ''}`}
261
- data-wp-class--is-active="state.isAnimating"
262
- >
263
- {animation}
264
- </div>
265
- )}
266
- </div>
267
- </div>
268
- </>
269
- );
270
- }
@@ -1,33 +0,0 @@
1
- import { registerBlockType } from '@wordpress/blocks';
2
- import type { BlockConfiguration } from '@wordpress/blocks';
3
- import type { BlockSupports } from '@wp-typia/block-types/blocks/supports';
4
- import {
5
- buildScaffoldBlockRegistration,
6
- type ScaffoldBlockMetadata,
7
- } from '@wp-typia/block-runtime/blocks';
8
-
9
- import Edit from './edit';
10
- import Save from './save';
11
- import metadata from './block.json';
12
- import './editor.scss';
13
- import './style.scss';
14
-
15
- import type { {{pascalCase}}Attributes } from './types';
16
-
17
- const scaffoldSupports = {
18
- html: false,
19
- align: true,
20
- anchor: true,
21
- className: true,
22
- interactivity: true,
23
- } satisfies BlockSupports;
24
-
25
- const registration = buildScaffoldBlockRegistration<
26
- BlockConfiguration<{{pascalCase}}Attributes>
27
- >(metadata as ScaffoldBlockMetadata, {
28
- supports: scaffoldSupports,
29
- edit: Edit,
30
- save: Save,
31
- });
32
-
33
- registerBlockType<{{pascalCase}}Attributes>(registration.name, registration.settings);
@@ -1,152 +0,0 @@
1
- /**
2
- * WordPress Interactivity API implementation for {{title}} block
3
- */
4
- import { store, getContext, getElement } from '@wordpress/interactivity';
5
-
6
- // Store configuration
7
- store('{{slugKebabCase}}', {
8
- // State - reactive data that updates the UI
9
- state: {
10
- get clicks() {
11
- return getContext().clicks;
12
- },
13
- get isAnimating() {
14
- return getContext().isAnimating;
15
- },
16
- get isVisible() {
17
- return getContext().isVisible;
18
- },
19
- get animationClass() {
20
- const context = getContext();
21
- if (!context.isAnimating) return '';
22
-
23
- switch (context.animation) {
24
- case 'bounce':
25
- return 'animate-bounce';
26
- case 'pulse':
27
- return 'animate-pulse';
28
- case 'shake':
29
- return 'animate-shake';
30
- case 'flip':
31
- return 'animate-flip';
32
- default:
33
- return '';
34
- }
35
- },
36
- get progress() {
37
- const context = getContext();
38
- return context.maxClicks > 0 ? (context.clicks / context.maxClicks) * 100 : 0;
39
- },
40
- get clampedClicks() {
41
- const context = getContext();
42
- return context.maxClicks > 0
43
- ? Math.min(context.clicks, context.maxClicks)
44
- : context.clicks;
45
- },
46
- get isComplete() {
47
- const context = getContext();
48
- return context.clicks >= context.maxClicks && context.maxClicks > 0;
49
- }
50
- },
51
-
52
- // Actions - user interactions
53
- actions: {
54
- // Handle block click
55
- handleClick: () => {
56
- const context = getContext();
57
- const { element } = getElement();
58
-
59
- // Increment click counter
60
- context.clicks += 1;
61
- context.lastInteraction = 'click';
62
-
63
- // Trigger animation
64
- if (context.animation !== 'none') {
65
- context.isAnimating = true;
66
- setTimeout(() => {
67
- context.isAnimating = false;
68
- }, 1000);
69
- }
70
-
71
- // Emit custom event
72
- element.dispatchEvent(new CustomEvent('{{slugKebabCase}}:click', {
73
- detail: { clicks: context.clicks }
74
- }));
75
-
76
- // Check if max clicks reached
77
- if (context.maxClicks > 0 && context.clicks >= context.maxClicks) {
78
- element.dispatchEvent(new CustomEvent('{{slugKebabCase}}:complete', {
79
- detail: { totalClicks: context.clicks }
80
- }));
81
- }
82
- },
83
-
84
- // Handle hover events
85
- handleMouseEnter: () => {
86
- const context = getContext();
87
- if (context.interactiveMode === 'hover') {
88
- context.isAnimating = true;
89
- context.lastInteraction = 'hover';
90
- }
91
- },
92
-
93
- handleMouseLeave: () => {
94
- const context = getContext();
95
- if (context.interactiveMode === 'hover') {
96
- context.isAnimating = false;
97
- }
98
- },
99
-
100
- // Toggle visibility
101
- toggleVisibility: () => {
102
- const context = getContext();
103
- context.isVisible = !context.isVisible;
104
- },
105
-
106
- // Reset counter
107
- reset: () => {
108
- const context = getContext();
109
- context.clicks = 0;
110
- context.isAnimating = false;
111
- context.lastInteraction = 'reset';
112
- },
113
-
114
- // Start/stop auto play
115
- toggleAutoPlay: () => {
116
- const context = getContext();
117
- if (context.autoPlayInterval > 0) {
118
- if (context.autoPlayTimer) {
119
- clearInterval(context.autoPlayTimer);
120
- context.autoPlayTimer = null;
121
- } else {
122
- context.autoPlayTimer = setInterval(() => {
123
- context.clicks += 1;
124
- context.isAnimating = true;
125
- setTimeout(() => {
126
- context.isAnimating = false;
127
- }, 500);
128
- }, context.autoPlayInterval);
129
- }
130
- }
131
- }
132
- }
133
- });
134
-
135
- // Type definitions for TypeScript
136
- declare global {
137
- namespace WordPress {
138
- namespace Interactivity {
139
- interface {{pascalCase}}Context {
140
- clicks: number;
141
- isAnimating: boolean;
142
- isVisible: boolean;
143
- lastInteraction: string;
144
- animation: string;
145
- interactiveMode: string;
146
- maxClicks: number;
147
- autoPlayInterval: number;
148
- autoPlayTimer?: number;
149
- }
150
- }
151
- }
152
- }
@@ -1,101 +0,0 @@
1
- import { useBlockProps, RichText } from '@wordpress/block-editor';
2
- import type { {{pascalCase}}Attributes } from './types';
3
-
4
- export default function Save({ attributes }: { attributes: {{pascalCase}}Attributes }) {
5
- const blockProps = useBlockProps.save({
6
- className: `{{cssClassName}} {{cssClassName}}--${attributes.interactiveMode}`,
7
- 'data-wp-interactive': '{{slugKebabCase}}',
8
- 'data-wp-context': JSON.stringify({
9
- clicks: attributes.clickCount,
10
- isAnimating: attributes.isAnimating,
11
- isVisible: attributes.isVisible,
12
- lastInteraction: '',
13
- animation: attributes.animation,
14
- interactiveMode: attributes.interactiveMode,
15
- maxClicks: attributes.maxClicks,
16
- autoPlayInterval: attributes.autoPlayInterval
17
- })
18
- });
19
-
20
- return (
21
- <div {...blockProps}>
22
- <div
23
- className={`{{cssClassName}}__content ${attributes.isAnimating ? 'is-animating' : ''}`}
24
- style={{ textAlign: attributes.alignment }}
25
- data-wp-on--click="actions.handleClick"
26
- data-wp-on--mouseenter={attributes.interactiveMode === 'hover' ? 'actions.handleMouseEnter' : undefined}
27
- data-wp-on--mouseleave={attributes.interactiveMode === 'hover' ? 'actions.handleMouseLeave' : undefined}
28
- data-wp-bind--hidden="!state.isVisible"
29
- >
30
- <RichText.Content
31
- tagName="p"
32
- value={attributes.content}
33
- className="{{cssClassName}}__text"
34
- />
35
-
36
- {attributes.showCounter && (
37
- <div
38
- className="{{cssClassName}}__counter"
39
- role="status"
40
- aria-live="polite"
41
- aria-atomic="true"
42
- >
43
- <span className="{{cssClassName}}__counter-label">
44
- Clicks:
45
- </span>
46
- <span
47
- className="{{cssClassName}}__counter-value"
48
- data-wp-text="state.clicks"
49
- >
50
- {attributes.clickCount}
51
- </span>
52
- </div>
53
- )}
54
-
55
- {attributes.maxClicks > 0 && (
56
- <div className="{{cssClassName}}__progress">
57
- <div
58
- className="{{cssClassName}}__progress-bar"
59
- role="progressbar"
60
- aria-label="Click progress"
61
- aria-valuemin={0}
62
- aria-valuemax={attributes.maxClicks}
63
- aria-valuenow={Math.min(attributes.clickCount, attributes.maxClicks)}
64
- data-wp-bind--aria-valuenow="state.clampedClicks"
65
- data-wp-style--width="state.progress + '%'"
66
- />
67
- </div>
68
- )}
69
-
70
- <div
71
- className={`{{cssClassName}}__animation ${attributes.animation}`}
72
- aria-hidden="true"
73
- data-wp-class--is-active="state.isAnimating"
74
- />
75
-
76
- {attributes.maxClicks > 0 && (
77
- <div
78
- className="{{cssClassName}}__completion"
79
- role="status"
80
- aria-live="polite"
81
- aria-atomic="true"
82
- data-wp-bind--hidden="!state.isComplete"
83
- >
84
- 🎉 Complete!
85
- </div>
86
- )}
87
-
88
- <button
89
- className="{{cssClassName}}__reset"
90
- data-wp-on--click="actions.reset"
91
- aria-label="Reset counter"
92
- >
93
- <span aria-hidden="true">↻</span>
94
- <span className="screen-reader-text">
95
- Reset counter
96
- </span>
97
- </button>
98
- </div>
99
- </div>
100
- );
101
- }
@@ -1,32 +0,0 @@
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 { TypiaValidationError, ValidationResult } from "@wp-typia/block-runtime/validation";
9
-
10
- export interface {{pascalCase}}Attributes {
11
- content: string & tags.MinLength<1> & tags.MaxLength<1000> & tags.Default<"">;
12
- alignment?: TextAlignment & tags.Default<"left">;
13
- isVisible?: boolean & tags.Default<true>;
14
- interactiveMode?: ('click' | 'hover' | 'auto') & tags.Default<"click">;
15
- animation?: ('none' | 'bounce' | 'pulse' | 'shake' | 'flip') & tags.Default<"none">;
16
- clickCount?: number & tags.Minimum<0> & tags.Type<"uint32"> & tags.Default<0>;
17
- isAnimating?: boolean & tags.Default<false>;
18
- showCounter?: boolean & tags.Default<true>;
19
- maxClicks?: number & tags.Minimum<0> & tags.Type<"uint32"> & tags.Default<10>;
20
- autoPlayInterval?: number & tags.Minimum<0> & tags.Type<"uint32"> & tags.Default<0>;
21
- uniqueId?: string & tags.Default<"">;
22
- }
23
-
24
- export interface {{pascalCase}}Context {
25
- clicks: number;
26
- isAnimating: boolean;
27
- isVisible: boolean;
28
- lastInteraction: string;
29
- animationClass: string;
30
- }
31
-
32
- export type {{pascalCase}}ValidationResult = ValidationResult<{{pascalCase}}Attributes>;
@@ -1,47 +0,0 @@
1
- import typia from 'typia';
2
- import currentManifest from "./typia.manifest.json";
3
- import { {{pascalCase}}Attributes, {{pascalCase}}ValidationResult } from "./types";
4
- import { generateScopedClientId } from "@wp-typia/block-runtime/identifiers";
5
- import { createTemplateValidatorToolkit } from "./validator-toolkit";
6
-
7
- const scaffoldValidators = createTemplateValidatorToolkit<{{pascalCase}}Attributes>({
8
- assert: typia.createAssert<{{pascalCase}}Attributes>(),
9
- clone: typia.misc.createClone<{{pascalCase}}Attributes>() as (
10
- value: {{pascalCase}}Attributes,
11
- ) => {{pascalCase}}Attributes,
12
- is: typia.createIs<{{pascalCase}}Attributes>(),
13
- manifest: currentManifest,
14
- prune: typia.misc.createPrune<{{pascalCase}}Attributes>(),
15
- random: typia.createRandom<{{pascalCase}}Attributes>() as (
16
- ...args: unknown[]
17
- ) => {{pascalCase}}Attributes,
18
- finalize: (normalized) => ({
19
- ...normalized,
20
- uniqueId:
21
- normalized.uniqueId && normalized.uniqueId.length > 0
22
- ? normalized.uniqueId
23
- : generateScopedClientId("{{slugKebabCase}}"),
24
- }),
25
- validate: typia.createValidate<{{pascalCase}}Attributes>(),
26
- });
27
-
28
- export const validate{{pascalCase}}Attributes =
29
- scaffoldValidators.validateAttributes as (
30
- attributes: unknown,
31
- ) => {{pascalCase}}ValidationResult;
32
-
33
- export const validators = scaffoldValidators.validators;
34
-
35
- export const sanitize{{pascalCase}}Attributes =
36
- scaffoldValidators.sanitizeAttributes as (
37
- attributes: Partial<{{pascalCase}}Attributes>,
38
- ) => {{pascalCase}}Attributes;
39
-
40
- /**
41
- * Runtime type guard for checking if an object is {{pascalCase}}Attributes.
42
- */
43
- export const is{{pascalCase}}Attributes = (obj: unknown): obj is {{pascalCase}}Attributes => {
44
- return validators.is(obj);
45
- };
46
-
47
- export const createAttributeUpdater = scaffoldValidators.createAttributeUpdater;
@@ -1,52 +0,0 @@
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
- }