@wp-typia/project-tools 0.16.9 → 0.16.11
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.
- package/dist/runtime/built-in-block-artifacts.js +342 -274
- package/dist/runtime/built-in-block-code-templates.d.ts +5 -5
- package/dist/runtime/built-in-block-code-templates.js +25 -24
- package/dist/runtime/cli-help.js +1 -1
- package/dist/runtime/cli-scaffold.js +1 -1
- package/dist/runtime/migration-ui-capability.js +1 -1
- package/package.json +3 -3
- package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +5 -6
|
@@ -116,6 +116,36 @@ function defineNumberAttribute(spec) {
|
|
|
116
116
|
sourceType: "number",
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
|
+
function resolveBuiltInAttributeValue(value, context) {
|
|
120
|
+
if (typeof value === "function") {
|
|
121
|
+
return value(context);
|
|
122
|
+
}
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
function buildAttributesFromSpecs(specs, context) {
|
|
126
|
+
return specs.map((spec) => {
|
|
127
|
+
const resolvedSpec = {
|
|
128
|
+
blockJsonDefaultValue: resolveBuiltInAttributeValue(spec.blockJsonDefaultValue, context),
|
|
129
|
+
constraints: resolveBuiltInAttributeValue(spec.constraints, context),
|
|
130
|
+
defaultValue: resolveBuiltInAttributeValue(spec.defaultValue, context),
|
|
131
|
+
description: resolveBuiltInAttributeValue(spec.description, context),
|
|
132
|
+
enumValues: resolveBuiltInAttributeValue(spec.enumValues, context),
|
|
133
|
+
manifestDefaultValue: resolveBuiltInAttributeValue(spec.manifestDefaultValue, context),
|
|
134
|
+
name: spec.name,
|
|
135
|
+
optional: spec.optional,
|
|
136
|
+
selector: resolveBuiltInAttributeValue(spec.selector, context),
|
|
137
|
+
source: resolveBuiltInAttributeValue(spec.source, context),
|
|
138
|
+
typeExpression: resolveBuiltInAttributeValue(spec.typeExpression, context) ?? "unknown",
|
|
139
|
+
};
|
|
140
|
+
if (spec.attributeType === "boolean") {
|
|
141
|
+
return defineBooleanAttribute(resolvedSpec);
|
|
142
|
+
}
|
|
143
|
+
if (spec.attributeType === "number") {
|
|
144
|
+
return defineNumberAttribute(resolvedSpec);
|
|
145
|
+
}
|
|
146
|
+
return defineStringAttribute(resolvedSpec);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
119
149
|
function buildManifestDocument(sourceType, attributes) {
|
|
120
150
|
return {
|
|
121
151
|
attributes: Object.fromEntries(attributes.map((attribute) => [
|
|
@@ -175,289 +205,327 @@ function emitTypesModule({ preambleLines, interfaces, typeAliases, }) {
|
|
|
175
205
|
function stringifyBlockJsonDocument(document) {
|
|
176
206
|
return `${JSON.stringify(document, null, "\t")}\n`;
|
|
177
207
|
}
|
|
208
|
+
const BASIC_ATTRIBUTE_SPECS = [
|
|
209
|
+
{
|
|
210
|
+
attributeType: "string",
|
|
211
|
+
constraints: {
|
|
212
|
+
maxLength: 1000,
|
|
213
|
+
},
|
|
214
|
+
defaultValue: "",
|
|
215
|
+
description: describe("Main block content"),
|
|
216
|
+
name: "content",
|
|
217
|
+
optional: false,
|
|
218
|
+
typeExpression: 'string & tags.MaxLength<1000> & tags.Default<"">',
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
attributeType: "string",
|
|
222
|
+
defaultValue: "left",
|
|
223
|
+
description: describe("Alignment"),
|
|
224
|
+
enumValues: [...BASIC_ALIGNMENT_VALUES],
|
|
225
|
+
name: "alignment",
|
|
226
|
+
optional: true,
|
|
227
|
+
typeExpression: 'TextAlignment & tags.Default<"left">',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
attributeType: "boolean",
|
|
231
|
+
defaultValue: true,
|
|
232
|
+
description: describe("Visibility toggle"),
|
|
233
|
+
name: "isVisible",
|
|
234
|
+
optional: true,
|
|
235
|
+
typeExpression: "boolean & tags.Default<true>",
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
attributeType: "string",
|
|
239
|
+
constraints: {
|
|
240
|
+
maxLength: 100,
|
|
241
|
+
},
|
|
242
|
+
defaultValue: "",
|
|
243
|
+
description: describe("Custom CSS class"),
|
|
244
|
+
name: "className",
|
|
245
|
+
optional: true,
|
|
246
|
+
typeExpression: 'string & tags.MaxLength<100> & tags.Default<"">',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
attributeType: "string",
|
|
250
|
+
constraints: {
|
|
251
|
+
format: "uuid",
|
|
252
|
+
},
|
|
253
|
+
description: describe("Generated runtime ID"),
|
|
254
|
+
name: "id",
|
|
255
|
+
optional: true,
|
|
256
|
+
typeExpression: 'string & tags.Format<"uuid">',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
attributeType: "number",
|
|
260
|
+
constraints: {
|
|
261
|
+
typeTag: "uint32",
|
|
262
|
+
},
|
|
263
|
+
defaultValue: 1,
|
|
264
|
+
description: describe("Block version for migrations"),
|
|
265
|
+
name: "schemaVersion",
|
|
266
|
+
optional: true,
|
|
267
|
+
typeExpression: 'number & tags.Type<"uint32"> & tags.Default<1>',
|
|
268
|
+
},
|
|
269
|
+
];
|
|
270
|
+
const INTERACTIVITY_ATTRIBUTE_SPECS = [
|
|
271
|
+
{
|
|
272
|
+
attributeType: "string",
|
|
273
|
+
constraints: {
|
|
274
|
+
maxLength: 1000,
|
|
275
|
+
},
|
|
276
|
+
defaultValue: "",
|
|
277
|
+
name: "content",
|
|
278
|
+
optional: false,
|
|
279
|
+
selector: (variables) => `.${variables.cssClassName}__content`,
|
|
280
|
+
source: "html",
|
|
281
|
+
typeExpression: 'string & tags.MaxLength<1000> & tags.Default<"">',
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
attributeType: "string",
|
|
285
|
+
defaultValue: "left",
|
|
286
|
+
enumValues: [...ALIGNMENT_VALUES],
|
|
287
|
+
name: "alignment",
|
|
288
|
+
optional: true,
|
|
289
|
+
typeExpression: 'TextAlignment & tags.Default<"left">',
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
attributeType: "boolean",
|
|
293
|
+
defaultValue: true,
|
|
294
|
+
name: "isVisible",
|
|
295
|
+
optional: true,
|
|
296
|
+
typeExpression: "boolean & tags.Default<true>",
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
attributeType: "string",
|
|
300
|
+
defaultValue: "click",
|
|
301
|
+
enumValues: [...INTERACTIVE_MODE_VALUES],
|
|
302
|
+
name: "interactiveMode",
|
|
303
|
+
optional: true,
|
|
304
|
+
typeExpression: '("click" | "hover") & tags.Default<"click">',
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
attributeType: "string",
|
|
308
|
+
defaultValue: "none",
|
|
309
|
+
enumValues: [...ANIMATION_VALUES],
|
|
310
|
+
name: "animation",
|
|
311
|
+
optional: true,
|
|
312
|
+
typeExpression: '("none" | "bounce" | "pulse" | "shake" | "flip") & tags.Default<"none">',
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
attributeType: "number",
|
|
316
|
+
constraints: {
|
|
317
|
+
minimum: 0,
|
|
318
|
+
typeTag: "uint32",
|
|
319
|
+
},
|
|
320
|
+
defaultValue: 0,
|
|
321
|
+
name: "clickCount",
|
|
322
|
+
optional: true,
|
|
323
|
+
typeExpression: 'number & tags.Minimum<0> & tags.Type<"uint32"> & tags.Default<0>',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
attributeType: "boolean",
|
|
327
|
+
defaultValue: false,
|
|
328
|
+
name: "isAnimating",
|
|
329
|
+
optional: true,
|
|
330
|
+
typeExpression: "boolean & tags.Default<false>",
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
attributeType: "boolean",
|
|
334
|
+
defaultValue: true,
|
|
335
|
+
name: "showCounter",
|
|
336
|
+
optional: true,
|
|
337
|
+
typeExpression: "boolean & tags.Default<true>",
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
attributeType: "number",
|
|
341
|
+
constraints: {
|
|
342
|
+
minimum: 0,
|
|
343
|
+
typeTag: "uint32",
|
|
344
|
+
},
|
|
345
|
+
defaultValue: 10,
|
|
346
|
+
name: "maxClicks",
|
|
347
|
+
optional: true,
|
|
348
|
+
typeExpression: 'number & tags.Minimum<0> & tags.Type<"uint32"> & tags.Default<10>',
|
|
349
|
+
},
|
|
350
|
+
];
|
|
351
|
+
const PERSISTENCE_ATTRIBUTE_SPECS = [
|
|
352
|
+
{
|
|
353
|
+
attributeType: "string",
|
|
354
|
+
constraints: {
|
|
355
|
+
maxLength: 250,
|
|
356
|
+
minLength: 1,
|
|
357
|
+
},
|
|
358
|
+
defaultValue: (variables) => `${variables.title} persistence block`,
|
|
359
|
+
name: "content",
|
|
360
|
+
optional: false,
|
|
361
|
+
selector: (variables) => `.${variables.cssClassName}__content`,
|
|
362
|
+
source: "html",
|
|
363
|
+
typeExpression: (variables) => `string & tags.MinLength<1> & tags.MaxLength<250> & tags.Default<${quote(`${variables.title} persistence block`)}>`,
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
attributeType: "string",
|
|
367
|
+
defaultValue: "left",
|
|
368
|
+
enumValues: [...ALIGNMENT_VALUES],
|
|
369
|
+
name: "alignment",
|
|
370
|
+
optional: true,
|
|
371
|
+
typeExpression: 'TextAlignment & tags.Default<"left">',
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
attributeType: "boolean",
|
|
375
|
+
defaultValue: true,
|
|
376
|
+
name: "isVisible",
|
|
377
|
+
optional: true,
|
|
378
|
+
typeExpression: "boolean & tags.Default<true>",
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
attributeType: "boolean",
|
|
382
|
+
defaultValue: true,
|
|
383
|
+
name: "showCount",
|
|
384
|
+
optional: true,
|
|
385
|
+
typeExpression: "boolean & tags.Default<true>",
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
attributeType: "string",
|
|
389
|
+
constraints: {
|
|
390
|
+
maxLength: 40,
|
|
391
|
+
minLength: 1,
|
|
392
|
+
},
|
|
393
|
+
defaultValue: "Persist Count",
|
|
394
|
+
name: "buttonLabel",
|
|
395
|
+
optional: true,
|
|
396
|
+
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<40> & tags.Default<"Persist Count">',
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
attributeType: "string",
|
|
400
|
+
blockJsonDefaultValue: "",
|
|
401
|
+
constraints: {
|
|
402
|
+
maxLength: 100,
|
|
403
|
+
minLength: 1,
|
|
404
|
+
},
|
|
405
|
+
manifestDefaultValue: "primary",
|
|
406
|
+
name: "resourceKey",
|
|
407
|
+
optional: true,
|
|
408
|
+
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<100> & tags.Default<"primary">',
|
|
409
|
+
},
|
|
410
|
+
];
|
|
411
|
+
const COMPOUND_PARENT_BASE_ATTRIBUTE_SPECS = [
|
|
412
|
+
{
|
|
413
|
+
attributeType: "string",
|
|
414
|
+
constraints: {
|
|
415
|
+
maxLength: 80,
|
|
416
|
+
minLength: 1,
|
|
417
|
+
},
|
|
418
|
+
defaultValue: (variables) => variables.title,
|
|
419
|
+
name: "heading",
|
|
420
|
+
optional: false,
|
|
421
|
+
selector: (variables) => `.${variables.cssClassName}__heading`,
|
|
422
|
+
source: "html",
|
|
423
|
+
typeExpression: (variables) => `string & tags.MinLength<1> & tags.MaxLength<80> & tags.Default<${quote(variables.title)}>`,
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
attributeType: "string",
|
|
427
|
+
constraints: {
|
|
428
|
+
maxLength: 180,
|
|
429
|
+
minLength: 1,
|
|
430
|
+
},
|
|
431
|
+
defaultValue: "Add and reorder internal items inside this compound block.",
|
|
432
|
+
name: "intro",
|
|
433
|
+
optional: true,
|
|
434
|
+
selector: (variables) => `.${variables.cssClassName}__intro`,
|
|
435
|
+
source: "html",
|
|
436
|
+
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<180> & tags.Default<"Add and reorder internal items inside this compound block.">',
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
attributeType: "boolean",
|
|
440
|
+
defaultValue: true,
|
|
441
|
+
name: "showDividers",
|
|
442
|
+
optional: true,
|
|
443
|
+
typeExpression: "boolean & tags.Default<true>",
|
|
444
|
+
},
|
|
445
|
+
];
|
|
446
|
+
const COMPOUND_PARENT_PERSISTENCE_ATTRIBUTE_SPECS = [
|
|
447
|
+
{
|
|
448
|
+
attributeType: "boolean",
|
|
449
|
+
defaultValue: true,
|
|
450
|
+
name: "showCount",
|
|
451
|
+
optional: true,
|
|
452
|
+
typeExpression: "boolean & tags.Default<true>",
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
attributeType: "string",
|
|
456
|
+
constraints: {
|
|
457
|
+
maxLength: 40,
|
|
458
|
+
minLength: 1,
|
|
459
|
+
},
|
|
460
|
+
defaultValue: "Persist Count",
|
|
461
|
+
name: "buttonLabel",
|
|
462
|
+
optional: true,
|
|
463
|
+
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<40> & tags.Default<"Persist Count">',
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
attributeType: "string",
|
|
467
|
+
blockJsonDefaultValue: "",
|
|
468
|
+
constraints: {
|
|
469
|
+
maxLength: 100,
|
|
470
|
+
minLength: 1,
|
|
471
|
+
},
|
|
472
|
+
manifestDefaultValue: "primary",
|
|
473
|
+
name: "resourceKey",
|
|
474
|
+
optional: true,
|
|
475
|
+
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<100> & tags.Default<"primary">',
|
|
476
|
+
},
|
|
477
|
+
];
|
|
478
|
+
const COMPOUND_CHILD_ATTRIBUTE_SPECS = [
|
|
479
|
+
{
|
|
480
|
+
attributeType: "string",
|
|
481
|
+
constraints: {
|
|
482
|
+
maxLength: 80,
|
|
483
|
+
minLength: 1,
|
|
484
|
+
},
|
|
485
|
+
defaultValue: ({ childTitle }) => childTitle,
|
|
486
|
+
name: "title",
|
|
487
|
+
optional: false,
|
|
488
|
+
selector: ({ childCssClassName }) => childCssClassName ? `.${childCssClassName}__title` : null,
|
|
489
|
+
source: ({ childCssClassName }) => childCssClassName ? "html" : null,
|
|
490
|
+
typeExpression: ({ childTitle }) => `string & tags.MinLength<1> & tags.MaxLength<80> & tags.Default<${quote(childTitle)}>`,
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
attributeType: "string",
|
|
494
|
+
constraints: {
|
|
495
|
+
maxLength: 280,
|
|
496
|
+
minLength: 1,
|
|
497
|
+
},
|
|
498
|
+
defaultValue: ({ bodyPlaceholder }) => bodyPlaceholder,
|
|
499
|
+
name: "body",
|
|
500
|
+
optional: false,
|
|
501
|
+
selector: ({ childCssClassName }) => childCssClassName ? `.${childCssClassName}__body` : null,
|
|
502
|
+
source: ({ childCssClassName }) => childCssClassName ? "html" : null,
|
|
503
|
+
typeExpression: ({ bodyPlaceholder }) => `string & tags.MinLength<1> & tags.MaxLength<280> & tags.Default<${quote(bodyPlaceholder)}>`,
|
|
504
|
+
},
|
|
505
|
+
];
|
|
178
506
|
function buildBasicAttributes() {
|
|
179
|
-
return
|
|
180
|
-
defineStringAttribute({
|
|
181
|
-
constraints: {
|
|
182
|
-
maxLength: 1000,
|
|
183
|
-
},
|
|
184
|
-
defaultValue: "",
|
|
185
|
-
description: describe("Main block content"),
|
|
186
|
-
name: "content",
|
|
187
|
-
optional: false,
|
|
188
|
-
typeExpression: 'string & tags.MaxLength<1000> & tags.Default<"">',
|
|
189
|
-
}),
|
|
190
|
-
defineStringAttribute({
|
|
191
|
-
defaultValue: "left",
|
|
192
|
-
description: describe("Alignment"),
|
|
193
|
-
enumValues: [...BASIC_ALIGNMENT_VALUES],
|
|
194
|
-
name: "alignment",
|
|
195
|
-
optional: true,
|
|
196
|
-
typeExpression: 'TextAlignment & tags.Default<"left">',
|
|
197
|
-
}),
|
|
198
|
-
defineBooleanAttribute({
|
|
199
|
-
defaultValue: true,
|
|
200
|
-
description: describe("Visibility toggle"),
|
|
201
|
-
name: "isVisible",
|
|
202
|
-
optional: true,
|
|
203
|
-
typeExpression: "boolean & tags.Default<true>",
|
|
204
|
-
}),
|
|
205
|
-
defineStringAttribute({
|
|
206
|
-
constraints: {
|
|
207
|
-
maxLength: 100,
|
|
208
|
-
},
|
|
209
|
-
defaultValue: "",
|
|
210
|
-
description: describe("Custom CSS class"),
|
|
211
|
-
name: "className",
|
|
212
|
-
optional: true,
|
|
213
|
-
typeExpression: 'string & tags.MaxLength<100> & tags.Default<"">',
|
|
214
|
-
}),
|
|
215
|
-
defineStringAttribute({
|
|
216
|
-
constraints: {
|
|
217
|
-
format: "uuid",
|
|
218
|
-
},
|
|
219
|
-
description: describe("Generated runtime ID"),
|
|
220
|
-
name: "id",
|
|
221
|
-
optional: true,
|
|
222
|
-
typeExpression: 'string & tags.Format<"uuid">',
|
|
223
|
-
}),
|
|
224
|
-
defineNumberAttribute({
|
|
225
|
-
constraints: {
|
|
226
|
-
typeTag: "uint32",
|
|
227
|
-
},
|
|
228
|
-
defaultValue: 1,
|
|
229
|
-
description: describe("Block version for migrations"),
|
|
230
|
-
name: "schemaVersion",
|
|
231
|
-
optional: true,
|
|
232
|
-
typeExpression: 'number & tags.Type<"uint32"> & tags.Default<1>',
|
|
233
|
-
}),
|
|
234
|
-
];
|
|
507
|
+
return buildAttributesFromSpecs(BASIC_ATTRIBUTE_SPECS, undefined);
|
|
235
508
|
}
|
|
236
509
|
function buildInteractivityAttributes(variables) {
|
|
237
|
-
return
|
|
238
|
-
defineStringAttribute({
|
|
239
|
-
constraints: {
|
|
240
|
-
maxLength: 1000,
|
|
241
|
-
},
|
|
242
|
-
defaultValue: "",
|
|
243
|
-
name: "content",
|
|
244
|
-
optional: false,
|
|
245
|
-
selector: `.${variables.cssClassName}__content`,
|
|
246
|
-
source: "html",
|
|
247
|
-
typeExpression: 'string & tags.MaxLength<1000> & tags.Default<"">',
|
|
248
|
-
}),
|
|
249
|
-
defineStringAttribute({
|
|
250
|
-
defaultValue: "left",
|
|
251
|
-
enumValues: [...ALIGNMENT_VALUES],
|
|
252
|
-
name: "alignment",
|
|
253
|
-
optional: true,
|
|
254
|
-
typeExpression: 'TextAlignment & tags.Default<"left">',
|
|
255
|
-
}),
|
|
256
|
-
defineBooleanAttribute({
|
|
257
|
-
defaultValue: true,
|
|
258
|
-
name: "isVisible",
|
|
259
|
-
optional: true,
|
|
260
|
-
typeExpression: "boolean & tags.Default<true>",
|
|
261
|
-
}),
|
|
262
|
-
defineStringAttribute({
|
|
263
|
-
defaultValue: "click",
|
|
264
|
-
enumValues: [...INTERACTIVE_MODE_VALUES],
|
|
265
|
-
name: "interactiveMode",
|
|
266
|
-
optional: true,
|
|
267
|
-
typeExpression: '("click" | "hover") & tags.Default<"click">',
|
|
268
|
-
}),
|
|
269
|
-
defineStringAttribute({
|
|
270
|
-
defaultValue: "none",
|
|
271
|
-
enumValues: [...ANIMATION_VALUES],
|
|
272
|
-
name: "animation",
|
|
273
|
-
optional: true,
|
|
274
|
-
typeExpression: '("none" | "bounce" | "pulse" | "shake" | "flip") & tags.Default<"none">',
|
|
275
|
-
}),
|
|
276
|
-
defineNumberAttribute({
|
|
277
|
-
constraints: {
|
|
278
|
-
minimum: 0,
|
|
279
|
-
typeTag: "uint32",
|
|
280
|
-
},
|
|
281
|
-
defaultValue: 0,
|
|
282
|
-
name: "clickCount",
|
|
283
|
-
optional: true,
|
|
284
|
-
typeExpression: 'number & tags.Minimum<0> & tags.Type<"uint32"> & tags.Default<0>',
|
|
285
|
-
}),
|
|
286
|
-
defineBooleanAttribute({
|
|
287
|
-
defaultValue: false,
|
|
288
|
-
name: "isAnimating",
|
|
289
|
-
optional: true,
|
|
290
|
-
typeExpression: "boolean & tags.Default<false>",
|
|
291
|
-
}),
|
|
292
|
-
defineBooleanAttribute({
|
|
293
|
-
defaultValue: true,
|
|
294
|
-
name: "showCounter",
|
|
295
|
-
optional: true,
|
|
296
|
-
typeExpression: "boolean & tags.Default<true>",
|
|
297
|
-
}),
|
|
298
|
-
defineNumberAttribute({
|
|
299
|
-
constraints: {
|
|
300
|
-
minimum: 0,
|
|
301
|
-
typeTag: "uint32",
|
|
302
|
-
},
|
|
303
|
-
defaultValue: 10,
|
|
304
|
-
name: "maxClicks",
|
|
305
|
-
optional: true,
|
|
306
|
-
typeExpression: 'number & tags.Minimum<0> & tags.Type<"uint32"> & tags.Default<10>',
|
|
307
|
-
}),
|
|
308
|
-
];
|
|
510
|
+
return buildAttributesFromSpecs(INTERACTIVITY_ATTRIBUTE_SPECS, variables);
|
|
309
511
|
}
|
|
310
512
|
function buildPersistenceAttributes(variables) {
|
|
311
|
-
return
|
|
312
|
-
defineStringAttribute({
|
|
313
|
-
constraints: {
|
|
314
|
-
maxLength: 250,
|
|
315
|
-
minLength: 1,
|
|
316
|
-
},
|
|
317
|
-
defaultValue: `${variables.title} persistence block`,
|
|
318
|
-
name: "content",
|
|
319
|
-
optional: false,
|
|
320
|
-
selector: `.${variables.cssClassName}__content`,
|
|
321
|
-
source: "html",
|
|
322
|
-
typeExpression: `string & tags.MinLength<1> & tags.MaxLength<250> & tags.Default<${quote(`${variables.title} persistence block`)}>`,
|
|
323
|
-
}),
|
|
324
|
-
defineStringAttribute({
|
|
325
|
-
defaultValue: "left",
|
|
326
|
-
enumValues: [...ALIGNMENT_VALUES],
|
|
327
|
-
name: "alignment",
|
|
328
|
-
optional: true,
|
|
329
|
-
typeExpression: 'TextAlignment & tags.Default<"left">',
|
|
330
|
-
}),
|
|
331
|
-
defineBooleanAttribute({
|
|
332
|
-
defaultValue: true,
|
|
333
|
-
name: "isVisible",
|
|
334
|
-
optional: true,
|
|
335
|
-
typeExpression: "boolean & tags.Default<true>",
|
|
336
|
-
}),
|
|
337
|
-
defineBooleanAttribute({
|
|
338
|
-
defaultValue: true,
|
|
339
|
-
name: "showCount",
|
|
340
|
-
optional: true,
|
|
341
|
-
typeExpression: "boolean & tags.Default<true>",
|
|
342
|
-
}),
|
|
343
|
-
defineStringAttribute({
|
|
344
|
-
constraints: {
|
|
345
|
-
maxLength: 40,
|
|
346
|
-
minLength: 1,
|
|
347
|
-
},
|
|
348
|
-
defaultValue: "Persist Count",
|
|
349
|
-
name: "buttonLabel",
|
|
350
|
-
optional: true,
|
|
351
|
-
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<40> & tags.Default<"Persist Count">',
|
|
352
|
-
}),
|
|
353
|
-
defineStringAttribute({
|
|
354
|
-
blockJsonDefaultValue: "",
|
|
355
|
-
constraints: {
|
|
356
|
-
maxLength: 100,
|
|
357
|
-
minLength: 1,
|
|
358
|
-
},
|
|
359
|
-
manifestDefaultValue: "primary",
|
|
360
|
-
name: "resourceKey",
|
|
361
|
-
optional: true,
|
|
362
|
-
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<100> & tags.Default<"primary">',
|
|
363
|
-
}),
|
|
364
|
-
];
|
|
513
|
+
return buildAttributesFromSpecs(PERSISTENCE_ATTRIBUTE_SPECS, variables);
|
|
365
514
|
}
|
|
366
515
|
function buildCompoundParentAttributes(variables) {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
defaultValue: variables.title,
|
|
374
|
-
name: "heading",
|
|
375
|
-
optional: false,
|
|
376
|
-
selector: `.${variables.cssClassName}__heading`,
|
|
377
|
-
source: "html",
|
|
378
|
-
typeExpression: `string & tags.MinLength<1> & tags.MaxLength<80> & tags.Default<${quote(variables.title)}>`,
|
|
379
|
-
}),
|
|
380
|
-
defineStringAttribute({
|
|
381
|
-
constraints: {
|
|
382
|
-
maxLength: 180,
|
|
383
|
-
minLength: 1,
|
|
384
|
-
},
|
|
385
|
-
defaultValue: "Add and reorder internal items inside this compound block.",
|
|
386
|
-
name: "intro",
|
|
387
|
-
optional: true,
|
|
388
|
-
selector: `.${variables.cssClassName}__intro`,
|
|
389
|
-
source: "html",
|
|
390
|
-
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<180> & tags.Default<"Add and reorder internal items inside this compound block.">',
|
|
391
|
-
}),
|
|
392
|
-
defineBooleanAttribute({
|
|
393
|
-
defaultValue: true,
|
|
394
|
-
name: "showDividers",
|
|
395
|
-
optional: true,
|
|
396
|
-
typeExpression: "boolean & tags.Default<true>",
|
|
397
|
-
}),
|
|
398
|
-
];
|
|
399
|
-
if (variables.compoundPersistenceEnabled === "true") {
|
|
400
|
-
attributes.push(defineBooleanAttribute({
|
|
401
|
-
defaultValue: true,
|
|
402
|
-
name: "showCount",
|
|
403
|
-
optional: true,
|
|
404
|
-
typeExpression: "boolean & tags.Default<true>",
|
|
405
|
-
}), defineStringAttribute({
|
|
406
|
-
constraints: {
|
|
407
|
-
maxLength: 40,
|
|
408
|
-
minLength: 1,
|
|
409
|
-
},
|
|
410
|
-
defaultValue: "Persist Count",
|
|
411
|
-
name: "buttonLabel",
|
|
412
|
-
optional: true,
|
|
413
|
-
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<40> & tags.Default<"Persist Count">',
|
|
414
|
-
}), defineStringAttribute({
|
|
415
|
-
blockJsonDefaultValue: "",
|
|
416
|
-
constraints: {
|
|
417
|
-
maxLength: 100,
|
|
418
|
-
minLength: 1,
|
|
419
|
-
},
|
|
420
|
-
manifestDefaultValue: "primary",
|
|
421
|
-
name: "resourceKey",
|
|
422
|
-
optional: true,
|
|
423
|
-
typeExpression: 'string & tags.MinLength<1> & tags.MaxLength<100> & tags.Default<"primary">',
|
|
424
|
-
}));
|
|
425
|
-
}
|
|
426
|
-
return attributes;
|
|
516
|
+
return buildAttributesFromSpecs(variables.compoundPersistenceEnabled === "true"
|
|
517
|
+
? [
|
|
518
|
+
...COMPOUND_PARENT_BASE_ATTRIBUTE_SPECS,
|
|
519
|
+
...COMPOUND_PARENT_PERSISTENCE_ATTRIBUTE_SPECS,
|
|
520
|
+
]
|
|
521
|
+
: COMPOUND_PARENT_BASE_ATTRIBUTE_SPECS, variables);
|
|
427
522
|
}
|
|
428
523
|
function buildCompoundChildAttributes(bodyPlaceholder = DEFAULT_COMPOUND_CHILD_BODY_PLACEHOLDER, childTitle, childCssClassName) {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
: null;
|
|
435
|
-
return [
|
|
436
|
-
defineStringAttribute({
|
|
437
|
-
constraints: {
|
|
438
|
-
maxLength: 80,
|
|
439
|
-
minLength: 1,
|
|
440
|
-
},
|
|
441
|
-
defaultValue: childTitle,
|
|
442
|
-
name: "title",
|
|
443
|
-
optional: false,
|
|
444
|
-
selector: titleSelector,
|
|
445
|
-
source: titleSelector ? "html" : null,
|
|
446
|
-
typeExpression: `string & tags.MinLength<1> & tags.MaxLength<80> & tags.Default<${quote(childTitle)}>`,
|
|
447
|
-
}),
|
|
448
|
-
defineStringAttribute({
|
|
449
|
-
constraints: {
|
|
450
|
-
maxLength: 280,
|
|
451
|
-
minLength: 1,
|
|
452
|
-
},
|
|
453
|
-
defaultValue: bodyPlaceholder,
|
|
454
|
-
name: "body",
|
|
455
|
-
optional: false,
|
|
456
|
-
selector: bodySelector,
|
|
457
|
-
source: bodySelector ? "html" : null,
|
|
458
|
-
typeExpression: `string & tags.MinLength<1> & tags.MaxLength<280> & tags.Default<${quote(bodyPlaceholder)}>`,
|
|
459
|
-
}),
|
|
460
|
-
];
|
|
524
|
+
return buildAttributesFromSpecs(COMPOUND_CHILD_ATTRIBUTE_SPECS, {
|
|
525
|
+
bodyPlaceholder,
|
|
526
|
+
childCssClassName,
|
|
527
|
+
childTitle,
|
|
528
|
+
});
|
|
461
529
|
}
|
|
462
530
|
function buildBasicTypesSource(variables, attributes) {
|
|
463
531
|
return emitTypesModule({
|
|
@@ -8,27 +8,27 @@ export declare const MANIFEST_DOCUMENT_WRAPPER_TEMPLATE = "import rawCurrentMani
|
|
|
8
8
|
export declare const MANIFEST_DEFAULTS_DOCUMENT_WRAPPER_TEMPLATE = "import rawCurrentManifest from './typia.manifest.json';\nimport { defineManifestDefaultsDocument } from '@wp-typia/block-runtime/defaults';\n\nconst currentManifest = defineManifestDefaultsDocument(rawCurrentManifest);\n\nexport default currentManifest;\n";
|
|
9
9
|
export declare const BASIC_EDIT_TEMPLATE = "/**\n * Editor component for {{title}} Block\n */\n\nimport type { BlockEditProps } from '@wp-typia/block-types/blocks/registration';\nimport {\n InspectorControls,\n RichText,\n useBlockProps,\n} from '@wordpress/block-editor';\nimport { Notice, PanelBody, TextControl } from '@wordpress/components';\nimport { __ } from '@wordpress/i18n';\nimport currentManifest from './manifest-document';\nimport {\n InspectorFromManifest,\n useEditorFields,\n useTypedAttributeUpdater,\n} from '@wp-typia/block-runtime/inspector';\nimport { {{pascalCase}}Attributes } from './types';\nimport {\n sanitize{{pascalCase}}Attributes,\n validate{{pascalCase}}Attributes,\n} from './validators';\nimport { useTypiaValidation } from './hooks';\n\ntype EditProps = BlockEditProps<{{pascalCase}}Attributes>;\n\nconst validationErrorItemStyle = { color: '#cc1818', fontSize: '12px' };\nconst validationListStyle = { margin: 0, paddingLeft: '1em' };\n\nfunction Edit({ attributes, setAttributes }: EditProps) {\n const isVisible = attributes.isVisible !== false;\n const blockProps = useBlockProps({\n className: `{{cssClassName}}${isVisible ? '' : ' is-hidden'}`,\n });\n const editorFields = useEditorFields(currentManifest, {\n hidden: ['id', 'schemaVersion'],\n manual: ['content'],\n labels: {\n alignment: __('Alignment', '{{textDomain}}'),\n className: __('CSS Class', '{{textDomain}}'),\n content: __('Content', '{{textDomain}}'),\n isVisible: __('Visible', '{{textDomain}}'),\n },\n });\n const classNameField = editorFields.getField('className');\n const { errorMessages, isValid } = useTypiaValidation(\n attributes,\n validate{{pascalCase}}Attributes\n );\n const validateEditorUpdate = (nextAttributes: {{pascalCase}}Attributes) => {\n try {\n return {\n data: sanitize{{pascalCase}}Attributes(nextAttributes),\n errors: [],\n isValid: true as const,\n };\n } catch {\n return validate{{pascalCase}}Attributes(nextAttributes);\n }\n };\n const { updateField } = useTypedAttributeUpdater(\n attributes,\n setAttributes,\n validateEditorUpdate\n );\n\n return (\n <>\n <InspectorControls>\n <InspectorFromManifest\n attributes={attributes}\n fieldLookup={editorFields}\n onChange={updateField}\n paths={['alignment', 'isVisible']}\n title={__('Settings', '{{textDomain}}')}\n >\n <TextControl\n label={__('Content', '{{textDomain}}')}\n value={attributes.content || ''}\n onChange={(value) => updateField('content', value)}\n help={__('Mirrors the main block content.', '{{textDomain}}')}\n />\n\n <TextControl\n label={classNameField?.label || __('CSS Class', '{{textDomain}}')}\n value={attributes.className || ''}\n onChange={(value) => updateField('className', value)}\n help={__('Add an optional CSS class name.', '{{textDomain}}')}\n />\n </InspectorFromManifest>\n\n {!isValid && (\n <PanelBody title={__('Validation Errors', '{{textDomain}}')} initialOpen>\n {errorMessages.map((error, index) => (\n <div key={index} style={validationErrorItemStyle}>\n \u2022 {error}\n </div>\n ))}\n </PanelBody>\n )}\n </InspectorControls>\n\n <div {...blockProps}>\n <div className=\"{{cssClassName}}__content\">\n <RichText\n tagName=\"p\"\n value={attributes.content || ''}\n onChange={(value) => updateField('content', value)}\n placeholder={__('Add your content...', '{{textDomain}}')}\n />\n </div>\n {!isValid && (\n <Notice status=\"error\" isDismissible={false}>\n <p>\n <strong>{__('Validation Errors', '{{textDomain}}')}</strong>\n </p>\n <ul style={validationListStyle}>\n {errorMessages.map((error, index) => (\n <li key={index}>{error}</li>\n ))}\n </ul>\n </Notice>\n )}\n </div>\n </>\n );\n}\n\nexport default Edit;\n";
|
|
10
10
|
export declare const BASIC_SAVE_TEMPLATE = "/**\n * Save/Frontend component for {{title}} Block\n */\n\nimport { RichText, useBlockProps } from '@wordpress/block-editor';\nimport { {{pascalCase}}Attributes } from './types';\n\ninterface SaveProps {\n attributes: {{pascalCase}}Attributes;\n}\n\nexport default function Save({ attributes }: SaveProps) {\n const isVisible = attributes.isVisible !== false;\n const blockProps = useBlockProps.save({\n className: `{{cssClassName}}${isVisible ? '' : ' is-hidden'}`,\n hidden: isVisible ? undefined : true,\n 'aria-hidden': isVisible ? undefined : 'true',\n });\n\n return (\n <div {...blockProps}>\n <div\n className=\"{{cssClassName}}__content\"\n data-align={attributes.alignment || 'left'}\n >\n <RichText.Content tagName=\"p\" value={attributes.content} />\n </div>\n </div>\n );\n}\n";
|
|
11
|
-
export declare const BASIC_INDEX_TEMPLATE = "/**\n * WordPress {{title}} Block\n *\n * Typia-powered type-safe block\n */\n\nimport {
|
|
11
|
+
export declare const BASIC_INDEX_TEMPLATE = "/**\n * WordPress {{title}} Block\n *\n * Typia-powered type-safe block\n */\n\nimport {\n registerScaffoldBlockType,\n type BlockConfiguration,\n} from '@wp-typia/block-types/blocks/registration';\nimport type { BlockSupports } from '@wp-typia/block-types/blocks/supports';\nimport { __ } from '@wordpress/i18n';\nimport {\n buildScaffoldBlockRegistration,\n parseScaffoldBlockMetadata,\n} from '@wp-typia/block-runtime/blocks';\n\n// Import components\nimport Edit from './edit';\nimport Save from './save';\nimport metadata from './block-metadata';\nimport './editor.scss';\nimport './style.scss';\n\n// Import types\nimport { {{pascalCase}}Attributes } from './types';\nimport { validators } from './validators';\n\nconst scaffoldSupports = {\n html: false,\n multiple: true,\n align: ['wide', 'full'],\n} satisfies BlockSupports;\n\n// Register the block\nconst registration = buildScaffoldBlockRegistration(\n parseScaffoldBlockMetadata<BlockConfiguration<{{pascalCase}}Attributes>>(metadata),\n {\n supports: scaffoldSupports,\n example: {\n attributes: validators.random(),\n },\n edit: Edit,\n save: Save,\n }\n);\n\nregisterScaffoldBlockType(registration.name, registration.settings);\n";
|
|
12
12
|
export declare const BASIC_VALIDATORS_TEMPLATE = "import typia from 'typia';\nimport currentManifest from \"./manifest-defaults-document\";\nimport { {{pascalCase}}Attributes, {{pascalCase}}ValidationResult } from \"./types\";\nimport { generateBlockId } from \"@wp-typia/block-runtime/identifiers\";\nimport { createTemplateValidatorToolkit } from \"./validator-toolkit\";\n\nconst scaffoldValidators = createTemplateValidatorToolkit<{{pascalCase}}Attributes>({\n assert: typia.createAssert<{{pascalCase}}Attributes>(),\n clone: typia.misc.createClone<{{pascalCase}}Attributes>() as (\n value: {{pascalCase}}Attributes,\n ) => {{pascalCase}}Attributes,\n is: typia.createIs<{{pascalCase}}Attributes>(),\n manifest: currentManifest,\n prune: typia.misc.createPrune<{{pascalCase}}Attributes>(),\n random: typia.createRandom<{{pascalCase}}Attributes>() as (\n ...args: unknown[]\n ) => {{pascalCase}}Attributes,\n finalize: (normalized) => ({\n ...normalized,\n id: normalized.id && normalized.id.length > 0 ? normalized.id : generateBlockId(),\n }),\n validate: typia.createValidate<{{pascalCase}}Attributes>(),\n});\n\nexport const validate{{pascalCase}}Attributes =\n scaffoldValidators.validateAttributes as (\n attributes: unknown,\n ) => {{pascalCase}}ValidationResult;\n\nexport const validators = scaffoldValidators.validators;\n\nexport const sanitize{{pascalCase}}Attributes =\n scaffoldValidators.sanitizeAttributes as (\n attributes: Partial<{{pascalCase}}Attributes>,\n ) => {{pascalCase}}Attributes;\n\nexport const createAttributeUpdater = scaffoldValidators.createAttributeUpdater;\n";
|
|
13
13
|
export declare const INTERACTIVITY_EDIT_TEMPLATE = "import type { BlockEditProps } from '@wp-typia/block-types/blocks/registration';\nimport { __ } from '@wordpress/i18n';\nimport { useBlockProps, InspectorControls, RichText, BlockControls, AlignmentToolbar } from '@wordpress/block-editor';\nimport { PanelBody, RangeControl, Button, Notice } from '@wordpress/components';\nimport { useState } from '@wordpress/element';\nimport currentManifest from './manifest-document';\nimport {\n InspectorFromManifest,\n useEditorFields,\n useTypedAttributeUpdater,\n} from '@wp-typia/block-runtime/inspector';\nimport type { {{pascalCase}}Attributes } from './types';\nimport {\n sanitize{{pascalCase}}Attributes,\n validate{{pascalCase}}Attributes,\n} from './validators';\nimport { useTypiaValidation } from './hooks';\n\ntype EditProps = BlockEditProps<{{pascalCase}}Attributes>;\n\nconst actionButtonRowStyle = { display: 'flex', gap: '8px', marginTop: '16px' };\nconst validationListStyle = { margin: 0, paddingLeft: '1em' };\n\nexport default function Edit({ attributes, setAttributes, isSelected }: EditProps) {\n const [isPreviewing, setIsPreviewing] = useState(false);\n const editorFields = useEditorFields(currentManifest, {\n manual: ['content', 'clickCount', 'maxClicks'],\n labels: {\n alignment: __('Alignment', '{{textDomain}}'),\n animation: __('Animation', '{{textDomain}}'),\n interactiveMode: __('Interactive Mode', '{{textDomain}}'),\n isVisible: __('Visible', '{{textDomain}}'),\n showCounter: __('Show Counter', '{{textDomain}}'),\n },\n });\n const { errorMessages, isValid } = useTypiaValidation(\n attributes,\n validate{{pascalCase}}Attributes,\n );\n const validateEditorUpdate = (nextAttributes: {{pascalCase}}Attributes) => {\n try {\n return {\n data: sanitize{{pascalCase}}Attributes(nextAttributes),\n errors: [],\n isValid: true as const,\n };\n } catch {\n return validate{{pascalCase}}Attributes(nextAttributes);\n }\n };\n const { updateField } = useTypedAttributeUpdater(\n attributes,\n setAttributes,\n validateEditorUpdate\n );\n const alignmentValue = editorFields.getStringValue(\n attributes,\n 'alignment',\n 'left'\n ) as NonNullable<{{pascalCase}}Attributes['alignment']>;\n const clickCount = attributes.clickCount ?? 0;\n const isVisible = editorFields.getBooleanValue(\n attributes,\n 'isVisible',\n true\n );\n const isAnimating = attributes.isAnimating ?? false;\n const maxClicks = attributes.maxClicks ?? 0;\n const showCounter = editorFields.getBooleanValue(\n attributes,\n 'showCounter',\n true\n );\n const interactiveMode = editorFields.getStringValue(\n attributes,\n 'interactiveMode',\n 'click'\n ) as NonNullable<{{pascalCase}}Attributes['interactiveMode']>;\n const animation = editorFields.getStringValue(\n attributes,\n 'animation',\n 'none'\n ) as NonNullable<{{pascalCase}}Attributes['animation']>;\n\n const blockProps = useBlockProps({\n className: `{{cssClassName}} {{cssClassName}}--${interactiveMode}`,\n 'data-wp-interactive': '{{slugKebabCase}}',\n 'data-wp-context': JSON.stringify({\n clicks: clickCount,\n isAnimating,\n isVisible,\n animation,\n maxClicks,\n })\n });\n const previewContentStyle = { textAlign: alignmentValue };\n const progressBarStyle = { width: `${(clickCount / maxClicks) * 100}%` };\n\n const resetCounter = () => {\n updateField('clickCount', 0);\n updateField('isAnimating', false);\n };\n\n const testAnimation = () => {\n updateField('isAnimating', true);\n setTimeout(() => {\n updateField('isAnimating', false);\n }, 1000);\n };\n\n return (\n <>\n <BlockControls>\n <AlignmentToolbar\n value={alignmentValue}\n onChange={(value) => updateField('alignment', (value || alignmentValue) as NonNullable<{{pascalCase}}Attributes['alignment']>)}\n />\n </BlockControls>\n\n <InspectorControls>\n <InspectorFromManifest\n attributes={attributes}\n fieldLookup={editorFields}\n onChange={updateField}\n paths={['alignment', 'interactiveMode', 'animation', 'showCounter', 'isVisible']}\n title={__('Interactive Settings', '{{textDomain}}')}\n />\n\n <PanelBody title={__('Counter Settings', '{{textDomain}}')}>\n <RangeControl\n label={__('Max Clicks', '{{textDomain}}')}\n value={maxClicks}\n onChange={(value) => updateField('maxClicks', value ?? maxClicks)}\n min={0}\n max={100}\n help={__('Set to 0 for unlimited clicks', '{{textDomain}}')}\n />\n\n <div style={actionButtonRowStyle}>\n <Button\n variant=\"secondary\"\n onClick={resetCounter}\n isSmall\n >\n {__('Reset Counter', '{{textDomain}}')}\n </Button>\n <Button\n variant=\"secondary\"\n onClick={testAnimation}\n isSmall\n >\n {__('Test Animation', '{{textDomain}}')}\n </Button>\n </div>\n </PanelBody>\n\n {!isValid && (\n <PanelBody title={__('Validation Errors', '{{textDomain}}')} initialOpen>\n {errorMessages.map((error, index) => (\n <Notice key={index} status=\"error\" isDismissible={false}>\n {error}\n </Notice>\n ))}\n </PanelBody>\n )}\n\n {isSelected && (\n <PanelBody title={__('Preview', '{{textDomain}}')}>\n <Button\n variant={isPreviewing ? 'primary' : 'secondary'}\n onClick={() => setIsPreviewing(!isPreviewing)}\n aria-pressed={isPreviewing}\n isSmall\n >\n {isPreviewing\n ? __('Disable Preview Mode', '{{textDomain}}')\n : __('Enable Preview Mode', '{{textDomain}}')}\n </Button>\n\n {clickCount > 0 && (\n <Notice status=\"info\" isDismissible={false}>\n {__('Current clicks:', '{{textDomain}}')} {clickCount}\n {maxClicks > 0 && ` / ${maxClicks}`}\n </Notice>\n )}\n </PanelBody>\n )}\n </InspectorControls>\n\n <div {...blockProps}>\n <div\n className={`{{cssClassName}}__content ${isAnimating ? 'is-animating' : ''}`}\n style={previewContentStyle}\n data-wp-on--click={isPreviewing ? 'actions.handleClick' : undefined}\n data-wp-on--mouseenter={isPreviewing && interactiveMode === 'hover' ? 'actions.handleMouseEnter' : undefined}\n data-wp-on--mouseleave={isPreviewing && interactiveMode === 'hover' ? 'actions.handleMouseLeave' : undefined}\n >\n <RichText\n tagName=\"p\"\n value={attributes.content}\n onChange={(value) => updateField('content', value)}\n placeholder={__( {{titleJson}} + ' \u2013 click me to interact!', '{{textDomain}}')}\n />\n\n {!isValid && (\n <Notice status=\"error\" isDismissible={false}>\n <p>\n <strong>{__('Validation Errors', '{{textDomain}}')}</strong>\n </p>\n <ul style={validationListStyle}>\n {errorMessages.map((error, index) => (\n <li key={index}>{error}</li>\n ))}\n </ul>\n </Notice>\n )}\n\n {showCounter && (\n <div className=\"{{cssClassName}}__counter\">\n <span className=\"{{cssClassName}}__counter-label\">\n {__('Clicks:', '{{textDomain}}')}\n </span>\n <span\n className=\"{{cssClassName}}__counter-value\"\n data-wp-text=\"state.clicks\"\n >\n {clickCount}\n </span>\n </div>\n )}\n\n {maxClicks > 0 && (\n <div className=\"{{cssClassName}}__progress\">\n <div\n className=\"{{cssClassName}}__progress-bar\"\n style={progressBarStyle}\n data-wp-style--width=\"state.progress + '%'\"\n />\n </div>\n )}\n\n {animation !== 'none' && (\n <div\n className={`{{cssClassName}}__animation ${isAnimating ? 'is-active' : ''}`}\n data-wp-class--is-active=\"state.isAnimating\"\n >\n {animation}\n </div>\n )}\n </div>\n </div>\n </>\n );\n}\n";
|
|
14
14
|
export declare const INTERACTIVITY_SAVE_TEMPLATE = "import { useBlockProps, RichText } from '@wordpress/block-editor';\nimport { __ } from '@wordpress/i18n';\nimport type { {{pascalCase}}Attributes } from './types';\n\nexport default function Save({ attributes }: { attributes: {{pascalCase}}Attributes }) {\n const clickCount = attributes.clickCount ?? 0;\n const interactiveMode = attributes.interactiveMode ?? 'click';\n const animation = attributes.animation ?? 'none';\n const isAnimating = attributes.isAnimating ?? false;\n const isVisible = attributes.isVisible ?? true;\n const maxClicks = attributes.maxClicks ?? 0;\n const showCounter = attributes.showCounter ?? true;\n const contentStyle = { textAlign: attributes.alignment };\n const blockProps = useBlockProps.save({\n className: `{{cssClassName}} {{cssClassName}}--${interactiveMode}`,\n 'data-wp-interactive': '{{slugKebabCase}}',\n 'data-wp-context': JSON.stringify({\n clicks: clickCount,\n isAnimating,\n isVisible,\n animation,\n maxClicks,\n })\n });\n\n return (\n <div {...blockProps}>\n <div\n className={`{{cssClassName}}__content ${isAnimating ? 'is-animating' : ''}`}\n style={contentStyle}\n data-wp-on--click=\"actions.handleClick\"\n data-wp-on--mouseenter={interactiveMode === 'hover' ? 'actions.handleMouseEnter' : undefined}\n data-wp-on--mouseleave={interactiveMode === 'hover' ? 'actions.handleMouseLeave' : undefined}\n data-wp-bind--hidden=\"!state.isVisible\"\n >\n <RichText.Content\n tagName=\"p\"\n value={attributes.content}\n className=\"{{cssClassName}}__text\"\n />\n\n {showCounter && (\n <div\n className=\"{{cssClassName}}__counter\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n <span className=\"{{cssClassName}}__counter-label\">\n { __( 'Clicks:', '{{textDomain}}' ) }\n </span>\n <span\n className=\"{{cssClassName}}__counter-value\"\n data-wp-text=\"state.clicks\"\n >\n {clickCount}\n </span>\n </div>\n )}\n\n {maxClicks > 0 && (\n <div className=\"{{cssClassName}}__progress\">\n <div\n className=\"{{cssClassName}}__progress-bar\"\n role=\"progressbar\"\n aria-label={ __( 'Click progress', '{{textDomain}}' ) }\n aria-valuemin={0}\n aria-valuemax={maxClicks}\n aria-valuenow={Math.min(clickCount, maxClicks)}\n data-wp-bind--aria-valuenow=\"state.clampedClicks\"\n data-wp-style--width=\"state.progress + '%'\"\n />\n </div>\n )}\n\n <div\n className={`{{cssClassName}}__animation ${animation}`}\n aria-hidden=\"true\"\n data-wp-class--is-active=\"state.isAnimating\"\n />\n\n {maxClicks > 0 && (\n <div\n className=\"{{cssClassName}}__completion\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n data-wp-bind--hidden=\"!state.isComplete\"\n >\n { __( '\uD83C\uDF89 Complete!', '{{textDomain}}' ) }\n </div>\n )}\n\n <button\n className=\"{{cssClassName}}__reset\"\n data-wp-on--click=\"actions.reset\"\n aria-label={ __( 'Reset counter', '{{textDomain}}' ) }\n >\n <span aria-hidden=\"true\">\u21BB</span>\n <span className=\"screen-reader-text\">\n { __( 'Reset counter', '{{textDomain}}' ) }\n </span>\n </button>\n </div>\n </div>\n );\n}\n";
|
|
15
|
-
export declare const INTERACTIVITY_INDEX_TEMPLATE = "import {
|
|
15
|
+
export declare const INTERACTIVITY_INDEX_TEMPLATE = "import {\n registerScaffoldBlockType,\n type BlockConfiguration,\n} from '@wp-typia/block-types/blocks/registration';\nimport type { BlockSupports } from '@wp-typia/block-types/blocks/supports';\nimport {\n buildScaffoldBlockRegistration,\n parseScaffoldBlockMetadata,\n} from '@wp-typia/block-runtime/blocks';\n\nimport Edit from './edit';\nimport Save from './save';\nimport metadata from './block-metadata';\nimport './editor.scss';\nimport './style.scss';\n\nimport type { {{pascalCase}}Attributes } from './types';\n\nconst scaffoldSupports = {\n html: false,\n align: true,\n anchor: true,\n className: true,\n interactivity: true,\n} satisfies BlockSupports;\n\nconst registration = buildScaffoldBlockRegistration(\n parseScaffoldBlockMetadata<BlockConfiguration<{{pascalCase}}Attributes>>(metadata),\n {\n supports: scaffoldSupports,\n edit: Edit,\n save: Save,\n }\n);\n\nregisterScaffoldBlockType(registration.name, registration.settings);\n";
|
|
16
16
|
export declare const INTERACTIVITY_SCRIPT_TEMPLATE = "/**\n * WordPress Interactivity API implementation for {{title}} block\n */\nimport { store, getContext, getElement, withSyncEvent } from '@wordpress/interactivity';\nimport type { {{pascalCase}}Context } from './types';\n\nfunction getBlockContext() {\n return getContext<{{pascalCase}}Context>();\n}\n\n// Store configuration\nstore('{{slugKebabCase}}', {\n // State - reactive data that updates the UI\n state: {\n get clicks() {\n return getBlockContext().clicks;\n },\n get isAnimating() {\n return getBlockContext().isAnimating;\n },\n get isVisible() {\n return getBlockContext().isVisible;\n },\n get progress() {\n const context = getBlockContext();\n const clampedClicks =\n context.maxClicks > 0\n ? Math.min(context.clicks, context.maxClicks)\n : context.clicks;\n return context.maxClicks > 0 ? (clampedClicks / context.maxClicks) * 100 : 0;\n },\n get clampedClicks() {\n const context = getBlockContext();\n return context.maxClicks > 0\n ? Math.min(context.clicks, context.maxClicks)\n : context.clicks;\n },\n get isComplete() {\n const context = getBlockContext();\n return context.clicks >= context.maxClicks && context.maxClicks > 0;\n }\n },\n\n // Actions - user interactions\n actions: {\n // Handle block click\n handleClick: () => {\n const context = getBlockContext();\n const { ref } = getElement();\n\n if (!ref) {\n return;\n }\n\n if (context.maxClicks > 0 && context.clicks >= context.maxClicks) {\n return;\n }\n\n const previousClicks = context.clicks;\n\n // Increment click counter\n context.clicks += 1;\n\n // Trigger animation\n if (context.animation !== 'none') {\n context.isAnimating = true;\n setTimeout(() => {\n context.isAnimating = false;\n }, 1000);\n }\n\n // Emit custom event\n ref.dispatchEvent(new CustomEvent('{{slugKebabCase}}:click', {\n detail: { clicks: context.clicks }\n }));\n\n // Check if max clicks reached\n if (context.maxClicks > 0 && previousClicks < context.maxClicks && context.clicks === context.maxClicks) {\n ref.dispatchEvent(new CustomEvent('{{slugKebabCase}}:complete', {\n detail: { totalClicks: context.clicks }\n }));\n }\n },\n\n // Handle hover events\n handleMouseEnter: () => {\n const context = getBlockContext();\n if (context.animation === 'none') return;\n context.isAnimating = true;\n },\n\n handleMouseLeave: () => {\n const context = getBlockContext();\n if (context.animation === 'none') return;\n context.isAnimating = false;\n },\n\n // Reset counter\n reset: withSyncEvent((event: Event) => {\n event.stopPropagation();\n const context = getBlockContext();\n context.clicks = 0;\n context.isAnimating = false;\n })\n }\n});\n";
|
|
17
17
|
export declare const INTERACTIVITY_VALIDATORS_TEMPLATE = "import typia from 'typia';\nimport currentManifest from \"./manifest-defaults-document\";\nimport { {{pascalCase}}Attributes, {{pascalCase}}ValidationResult } from \"./types\";\nimport { createTemplateValidatorToolkit } from \"./validator-toolkit\";\n\nconst scaffoldValidators = createTemplateValidatorToolkit<{{pascalCase}}Attributes>({\n assert: typia.createAssert<{{pascalCase}}Attributes>(),\n clone: typia.misc.createClone<{{pascalCase}}Attributes>() as (\n value: {{pascalCase}}Attributes,\n ) => {{pascalCase}}Attributes,\n is: typia.createIs<{{pascalCase}}Attributes>(),\n manifest: currentManifest,\n prune: typia.misc.createPrune<{{pascalCase}}Attributes>(),\n random: typia.createRandom<{{pascalCase}}Attributes>() as (\n ...args: unknown[]\n ) => {{pascalCase}}Attributes,\n validate: typia.createValidate<{{pascalCase}}Attributes>(),\n});\n\nexport const validate{{pascalCase}}Attributes =\n scaffoldValidators.validateAttributes as (\n attributes: unknown,\n ) => {{pascalCase}}ValidationResult;\n\nexport const validators = scaffoldValidators.validators;\n\nexport const sanitize{{pascalCase}}Attributes =\n scaffoldValidators.sanitizeAttributes as (\n attributes: Partial<{{pascalCase}}Attributes>,\n ) => {{pascalCase}}Attributes;\n\n/**\n * Runtime type guard for checking if an object is {{pascalCase}}Attributes.\n */\nexport const is{{pascalCase}}Attributes = (obj: unknown): obj is {{pascalCase}}Attributes => {\n return validators.is(obj);\n};\n\nexport const createAttributeUpdater = scaffoldValidators.createAttributeUpdater;\n";
|
|
18
18
|
export declare const PERSISTENCE_EDIT_TEMPLATE = "import type { BlockEditProps } from '@wp-typia/block-types/blocks/registration';\nimport { __ } from '@wordpress/i18n';\nimport {\n\tAlignmentToolbar,\n\tBlockControls,\n\tInspectorControls,\n\tRichText,\n\tuseBlockProps,\n} from '@wordpress/block-editor';\nimport {\n\tNotice,\n\tPanelBody,\n\tTextControl,\n} from '@wordpress/components';\nimport currentManifest from './manifest-document';\nimport {\n\tInspectorFromManifest,\n\tuseEditorFields,\n\tuseTypedAttributeUpdater,\n} from '@wp-typia/block-runtime/inspector';\nimport type { {{pascalCase}}Attributes } from './types';\nimport {\n\tsanitize{{pascalCase}}Attributes,\n\tvalidate{{pascalCase}}Attributes,\n} from './validators';\nimport { useTypiaValidation } from './hooks';\n\ntype EditProps = BlockEditProps< {{pascalCase}}Attributes >;\n\nexport default function Edit( {\n\tattributes,\n\tsetAttributes,\n}: EditProps ) {\n\tconst editorFields = useEditorFields(\n\t\tcurrentManifest,\n\t\t{\n\t\t\tmanual: [ 'content', 'resourceKey' ],\n\t\t\tlabels: {\n\t\t\t\tbuttonLabel: __( 'Button Label', '{{textDomain}}' ),\n\t\t\t\tresourceKey: __( 'Resource Key', '{{textDomain}}' ),\n\t\t\t\tshowCount: __( 'Show Count', '{{textDomain}}' ),\n\t\t\t},\n\t\t}\n\t);\n\tconst { errorMessages, isValid } = useTypiaValidation(\n\t\tattributes,\n\t\tvalidate{{pascalCase}}Attributes\n\t);\n\tconst validateEditorUpdate = (\n\t\tnextAttributes: {{pascalCase}}Attributes\n\t) => {\n\t\ttry {\n\t\t\treturn {\n\t\t\t\tdata: sanitize{{pascalCase}}Attributes( nextAttributes ),\n\t\t\t\terrors: [],\n\t\t\t\tisValid: true as const,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn validate{{pascalCase}}Attributes( nextAttributes );\n\t\t}\n\t};\n\tconst { updateField } = useTypedAttributeUpdater(\n\t\tattributes,\n\t\tsetAttributes,\n\t\tvalidateEditorUpdate\n\t);\n\tconst alignmentValue = editorFields.getStringValue(\n\t\tattributes,\n\t\t'alignment',\n\t\t'left'\n\t);\n\tconst persistencePolicy = '{{persistencePolicy}}';\n\tconst persistencePolicyDescription = __(\n\t\t{{persistencePolicyDescriptionJson}},\n\t\t'{{textDomain}}'\n\t);\n\n\treturn (\n\t\t<>\n\t\t\t<BlockControls>\n\t\t\t\t<AlignmentToolbar\n\t\t\t\t\tvalue={ alignmentValue }\n\t\t\t\t\tonChange={ ( value ) =>\n\t\t\t\t\t\tupdateField(\n\t\t\t\t\t\t\t'alignment',\n\t\t\t\t\t\t\t( value || alignmentValue ) as NonNullable< {{pascalCase}}Attributes[ 'alignment' ] >\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t</BlockControls>\n\t\t\t<InspectorControls>\n\t\t\t\t<InspectorFromManifest\n\t\t\t\t\tattributes={ attributes }\n\t\t\t\t\tfieldLookup={ editorFields }\n\t\t\t\t\tonChange={ updateField }\n\t\t\t\t\tpaths={ [ 'alignment', 'isVisible', 'showCount', 'buttonLabel' ] }\n\t\t\t\t\ttitle={ __( 'Persistence Settings', '{{textDomain}}' ) }\n\t\t\t\t>\n\t\t\t\t\t<TextControl\n\t\t\t\t\t\tlabel={ __( 'Resource Key', '{{textDomain}}' ) }\n\t\t\t\t\t\tvalue={ attributes.resourceKey ?? '' }\n\t\t\t\t\t\tonChange={ ( value ) => updateField( 'resourceKey', value ) }\n\t\t\t\t\t\thelp={ __( 'Stable persisted identifier used by the storage-backed counter endpoint.', '{{textDomain}}' ) }\n\t\t\t\t\t/>\n\t\t\t\t\t<Notice status=\"info\" isDismissible={ false }>\n\t\t\t\t\t\t{ __( 'Storage mode: {{dataStorageMode}}', '{{textDomain}}' ) }\n\t\t\t\t\t</Notice>\n\t\t\t\t\t<Notice status=\"info\" isDismissible={ false }>\n\t\t\t\t\t\t{ __( 'Persistence policy: {{persistencePolicy}}', '{{textDomain}}' ) }\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\t{ persistencePolicyDescription }\n\t\t\t\t\t</Notice>\n\t\t\t\t\t<Notice status=\"info\" isDismissible={ false }>\n\t\t\t\t\t\t{ __( 'Render mode: dynamic. `render.php` bootstraps durable post context, while fresh session-only write data is loaded from the dedicated `/bootstrap` endpoint after hydration.', '{{textDomain}}' ) }\n\t\t\t\t\t</Notice>\n\t\t\t\t</InspectorFromManifest>\n\t\t\t\t{ ! isValid && (\n\t\t\t\t\t<PanelBody\n\t\t\t\t\t\ttitle={ __( 'Validation Errors', '{{textDomain}}' ) }\n\t\t\t\t\t\tinitialOpen\n\t\t\t\t\t>\n\t\t\t\t\t\t{ errorMessages.map( ( error, index ) => (\n\t\t\t\t\t\t\t<Notice key={ index } status=\"error\" isDismissible={ false }>\n\t\t\t\t\t\t\t\t{ error }\n\t\t\t\t\t\t\t</Notice>\n\t\t\t\t\t\t) ) }\n\t\t\t\t\t</PanelBody>\n\t\t\t\t) }\n\t\t\t</InspectorControls>\n\t\t\t<div\n\t\t\t\t{ ...useBlockProps( {\n\t\t\t\t\tclassName: '{{cssClassName}}',\n\t\t\t\t\tstyle: {\n\t\t\t\t\t\ttextAlign:\n\t\t\t\t\t\t\talignmentValue as NonNullable< {{pascalCase}}Attributes[ 'alignment' ] >,\n\t\t\t\t\t},\n\t\t\t\t} ) }\n\t\t\t>\n\t\t\t\t<RichText\n\t\t\t\t\ttagName=\"p\"\n\t\t\t\t\tvalue={ attributes.content }\n\t\t\t\t\tonChange={ ( value ) => updateField( 'content', value ) }\n\t\t\t\t\tplaceholder={ __( {{titleJson}} + ' persistence block', '{{textDomain}}' ) }\n\t\t\t\t/>\n\t\t\t\t<p className=\"{{cssClassName}}__meta\">\n\t\t\t\t\t{ __( 'Resource key:', '{{textDomain}}' ) } { attributes.resourceKey || '\u2014' }\n\t\t\t\t</p>\n\t\t\t\t<p className=\"{{cssClassName}}__meta\">\n\t\t\t\t\t{ __( 'Storage mode:', '{{textDomain}}' ) } {{dataStorageMode}}\n\t\t\t\t</p>\n\t\t\t\t<p className=\"{{cssClassName}}__meta\">\n\t\t\t\t\t{ __( 'Persistence policy:', '{{textDomain}}' ) } {{persistencePolicy}}\n\t\t\t\t</p>\n\t\t\t\t{ ! isValid && (\n\t\t\t\t\t<Notice status=\"error\" isDismissible={ false }>\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t{ errorMessages.map( ( error, index ) => <li key={ index }>{ error }</li> ) }\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</Notice>\n\t\t\t\t) }\n\t\t\t</div>\n\t\t</>\n\t);\n}\n";
|
|
19
|
-
export declare const PERSISTENCE_INDEX_TEMPLATE = "import {
|
|
19
|
+
export declare const PERSISTENCE_INDEX_TEMPLATE = "import {\n\tregisterScaffoldBlockType,\n\ttype BlockConfiguration,\n} from '@wp-typia/block-types/blocks/registration';\nimport {\n\tbuildScaffoldBlockRegistration,\n\tparseScaffoldBlockMetadata,\n} from '@wp-typia/block-runtime/blocks';\n\nimport Edit from './edit';\nimport Save from './save';\nimport metadata from './block-metadata';\nimport './style.scss';\n\nimport type { {{pascalCase}}Attributes } from './types';\n\nconst registration = buildScaffoldBlockRegistration(\n\tparseScaffoldBlockMetadata<BlockConfiguration< {{pascalCase}}Attributes >>( metadata ),\n\t{\n\t\tedit: Edit,\n\t\tsave: Save,\n\t}\n);\n\nregisterScaffoldBlockType(registration.name, registration.settings);\n";
|
|
20
20
|
export declare const PERSISTENCE_SAVE_TEMPLATE = "export default function Save() {\n\t// This block is intentionally server-rendered. PHP bootstraps post context,\n\t// storage-backed state, and write-policy data before the frontend hydrates.\n\treturn null;\n}\n";
|
|
21
21
|
export declare const PERSISTENCE_VALIDATORS_TEMPLATE = "import typia from 'typia';\nimport currentManifest from './manifest-defaults-document';\nimport type {\n\t{{pascalCase}}Attributes,\n\t{{pascalCase}}ValidationResult,\n} from './types';\nimport { generateResourceKey } from '@wp-typia/block-runtime/identifiers';\nimport { createTemplateValidatorToolkit } from './validator-toolkit';\n\nconst scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}Attributes >( {\n\tassert: typia.createAssert< {{pascalCase}}Attributes >(),\n\tclone: typia.misc.createClone< {{pascalCase}}Attributes >() as (\n\t\tvalue: {{pascalCase}}Attributes,\n\t) => {{pascalCase}}Attributes,\n\tis: typia.createIs< {{pascalCase}}Attributes >(),\n\tmanifest: currentManifest,\n\tprune: typia.misc.createPrune< {{pascalCase}}Attributes >(),\n\trandom: typia.createRandom< {{pascalCase}}Attributes >() as (\n\t\t...args: unknown[]\n\t) => {{pascalCase}}Attributes,\n\tfinalize: ( normalized ) => ( {\n\t\t...normalized,\n\t\tresourceKey:\n\t\t\tnormalized.resourceKey && normalized.resourceKey.length > 0\n\t\t\t\t? normalized.resourceKey\n\t\t\t\t: generateResourceKey( '{{slugKebabCase}}' ),\n\t} ),\n\tvalidate: typia.createValidate< {{pascalCase}}Attributes >(),\n} );\n\nexport const validators = scaffoldValidators.validators;\n\nexport const validate{{pascalCase}}Attributes =\n\tscaffoldValidators.validateAttributes as (\n\t\tattributes: unknown\n\t) => {{pascalCase}}ValidationResult;\n\nexport const sanitize{{pascalCase}}Attributes =\n\tscaffoldValidators.sanitizeAttributes as (\n\t\tattributes: Partial< {{pascalCase}}Attributes >\n\t) => {{pascalCase}}Attributes;\n\nexport const createAttributeUpdater = scaffoldValidators.createAttributeUpdater;\n";
|
|
22
22
|
export declare const PERSISTENCE_INTERACTIVITY_TEMPLATE = "import { getContext, store } from '@wordpress/interactivity';\nimport { generatePublicWriteRequestId } from '@wp-typia/block-runtime/identifiers';\n\nimport { fetchBootstrap, fetchState, writeState } from './api';\nimport type {\n\t{{pascalCase}}ClientState,\n\t{{pascalCase}}Context,\n\t{{pascalCase}}State,\n} from './types';\nimport type {\n\t{{pascalCase}}WriteStateRequest,\n} from './api-types';\n\nfunction hasExpiredPublicWriteToken(\n\texpiresAt?: number\n): boolean {\n\treturn (\n\t\ttypeof expiresAt === 'number' &&\n\t\texpiresAt > 0 &&\n\t\tDate.now() >= expiresAt * 1000\n\t);\n}\n\nfunction getWriteBlockedMessage(\n\tcontext: {{pascalCase}}Context\n): string {\n\treturn context.persistencePolicy === 'authenticated'\n\t\t? 'Sign in to persist this counter.'\n\t\t: 'Public writes are temporarily unavailable.';\n}\n\nconst BOOTSTRAP_MAX_ATTEMPTS = 3;\nconst BOOTSTRAP_RETRY_DELAYS_MS = [ 250, 500 ];\n\nasync function waitForBootstrapRetry( delayMs: number ): Promise< void > {\n\tawait new Promise( ( resolve ) => {\n\t\tsetTimeout( resolve, delayMs );\n\t} );\n}\n\nfunction getClientState(\n\tcontext: {{pascalCase}}Context\n): {{pascalCase}}ClientState {\n\tif ( context.client ) {\n\t\treturn context.client;\n\t}\n\n\tcontext.client = {\n\t\tbootstrapError: '',\n\t\twriteExpiry: 0,\n\t\twriteNonce: '',\n\t\twriteToken: '',\n\t};\n\n\treturn context.client;\n}\n\nfunction clearBootstrapError(\n\tcontext: {{pascalCase}}Context,\n\tclientState: {{pascalCase}}ClientState\n): void {\n\tif ( context.error === clientState.bootstrapError ) {\n\t\tcontext.error = '';\n\t}\n\tclientState.bootstrapError = '';\n}\n\nfunction setBootstrapError(\n\tcontext: {{pascalCase}}Context,\n\tclientState: {{pascalCase}}ClientState,\n\tmessage: string\n): void {\n\tclientState.bootstrapError = message;\n\tcontext.error = message;\n}\n\nconst { actions, state } = store( '{{slugKebabCase}}', {\n\tstate: {\n\t\tisHydrated: false,\n\t} as {{pascalCase}}State,\n\n\tactions: {\n\t\tasync loadState() {\n\t\t\tconst context = getContext< {{pascalCase}}Context >();\n\t\t\tif ( context.postId <= 0 || ! context.resourceKey ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcontext.isLoading = true;\n\t\t\tcontext.error = '';\n\n\t\t\ttry {\n\t\t\t\tconst result = await fetchState( {\n\t\t\t\t\tpostId: context.postId,\n\t\t\t\t\tresourceKey: context.resourceKey,\n\t\t\t\t}, {\n\t\t\t\t\ttransportTarget: 'frontend',\n\t\t\t\t} );\n\t\t\t\tif ( ! result.isValid || ! result.data ) {\n\t\t\t\t\tcontext.error = result.errors[ 0 ]?.expected ?? 'Unable to load counter';\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcontext.count = result.data.count;\n\t\t\t} catch ( error ) {\n\t\t\t\tcontext.error =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown loading error';\n\t\t\t} finally {\n\t\t\t\tcontext.isLoading = false;\n\t\t\t}\n\t\t},\n\t\tasync loadBootstrap() {\n\t\t\tconst context = getContext< {{pascalCase}}Context >();\n\t\t\tconst clientState = getClientState( context );\n\t\t\tif ( context.postId <= 0 || ! context.resourceKey ) {\n\t\t\t\tcontext.bootstrapReady = true;\n\t\t\t\tcontext.canWrite = false;\n\t\t\t\tclientState.bootstrapError = '';\n\t\t\t\tclientState.writeExpiry = 0;\n\t\t\t\tclientState.writeNonce = '';\n\t\t\t\tclientState.writeToken = '';\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcontext.isBootstrapping = true;\n\n\t\t\tlet bootstrapSucceeded = false;\n\t\t\tlet lastBootstrapError =\n\t\t\t\t'Unable to initialize write access';\n\t\t\tconst includePublicWriteCredentials = {{isPublicPersistencePolicy}};\n\t\t\tconst includeRestNonce = {{isAuthenticatedPersistencePolicy}};\n\n\t\t\tfor ( let attempt = 1; attempt <= BOOTSTRAP_MAX_ATTEMPTS; attempt += 1 ) {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await fetchBootstrap( {\n\t\t\t\t\t\tpostId: context.postId,\n\t\t\t\t\t\tresourceKey: context.resourceKey,\n\t\t\t\t\t}, {\n\t\t\t\t\t\ttransportTarget: 'frontend',\n\t\t\t\t\t} );\n\t\t\t\t\tif ( ! result.isValid || ! result.data ) {\n\t\t\t\t\t\tlastBootstrapError =\n\t\t\t\t\t\t\tresult.errors[ 0 ]?.expected ??\n\t\t\t\t\t\t\t'Unable to initialize write access';\n\t\t\t\t\t\tif ( attempt < BOOTSTRAP_MAX_ATTEMPTS ) {\n\t\t\t\t\t\t\tawait waitForBootstrapRetry(\n\t\t\t\t\t\t\t\tBOOTSTRAP_RETRY_DELAYS_MS[ attempt - 1 ] ?? 750\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tclientState.writeExpiry =\n\t\t\t\t\t\tincludePublicWriteCredentials &&\n\t\t\t\t\t\t'publicWriteExpiresAt' in result.data &&\n\t\t\t\t\t\ttypeof result.data.publicWriteExpiresAt === 'number' &&\n\t\t\t\t\t\tresult.data.publicWriteExpiresAt > 0\n\t\t\t\t\t\t\t? result.data.publicWriteExpiresAt\n\t\t\t\t\t\t\t: 0;\n\t\t\t\t\tclientState.writeToken =\n\t\t\t\t\t\tincludePublicWriteCredentials &&\n\t\t\t\t\t\t'publicWriteToken' in result.data &&\n\t\t\t\t\t\ttypeof result.data.publicWriteToken === 'string' &&\n\t\t\t\t\t\tresult.data.publicWriteToken.length > 0\n\t\t\t\t\t\t\t? result.data.publicWriteToken\n\t\t\t\t\t\t\t: '';\n\t\t\t\t\tclientState.writeNonce =\n\t\t\t\t\t\tincludeRestNonce &&\n\t\t\t\t\t\t'restNonce' in result.data &&\n\t\t\t\t\t\ttypeof result.data.restNonce === 'string' &&\n\t\t\t\t\t\tresult.data.restNonce.length > 0\n\t\t\t\t\t\t\t? result.data.restNonce\n\t\t\t\t\t\t\t: '';\n\t\t\t\t\tcontext.bootstrapReady = true;\n\t\t\t\t\tcontext.canWrite =\n\t\t\t\t\t\tresult.data.canWrite === true &&\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\tcontext.persistencePolicy === 'authenticated'\n\t\t\t\t\t\t\t\t? clientState.writeNonce.length > 0\n\t\t\t\t\t\t\t\t: clientState.writeToken.length > 0 &&\n\t\t\t\t\t\t\t\t\t! hasExpiredPublicWriteToken( clientState.writeExpiry )\n\t\t\t\t\t\t);\n\t\t\t\t\tclearBootstrapError( context, clientState );\n\t\t\t\t\tbootstrapSucceeded = true;\n\t\t\t\t\tbreak;\n\t\t\t\t} catch ( error ) {\n\t\t\t\t\tlastBootstrapError =\n\t\t\t\t\t\terror instanceof Error ? error.message : 'Unknown bootstrap error';\n\t\t\t\t\tif ( attempt < BOOTSTRAP_MAX_ATTEMPTS ) {\n\t\t\t\t\t\tawait waitForBootstrapRetry(\n\t\t\t\t\t\t\tBOOTSTRAP_RETRY_DELAYS_MS[ attempt - 1 ] ?? 750\n\t\t\t\t\t\t);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ! bootstrapSucceeded ) {\n\t\t\t\tcontext.bootstrapReady = false;\n\t\t\t\tcontext.canWrite = false;\n\t\t\t\tclientState.writeExpiry = 0;\n\t\t\t\tclientState.writeNonce = '';\n\t\t\t\tclientState.writeToken = '';\n\t\t\t\tsetBootstrapError( context, clientState, lastBootstrapError );\n\t\t\t}\n\t\t\tcontext.isBootstrapping = false;\n\t\t},\n\t\tasync increment() {\n\t\t\tconst context = getContext< {{pascalCase}}Context >();\n\t\t\tconst clientState = getClientState( context );\n\t\t\tif ( context.postId <= 0 || ! context.resourceKey ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ( ! context.bootstrapReady ) {\n\t\t\t\tawait actions.loadBootstrap();\n\t\t\t}\n\t\t\tif ( ! context.bootstrapReady ) {\n\t\t\t\tcontext.error = 'Write access is still initializing.';\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tcontext.persistencePolicy === 'public' &&\n\t\t\t\thasExpiredPublicWriteToken( clientState.writeExpiry )\n\t\t\t) {\n\t\t\t\tawait actions.loadBootstrap();\n\t\t\t}\n\t\t\tif (\n\t\t\t\tcontext.persistencePolicy === 'public' &&\n\t\t\t\thasExpiredPublicWriteToken( clientState.writeExpiry )\n\t\t\t) {\n\t\t\t\tcontext.canWrite = false;\n\t\t\t\tcontext.error = getWriteBlockedMessage( context );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ( ! context.canWrite ) {\n\t\t\t\tcontext.error = getWriteBlockedMessage( context );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcontext.isSaving = true;\n\t\t\tcontext.error = '';\n\n\t\t\ttry {\n\t\t\t\tconst request = {\n\t\t\t\t\tdelta: 1,\n\t\t\t\t\tpostId: context.postId,\n\t\t\t\t\tresourceKey: context.resourceKey,\n\t\t\t\t} as {{pascalCase}}WriteStateRequest;\n\t\t\t\tif ( {{isPublicPersistencePolicy}} ) {\n\t\t\t\t\trequest.publicWriteRequestId =\n\t\t\t\t\t\tgeneratePublicWriteRequestId() as {{pascalCase}}WriteStateRequest[ 'publicWriteRequestId' ];\n\t\t\t\t\tif ( clientState.writeToken.length > 0 ) {\n\t\t\t\t\t\trequest.publicWriteToken =\n\t\t\t\t\t\t\tclientState.writeToken as {{pascalCase}}WriteStateRequest[ 'publicWriteToken' ];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst result = await writeState( request, {\n\t\t\t\t\trestNonce:\n\t\t\t\t\t\tclientState.writeNonce.length > 0\n\t\t\t\t\t\t\t? clientState.writeNonce\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\ttransportTarget: 'frontend',\n\t\t\t\t} );\n\t\t\t\tif ( ! result.isValid || ! result.data ) {\n\t\t\t\t\tcontext.error = result.errors[ 0 ]?.expected ?? 'Unable to update counter';\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcontext.count = result.data.count;\n\t\t\t\tcontext.storage = result.data.storage;\n\t\t\t} catch ( error ) {\n\t\t\t\tcontext.error =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown update error';\n\t\t\t} finally {\n\t\t\t\tcontext.isSaving = false;\n\t\t\t}\n\t\t},\n\t},\n\n\tcallbacks: {\n\t\tinit() {\n\t\t\tconst context = getContext< {{pascalCase}}Context >();\n\t\t\tcontext.client = {\n\t\t\t\tbootstrapError: '',\n\t\t\t\twriteExpiry: 0,\n\t\t\t\twriteNonce: '',\n\t\t\t\twriteToken: '',\n\t\t\t};\n\t\t\tcontext.bootstrapReady = false;\n\t\t\tcontext.canWrite = false;\n\t\t\tcontext.count = 0;\n\t\t\tcontext.error = '';\n\t\t\tcontext.isBootstrapping = false;\n\t\t\tcontext.isLoading = false;\n\t\t\tcontext.isSaving = false;\n\t\t},\n\t\tmounted() {\n\t\t\tstate.isHydrated = true;\n\t\t\tif ( typeof document !== 'undefined' ) {\n\t\t\t\tdocument.documentElement.dataset[ '{{slugCamelCase}}Hydrated' ] = 'true';\n\t\t\t}\n\t\t\tvoid Promise.allSettled( [\n\t\t\t\tactions.loadState(),\n\t\t\t\tactions.loadBootstrap(),\n\t\t\t] );\n\t\t},\n\t},\n} );\n";
|
|
23
23
|
export declare const COMPOUND_PARENT_EDIT_TEMPLATE = "import type { BlockEditProps } from '@wp-typia/block-types/blocks/registration';\nimport { __ } from '@wordpress/i18n';\nimport {\n\tInspectorControls,\n\tInnerBlocks,\n\tRichText,\n\tuseBlockProps,\n} from '@wordpress/block-editor';\nimport { Notice, PanelBody, ToggleControl } from '@wordpress/components';\n\nimport {\n\tALLOWED_CHILD_BLOCKS,\n\tDEFAULT_CHILD_TEMPLATE,\n} from './children';\nimport { useTypiaValidation } from './hooks';\nimport type { {{pascalCase}}Attributes } from './types';\nimport {\n\tcreateAttributeUpdater,\n\tvalidate{{pascalCase}}Attributes,\n} from './validators';\n\ntype EditProps = BlockEditProps< {{pascalCase}}Attributes >;\n\nexport default function Edit( {\n\tattributes,\n\tsetAttributes,\n}: EditProps ) {\n\tconst { errorMessages, isValid } = useTypiaValidation(\n\t\tattributes,\n\t\tvalidate{{pascalCase}}Attributes\n\t);\n\tconst updateAttribute = createAttributeUpdater( attributes, setAttributes );\n\tconst blockProps = useBlockProps( {\n\t\tclassName: '{{cssClassName}}',\n\t} );\n\n\treturn (\n\t\t<>\n\t\t\t<InspectorControls>\n\t\t\t\t<PanelBody title={ __( 'Compound Settings', '{{textDomain}}' ) }>\n\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\tlabel={ __( 'Show dividers between items', '{{textDomain}}' ) }\n\t\t\t\t\t\tchecked={ attributes.showDividers ?? true }\n\t\t\t\t\t\tonChange={ ( value ) => updateAttribute( 'showDividers', value ) }\n\t\t\t\t\t/>\n\t\t\t\t</PanelBody>\n\t\t\t\t{ ! isValid && (\n\t\t\t\t\t<PanelBody title={ __( 'Validation Errors', '{{textDomain}}' ) } initialOpen>\n\t\t\t\t\t\t{ errorMessages.map( ( error, index ) => (\n\t\t\t\t\t\t\t<Notice key={ index } status=\"error\" isDismissible={ false }>\n\t\t\t\t\t\t\t\t{ error }\n\t\t\t\t\t\t\t</Notice>\n\t\t\t\t\t\t) ) }\n\t\t\t\t\t</PanelBody>\n\t\t\t\t) }\n\t\t\t</InspectorControls>\n\t\t\t<div { ...blockProps }>\n\t\t\t\t<RichText\n\t\t\t\t\ttagName=\"h3\"\n\t\t\t\t\tclassName=\"{{cssClassName}}__heading\"\n\t\t\t\t\tvalue={ attributes.heading }\n\t\t\t\t\tonChange={ ( heading ) => updateAttribute( 'heading', heading ) }\n\t\t\t\t\tplaceholder={ __( {{titleJson}}, '{{textDomain}}' ) }\n\t\t\t\t/>\n\t\t\t\t<RichText\n\t\t\t\t\ttagName=\"p\"\n\t\t\t\t\tclassName=\"{{cssClassName}}__intro\"\n\t\t\t\t\tvalue={ attributes.intro ?? '' }\n\t\t\t\t\tonChange={ ( intro ) => updateAttribute( 'intro', intro ) }\n\t\t\t\t\tplaceholder={ __(\n\t\t\t\t\t\t'Add and reorder internal items inside this compound block.',\n\t\t\t\t\t\t'{{textDomain}}'\n\t\t\t\t\t) }\n\t\t\t\t/>\n\t\t\t\t{ ! isValid && (\n\t\t\t\t\t<Notice status=\"error\" isDismissible={ false }>\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t{ errorMessages.map( ( error, index ) => <li key={ index }>{ error }</li> ) }\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</Notice>\n\t\t\t\t) }\n\t\t\t\t<div className=\"{{cssClassName}}__items\">\n\t\t\t\t\t<InnerBlocks\n\t\t\t\t\t\tallowedBlocks={ ALLOWED_CHILD_BLOCKS }\n\t\t\t\t\t\trenderAppender={ InnerBlocks.ButtonBlockAppender }\n\t\t\t\t\t\ttemplate={ DEFAULT_CHILD_TEMPLATE }\n\t\t\t\t\t\ttemplateLock={ false }\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</>\n\t);\n}\n";
|
|
24
24
|
export declare const COMPOUND_PARENT_SAVE_TEMPLATE = "import { InnerBlocks, RichText, useBlockProps } from '@wordpress/block-editor';\n\nimport type { {{pascalCase}}Attributes } from './types';\n\nexport default function Save( {\n\tattributes,\n}: {\n\tattributes: {{pascalCase}}Attributes;\n} ) {\n\treturn (\n\t\t<div\n\t\t\t{ ...useBlockProps.save( {\n\t\t\t\tclassName: '{{cssClassName}}',\n\t\t\t\t'data-show-dividers': ( attributes.showDividers ?? true ) ? 'true' : 'false',\n\t\t\t} ) }\n\t\t>\n\t\t\t<RichText.Content\n\t\t\t\ttagName=\"h3\"\n\t\t\t\tclassName=\"{{cssClassName}}__heading\"\n\t\t\t\tvalue={ attributes.heading }\n\t\t\t/>\n\t\t\t<RichText.Content\n\t\t\t\ttagName=\"p\"\n\t\t\t\tclassName=\"{{cssClassName}}__intro\"\n\t\t\t\tvalue={ attributes.intro ?? '' }\n\t\t\t/>\n\t\t\t<div className=\"{{cssClassName}}__items\">\n\t\t\t\t<InnerBlocks.Content />\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n";
|
|
25
|
-
export declare const COMPOUND_PARENT_INDEX_TEMPLATE = "import {
|
|
25
|
+
export declare const COMPOUND_PARENT_INDEX_TEMPLATE = "import {\n\tregisterScaffoldBlockType,\n\ttype BlockConfiguration,\n} from '@wp-typia/block-types/blocks/registration';\nimport {\n\tbuildScaffoldBlockRegistration,\n\tparseScaffoldBlockMetadata,\n} from '@wp-typia/block-runtime/blocks';\n\nimport Edit from './edit';\nimport Save from './save';\nimport metadata from './block-metadata';\nimport './style.scss';\n\nimport type { {{pascalCase}}Attributes } from './types';\n\nconst registration = buildScaffoldBlockRegistration(\n\tparseScaffoldBlockMetadata<BlockConfiguration< {{pascalCase}}Attributes >>( metadata ),\n\t{\n\t\tedit: Edit,\n\t\tsave: Save,\n\t}\n);\n\nregisterScaffoldBlockType(registration.name, registration.settings);\n";
|
|
26
26
|
export declare const COMPOUND_LOCAL_HOOKS_TEMPLATE = "export {\n\tformatValidationError,\n\tformatValidationErrors,\n\tuseTypiaValidation,\n} from '../../hooks';\n\nexport type {\n\tTypiaValidationError,\n\tValidationResult,\n\tValidationState,\n} from '../../hooks';\n";
|
|
27
27
|
export declare const COMPOUND_PARENT_VALIDATORS_TEMPLATE = "import typia from 'typia';\nimport currentManifest from './manifest-defaults-document';\nimport type {\n\t{{pascalCase}}Attributes,\n\t{{pascalCase}}ValidationResult,\n} from './types';\nimport { createTemplateValidatorToolkit } from '../../validator-toolkit';\n\nconst scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}Attributes >( {\n\tassert: typia.createAssert< {{pascalCase}}Attributes >(),\n\tclone: typia.misc.createClone< {{pascalCase}}Attributes >() as (\n\t\tvalue: {{pascalCase}}Attributes,\n\t) => {{pascalCase}}Attributes,\n\tis: typia.createIs< {{pascalCase}}Attributes >(),\n\tmanifest: currentManifest,\n\tprune: typia.misc.createPrune< {{pascalCase}}Attributes >(),\n\trandom: typia.createRandom< {{pascalCase}}Attributes >() as (\n\t\t...args: unknown[]\n\t) => {{pascalCase}}Attributes,\n\tvalidate: typia.createValidate< {{pascalCase}}Attributes >(),\n} );\n\nexport const validate{{pascalCase}}Attributes =\n\tscaffoldValidators.validateAttributes as (\n\t\tattributes: unknown\n\t) => {{pascalCase}}ValidationResult;\n\nexport const validators = scaffoldValidators.validators;\n\nexport const sanitize{{pascalCase}}Attributes =\n\tscaffoldValidators.sanitizeAttributes as (\n\t\tattributes: Partial< {{pascalCase}}Attributes >\n\t) => {{pascalCase}}Attributes;\n\nexport const createAttributeUpdater = scaffoldValidators.createAttributeUpdater;\n";
|
|
28
28
|
export declare const COMPOUND_CHILDREN_TEMPLATE = "import type { BlockTemplate } from '@wp-typia/block-types/blocks/registration';\n\nexport const DEFAULT_CHILD_BLOCK_NAME = '{{namespace}}/{{slugKebabCase}}-item';\n\nexport const ALLOWED_CHILD_BLOCKS = [\n\tDEFAULT_CHILD_BLOCK_NAME,\n\t// add-child: insert new allowed child block names here\n];\n\nexport const DEFAULT_CHILD_TEMPLATE: BlockTemplate = [\n\t[\n\t\tDEFAULT_CHILD_BLOCK_NAME,\n\t\t{\n\t\t\tbody: 'Add supporting details for the first internal item.',\n\t\t\ttitle: 'First Item',\n\t\t},\n\t],\n\t[\n\t\tDEFAULT_CHILD_BLOCK_NAME,\n\t\t{\n\t\t\tbody: 'Add supporting details for the second internal item.',\n\t\t\ttitle: 'Second Item',\n\t\t},\n\t],\n];\n";
|
|
29
29
|
export declare const COMPOUND_CHILD_EDIT_TEMPLATE = "import type { BlockEditProps } from '@wp-typia/block-types/blocks/registration';\nimport { RichText, useBlockProps } from '@wordpress/block-editor';\nimport { Notice } from '@wordpress/components';\nimport { __ } from '@wordpress/i18n';\n\nimport { useTypiaValidation } from './hooks';\nimport type { {{pascalCase}}ItemAttributes } from './types';\nimport {\n\tcreateAttributeUpdater,\n\tvalidate{{pascalCase}}ItemAttributes,\n} from './validators';\n\ntype EditProps = BlockEditProps< {{pascalCase}}ItemAttributes >;\n\nexport default function Edit( {\n\tattributes,\n\tsetAttributes,\n}: EditProps ) {\n\tconst updateAttribute = createAttributeUpdater( attributes, setAttributes );\n\tconst { errorMessages, isValid } = useTypiaValidation(\n\t\tattributes,\n\t\tvalidate{{pascalCase}}ItemAttributes\n\t);\n\n\treturn (\n\t\t<div { ...useBlockProps( { className: '{{compoundChildCssClassName}}' } ) }>\n\t\t\t<RichText\n\t\t\t\ttagName=\"h4\"\n\t\t\t\tclassName=\"{{compoundChildCssClassName}}__title\"\n\t\t\t\tvalue={ attributes.title ?? '' }\n\t\t\t\tonChange={ ( title ) => updateAttribute( 'title', title ) }\n\t\t\t\tplaceholder={ __( {{compoundChildTitleJson}}, '{{textDomain}}' ) }\n\t\t\t/>\n\t\t\t<RichText\n\t\t\t\ttagName=\"p\"\n\t\t\t\tclassName=\"{{compoundChildCssClassName}}__body\"\n\t\t\t\tvalue={ attributes.body ?? '' }\n\t\t\t\tonChange={ ( body ) => updateAttribute( 'body', body ) }\n\t\t\t\tplaceholder={ __( 'Add supporting details for this internal item.', '{{textDomain}}' ) }\n\t\t\t/>\n\t\t\t{ ! isValid && (\n\t\t\t\t<Notice status=\"error\" isDismissible={ false }>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t{ errorMessages.map( ( error, index ) => <li key={ index }>{ error }</li> ) }\n\t\t\t\t\t</ul>\n\t\t\t\t</Notice>\n\t\t\t) }\n\t\t</div>\n\t);\n}\n";
|
|
30
30
|
export declare const COMPOUND_CHILD_SAVE_TEMPLATE = "import { RichText, useBlockProps } from '@wordpress/block-editor';\n\nimport type { {{pascalCase}}ItemAttributes } from './types';\n\nexport default function Save( {\n\tattributes,\n}: {\n\tattributes: {{pascalCase}}ItemAttributes;\n} ) {\n\treturn (\n\t\t<div { ...useBlockProps.save( { className: '{{compoundChildCssClassName}}' } ) }>\n\t\t\t<RichText.Content\n\t\t\t\ttagName=\"h4\"\n\t\t\t\tclassName=\"{{compoundChildCssClassName}}__title\"\n\t\t\t\tvalue={ attributes.title }\n\t\t\t/>\n\t\t\t<RichText.Content\n\t\t\t\ttagName=\"p\"\n\t\t\t\tclassName=\"{{compoundChildCssClassName}}__body\"\n\t\t\t\tvalue={ attributes.body }\n\t\t\t/>\n\t\t</div>\n\t);\n}\n";
|
|
31
|
-
export declare const COMPOUND_CHILD_INDEX_TEMPLATE = "import {
|
|
31
|
+
export declare const COMPOUND_CHILD_INDEX_TEMPLATE = "import {\n\tregisterScaffoldBlockType,\n\ttype BlockConfiguration,\n} from '@wp-typia/block-types/blocks/registration';\nimport {\n\tbuildScaffoldBlockRegistration,\n\tparseScaffoldBlockMetadata,\n} from '@wp-typia/block-runtime/blocks';\n\nimport Edit from './edit';\nimport Save from './save';\nimport metadata from './block-metadata';\nimport '../{{slugKebabCase}}/style.scss';\n\nimport type { {{pascalCase}}ItemAttributes } from './types';\n\nconst registration = buildScaffoldBlockRegistration(\n\tparseScaffoldBlockMetadata<BlockConfiguration< {{pascalCase}}ItemAttributes >>( metadata ),\n\t{\n\t\tedit: Edit,\n\t\tsave: Save,\n\t}\n);\n\nregisterScaffoldBlockType(registration.name, registration.settings);\n";
|
|
32
32
|
export declare const COMPOUND_CHILD_VALIDATORS_TEMPLATE = "import typia from 'typia';\nimport currentManifest from './manifest-defaults-document';\nimport type {\n\t{{pascalCase}}ItemAttributes,\n\t{{pascalCase}}ItemValidationResult,\n} from './types';\nimport { createTemplateValidatorToolkit } from '../../validator-toolkit';\n\nconst scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}ItemAttributes >( {\n\tassert: typia.createAssert< {{pascalCase}}ItemAttributes >(),\n\tclone: typia.misc.createClone< {{pascalCase}}ItemAttributes >() as (\n\t\tvalue: {{pascalCase}}ItemAttributes,\n\t) => {{pascalCase}}ItemAttributes,\n\tis: typia.createIs< {{pascalCase}}ItemAttributes >(),\n\tmanifest: currentManifest,\n\tprune: typia.misc.createPrune< {{pascalCase}}ItemAttributes >(),\n\trandom: typia.createRandom< {{pascalCase}}ItemAttributes >() as (\n\t\t...args: unknown[]\n\t) => {{pascalCase}}ItemAttributes,\n\tvalidate: typia.createValidate< {{pascalCase}}ItemAttributes >(),\n} );\n\nexport const validate{{pascalCase}}ItemAttributes =\n\tscaffoldValidators.validateAttributes as (\n\t\tattributes: unknown\n\t) => {{pascalCase}}ItemValidationResult;\n\nexport const validators = scaffoldValidators.validators;\n\nexport const sanitize{{pascalCase}}ItemAttributes =\n\tscaffoldValidators.sanitizeAttributes as (\n\t\tattributes: Partial< {{pascalCase}}ItemAttributes >\n\t) => {{pascalCase}}ItemAttributes;\n\nexport const createAttributeUpdater = scaffoldValidators.createAttributeUpdater;\n";
|
|
33
33
|
export declare const COMPOUND_PERSISTENCE_PARENT_EDIT_TEMPLATE = "import type { BlockEditProps } from '@wp-typia/block-types/blocks/registration';\nimport { __ } from '@wordpress/i18n';\nimport {\n\tInspectorControls,\n\tInnerBlocks,\n\tRichText,\n\tuseBlockProps,\n} from '@wordpress/block-editor';\nimport {\n\tNotice,\n\tPanelBody,\n\tTextControl,\n\tToggleControl,\n} from '@wordpress/components';\n\nimport {\n\tALLOWED_CHILD_BLOCKS,\n\tDEFAULT_CHILD_TEMPLATE,\n} from './children';\nimport { useTypiaValidation } from './hooks';\nimport type { {{pascalCase}}Attributes } from './types';\nimport {\n\tcreateAttributeUpdater,\n\tvalidate{{pascalCase}}Attributes,\n} from './validators';\n\ntype EditProps = BlockEditProps< {{pascalCase}}Attributes >;\n\nexport default function Edit( {\n\tattributes,\n\tsetAttributes,\n}: EditProps ) {\n\tconst { errorMessages, isValid } = useTypiaValidation(\n\t\tattributes,\n\t\tvalidate{{pascalCase}}Attributes\n\t);\n\tconst updateAttribute = createAttributeUpdater( attributes, setAttributes );\n\tconst blockProps = useBlockProps( {\n\t\tclassName: '{{cssClassName}}',\n\t} );\n\n\treturn (\n\t\t<>\n\t\t\t<InspectorControls>\n\t\t\t\t<PanelBody title={ __( 'Compound Settings', '{{textDomain}}' ) }>\n\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\tlabel={ __( 'Show dividers between items', '{{textDomain}}' ) }\n\t\t\t\t\t\tchecked={ attributes.showDividers ?? true }\n\t\t\t\t\t\tonChange={ ( value ) => updateAttribute( 'showDividers', value ) }\n\t\t\t\t\t/>\n\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\tlabel={ __( 'Show persisted count', '{{textDomain}}' ) }\n\t\t\t\t\t\tchecked={ attributes.showCount ?? true }\n\t\t\t\t\t\tonChange={ ( value ) => updateAttribute( 'showCount', value ) }\n\t\t\t\t\t/>\n\t\t\t\t\t<TextControl\n\t\t\t\t\t\tlabel={ __( 'Button label', '{{textDomain}}' ) }\n\t\t\t\t\t\tvalue={ attributes.buttonLabel ?? 'Persist Count' }\n\t\t\t\t\t\tonChange={ ( buttonLabel ) => updateAttribute( 'buttonLabel', buttonLabel ) }\n\t\t\t\t\t/>\n\t\t\t\t\t<TextControl\n\t\t\t\t\t\tlabel={ __( 'Resource key', '{{textDomain}}' ) }\n\t\t\t\t\t\tvalue={ attributes.resourceKey ?? '' }\n\t\t\t\t\t\tonChange={ ( resourceKey ) => updateAttribute( 'resourceKey', resourceKey ) }\n\t\t\t\t\t\thelp={ __( 'Stable key used by the persisted counter endpoint.', '{{textDomain}}' ) }\n\t\t\t\t\t/>\n\t\t\t\t\t<Notice status=\"info\" isDismissible={ false }>\n\t\t\t\t\t\t{ __( 'Storage mode: {{dataStorageMode}}', '{{textDomain}}' ) }\n\t\t\t\t\t</Notice>\n\t\t\t\t\t<Notice status=\"info\" isDismissible={ false }>\n\t\t\t\t\t\t{ __( 'Persistence policy: {{persistencePolicy}}', '{{textDomain}}' ) }\n\t\t\t\t\t</Notice>\n\t\t\t\t</PanelBody>\n\t\t\t\t{ ! isValid && (\n\t\t\t\t\t<PanelBody title={ __( 'Validation Errors', '{{textDomain}}' ) } initialOpen>\n\t\t\t\t\t\t{ errorMessages.map( ( error, index ) => (\n\t\t\t\t\t\t\t<Notice key={ index } status=\"error\" isDismissible={ false }>\n\t\t\t\t\t\t\t\t{ error }\n\t\t\t\t\t\t\t</Notice>\n\t\t\t\t\t\t) ) }\n\t\t\t\t\t</PanelBody>\n\t\t\t\t) }\n\t\t\t</InspectorControls>\n\t\t\t<div { ...blockProps }>\n\t\t\t\t<RichText\n\t\t\t\t\ttagName=\"h3\"\n\t\t\t\t\tclassName=\"{{cssClassName}}__heading\"\n\t\t\t\t\tvalue={ attributes.heading }\n\t\t\t\t\tonChange={ ( heading ) => updateAttribute( 'heading', heading ) }\n\t\t\t\t\tplaceholder={ __( {{titleJson}}, '{{textDomain}}' ) }\n\t\t\t\t/>\n\t\t\t\t<RichText\n\t\t\t\t\ttagName=\"p\"\n\t\t\t\t\tclassName=\"{{cssClassName}}__intro\"\n\t\t\t\t\tvalue={ attributes.intro ?? '' }\n\t\t\t\t\tonChange={ ( intro ) => updateAttribute( 'intro', intro ) }\n\t\t\t\t\tplaceholder={ __(\n\t\t\t\t\t\t'Add and reorder internal items inside this compound block.',\n\t\t\t\t\t\t'{{textDomain}}'\n\t\t\t\t\t) }\n\t\t\t\t/>\n\t\t\t\t{ ! isValid && (\n\t\t\t\t\t<Notice status=\"error\" isDismissible={ false }>\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t{ errorMessages.map( ( error, index ) => <li key={ index }>{ error }</li> ) }\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</Notice>\n\t\t\t\t) }\n\t\t\t\t<p className=\"{{cssClassName}}__meta\">\n\t\t\t\t\t{ __( 'Resource key:', '{{textDomain}}' ) } { attributes.resourceKey || '\u2014' }\n\t\t\t\t</p>\n\t\t\t\t<div className=\"{{cssClassName}}__items\">\n\t\t\t\t\t<InnerBlocks\n\t\t\t\t\t\tallowedBlocks={ ALLOWED_CHILD_BLOCKS }\n\t\t\t\t\t\trenderAppender={ InnerBlocks.ButtonBlockAppender }\n\t\t\t\t\t\ttemplate={ DEFAULT_CHILD_TEMPLATE }\n\t\t\t\t\t\ttemplateLock={ false }\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</>\n\t);\n}\n";
|
|
34
34
|
export declare const COMPOUND_PERSISTENCE_PARENT_SAVE_TEMPLATE = "export default function Save() {\n\treturn null;\n}\n";
|
|
@@ -211,8 +211,10 @@ export const BASIC_INDEX_TEMPLATE = `/**
|
|
|
211
211
|
* Typia-powered type-safe block
|
|
212
212
|
*/
|
|
213
213
|
|
|
214
|
-
import {
|
|
215
|
-
|
|
214
|
+
import {
|
|
215
|
+
registerScaffoldBlockType,
|
|
216
|
+
type BlockConfiguration,
|
|
217
|
+
} from '@wp-typia/block-types/blocks/registration';
|
|
216
218
|
import type { BlockSupports } from '@wp-typia/block-types/blocks/supports';
|
|
217
219
|
import { __ } from '@wordpress/i18n';
|
|
218
220
|
import {
|
|
@@ -250,7 +252,7 @@ const registration = buildScaffoldBlockRegistration(
|
|
|
250
252
|
}
|
|
251
253
|
);
|
|
252
254
|
|
|
253
|
-
|
|
255
|
+
registerScaffoldBlockType(registration.name, registration.settings);
|
|
254
256
|
`;
|
|
255
257
|
export const BASIC_VALIDATORS_TEMPLATE = `import typia from 'typia';
|
|
256
258
|
import currentManifest from "./manifest-defaults-document";
|
|
@@ -653,8 +655,10 @@ export default function Save({ attributes }: { attributes: {{pascalCase}}Attribu
|
|
|
653
655
|
);
|
|
654
656
|
}
|
|
655
657
|
`;
|
|
656
|
-
export const INTERACTIVITY_INDEX_TEMPLATE = `import {
|
|
657
|
-
|
|
658
|
+
export const INTERACTIVITY_INDEX_TEMPLATE = `import {
|
|
659
|
+
registerScaffoldBlockType,
|
|
660
|
+
type BlockConfiguration,
|
|
661
|
+
} from '@wp-typia/block-types/blocks/registration';
|
|
658
662
|
import type { BlockSupports } from '@wp-typia/block-types/blocks/supports';
|
|
659
663
|
import {
|
|
660
664
|
buildScaffoldBlockRegistration,
|
|
@@ -686,7 +690,7 @@ const registration = buildScaffoldBlockRegistration(
|
|
|
686
690
|
}
|
|
687
691
|
);
|
|
688
692
|
|
|
689
|
-
|
|
693
|
+
registerScaffoldBlockType(registration.name, registration.settings);
|
|
690
694
|
`;
|
|
691
695
|
export const INTERACTIVITY_SCRIPT_TEMPLATE = `/**
|
|
692
696
|
* WordPress Interactivity API implementation for {{title}} block
|
|
@@ -1000,8 +1004,10 @@ export default function Edit( {
|
|
|
1000
1004
|
);
|
|
1001
1005
|
}
|
|
1002
1006
|
`;
|
|
1003
|
-
export const PERSISTENCE_INDEX_TEMPLATE = `import {
|
|
1004
|
-
|
|
1007
|
+
export const PERSISTENCE_INDEX_TEMPLATE = `import {
|
|
1008
|
+
\tregisterScaffoldBlockType,
|
|
1009
|
+
\ttype BlockConfiguration,
|
|
1010
|
+
} from '@wp-typia/block-types/blocks/registration';
|
|
1005
1011
|
import {
|
|
1006
1012
|
buildScaffoldBlockRegistration,
|
|
1007
1013
|
parseScaffoldBlockMetadata,
|
|
@@ -1022,10 +1028,7 @@ const registration = buildScaffoldBlockRegistration(
|
|
|
1022
1028
|
}
|
|
1023
1029
|
);
|
|
1024
1030
|
|
|
1025
|
-
|
|
1026
|
-
registration.name,
|
|
1027
|
-
registration.settings
|
|
1028
|
-
);
|
|
1031
|
+
registerScaffoldBlockType(registration.name, registration.settings);
|
|
1029
1032
|
`;
|
|
1030
1033
|
export const PERSISTENCE_SAVE_TEMPLATE = `export default function Save() {
|
|
1031
1034
|
// This block is intentionally server-rendered. PHP bootstraps post context,
|
|
@@ -1513,8 +1516,10 @@ export default function Save( {
|
|
|
1513
1516
|
);
|
|
1514
1517
|
}
|
|
1515
1518
|
`;
|
|
1516
|
-
export const COMPOUND_PARENT_INDEX_TEMPLATE = `import {
|
|
1517
|
-
|
|
1519
|
+
export const COMPOUND_PARENT_INDEX_TEMPLATE = `import {
|
|
1520
|
+
\tregisterScaffoldBlockType,
|
|
1521
|
+
\ttype BlockConfiguration,
|
|
1522
|
+
} from '@wp-typia/block-types/blocks/registration';
|
|
1518
1523
|
import {
|
|
1519
1524
|
buildScaffoldBlockRegistration,
|
|
1520
1525
|
parseScaffoldBlockMetadata,
|
|
@@ -1535,10 +1540,7 @@ const registration = buildScaffoldBlockRegistration(
|
|
|
1535
1540
|
}
|
|
1536
1541
|
);
|
|
1537
1542
|
|
|
1538
|
-
|
|
1539
|
-
registration.name,
|
|
1540
|
-
registration.settings
|
|
1541
|
-
);
|
|
1543
|
+
registerScaffoldBlockType(registration.name, registration.settings);
|
|
1542
1544
|
`;
|
|
1543
1545
|
export const COMPOUND_LOCAL_HOOKS_TEMPLATE = `export {
|
|
1544
1546
|
formatValidationError,
|
|
@@ -1690,8 +1692,10 @@ export default function Save( {
|
|
|
1690
1692
|
);
|
|
1691
1693
|
}
|
|
1692
1694
|
`;
|
|
1693
|
-
export const COMPOUND_CHILD_INDEX_TEMPLATE = `import {
|
|
1694
|
-
|
|
1695
|
+
export const COMPOUND_CHILD_INDEX_TEMPLATE = `import {
|
|
1696
|
+
\tregisterScaffoldBlockType,
|
|
1697
|
+
\ttype BlockConfiguration,
|
|
1698
|
+
} from '@wp-typia/block-types/blocks/registration';
|
|
1695
1699
|
import {
|
|
1696
1700
|
buildScaffoldBlockRegistration,
|
|
1697
1701
|
parseScaffoldBlockMetadata,
|
|
@@ -1712,10 +1716,7 @@ const registration = buildScaffoldBlockRegistration(
|
|
|
1712
1716
|
}
|
|
1713
1717
|
);
|
|
1714
1718
|
|
|
1715
|
-
|
|
1716
|
-
registration.name,
|
|
1717
|
-
registration.settings
|
|
1718
|
-
);
|
|
1719
|
+
registerScaffoldBlockType(registration.name, registration.settings);
|
|
1719
1720
|
`;
|
|
1720
1721
|
export const COMPOUND_CHILD_VALIDATORS_TEMPLATE = `import typia from 'typia';
|
|
1721
1722
|
import currentManifest from './manifest-defaults-document';
|
package/dist/runtime/cli-help.js
CHANGED
|
@@ -33,7 +33,7 @@ Built-in templates: ${TEMPLATE_IDS.join(", ")}
|
|
|
33
33
|
Package managers: ${PACKAGE_MANAGER_IDS.join(", ")}
|
|
34
34
|
Notes:
|
|
35
35
|
\`wp-typia create\` is the canonical scaffold command.
|
|
36
|
-
\`wp-typia <project-dir>\` remains a backward-compatible alias to \`create
|
|
36
|
+
\`wp-typia <project-dir>\` remains a backward-compatible alias to \`create\` when \`<project-dir>\` is the only positional argument.
|
|
37
37
|
Use \`--template workspace\` as shorthand for \`@wp-typia/create-workspace-template\`, the official empty workspace scaffold behind \`wp-typia add ...\`.
|
|
38
38
|
\`add variation\` uses an existing workspace block from \`scripts/block-config.ts\`.
|
|
39
39
|
\`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
|
|
@@ -108,7 +108,7 @@ export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templ
|
|
|
108
108
|
? externalLayerSource.trim()
|
|
109
109
|
: undefined;
|
|
110
110
|
if (!projectInput) {
|
|
111
|
-
throw new Error("Project directory is required. Usage: wp-typia create <project-dir> (or wp-typia <project-dir>).");
|
|
111
|
+
throw new Error("Project directory is required. Usage: wp-typia create <project-dir> (or wp-typia <project-dir> when <project-dir> is the only positional argument).");
|
|
112
112
|
}
|
|
113
113
|
const resolvedTemplateId = await resolveTemplateId({
|
|
114
114
|
templateId,
|
|
@@ -41,7 +41,7 @@ function injectBefore(source, needle, insertion) {
|
|
|
41
41
|
return source.replace(needle, `${insertion}\n${needle}`);
|
|
42
42
|
}
|
|
43
43
|
function injectAfterBlockMetadataImport(source, insertion) {
|
|
44
|
-
|
|
44
|
+
const nextSource = injectAfter(source, BLOCK_METADATA_IMPORT_LINE, insertion);
|
|
45
45
|
if (nextSource !== source) {
|
|
46
46
|
return nextSource;
|
|
47
47
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wp-typia/project-tools",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.11",
|
|
4
4
|
"description": "Project orchestration and programmatic tooling for wp-typia",
|
|
5
5
|
"packageManager": "bun@1.3.11",
|
|
6
6
|
"type": "module",
|
|
@@ -121,8 +121,8 @@
|
|
|
121
121
|
"dependencies": {
|
|
122
122
|
"@wp-typia/api-client": "^0.4.4",
|
|
123
123
|
"@wp-typia/block-runtime": "^0.4.8",
|
|
124
|
-
"@wp-typia/rest": "^0.3.
|
|
125
|
-
"@wp-typia/block-types": "^0.2.
|
|
124
|
+
"@wp-typia/rest": "^0.3.8",
|
|
125
|
+
"@wp-typia/block-types": "^0.2.4",
|
|
126
126
|
"mustache": "^4.2.0",
|
|
127
127
|
"npm-package-arg": "^13.0.0",
|
|
128
128
|
"semver": "^7.7.3",
|
|
@@ -576,8 +576,10 @@ export default function Save( {
|
|
|
576
576
|
}
|
|
577
577
|
|
|
578
578
|
function renderIndexFile( childTypeName: string, childFolderSlug: string ): string {
|
|
579
|
-
return `import {
|
|
580
|
-
|
|
579
|
+
return `import {
|
|
580
|
+
\tregisterScaffoldBlockType,
|
|
581
|
+
\ttype BlockConfiguration,
|
|
582
|
+
} from '@wp-typia/block-types/blocks/registration';
|
|
581
583
|
import {
|
|
582
584
|
\tbuildScaffoldBlockRegistration,
|
|
583
585
|
\tparseScaffoldBlockMetadata,
|
|
@@ -598,10 +600,7 @@ const registration = buildScaffoldBlockRegistration(
|
|
|
598
600
|
\t}
|
|
599
601
|
);
|
|
600
602
|
|
|
601
|
-
|
|
602
|
-
\tregistration.name,
|
|
603
|
-
\tregistration.settings
|
|
604
|
-
);
|
|
603
|
+
registerScaffoldBlockType( registration.name, registration.settings );
|
|
605
604
|
`;
|
|
606
605
|
}
|
|
607
606
|
|