@sparkerp/plugin-sdk 0.1.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/blocks.json +169 -0
- package/bundle/catalog.json +614 -5
- package/bundle/docs/applications/hcm/admin-access-policies.md +38 -0
- package/bundle/docs/applications/hcm/admin-approval-hierarchies.md +33 -0
- package/bundle/docs/applications/hcm/admin-data-transfer.md +40 -0
- package/bundle/docs/applications/hcm/admin-hcm-users.md +35 -0
- package/bundle/docs/applications/hcm/admin-logs.md +57 -0
- package/bundle/docs/applications/hcm/admin-permissions-catalog.md +64 -0
- package/bundle/docs/applications/hcm/ai-intelligence-overview.md +37 -0
- package/bundle/docs/applications/hcm/ai-intelligence-people-risk.md +33 -0
- package/bundle/docs/applications/hcm/ai-intelligence-recruitment.md +37 -0
- package/bundle/docs/applications/hcm/ai-intelligence-tools.md +39 -0
- package/bundle/docs/applications/hcm/analytics-operations.md +31 -0
- package/bundle/docs/applications/hcm/analytics-overview.md +31 -0
- package/bundle/docs/applications/hcm/analytics-people.md +36 -0
- package/bundle/docs/applications/hcm/analytics-tools.md +35 -0
- package/bundle/docs/applications/hcm/assets-audits.md +30 -0
- package/bundle/docs/applications/hcm/assets-custody.md +36 -0
- package/bundle/docs/applications/hcm/assets-inventory.md +34 -0
- package/bundle/docs/applications/hcm/assets-maintenance.md +25 -0
- package/bundle/docs/applications/hcm/assets-software-licenses.md +27 -0
- package/bundle/docs/applications/hcm/attendance-core.md +46 -0
- package/bundle/docs/applications/hcm/attendance-exceptions.md +33 -0
- package/bundle/docs/applications/hcm/attendance-location.md +25 -0
- package/bundle/docs/applications/hcm/attendance-policies.md +23 -0
- package/bundle/docs/applications/hcm/attendance-reports.md +20 -0
- package/bundle/docs/applications/hcm/benefits-allowances.md +39 -0
- package/bundle/docs/applications/hcm/benefits-analytics.md +25 -0
- package/bundle/docs/applications/hcm/benefits-employee-processes.md +44 -0
- package/bundle/docs/applications/hcm/benefits-plans.md +60 -0
- package/bundle/docs/applications/hcm/communications-announcements.md +31 -0
- package/bundle/docs/applications/hcm/communications-history.md +23 -0
- package/bundle/docs/applications/hcm/communications-notifications.md +31 -0
- package/bundle/docs/applications/hcm/communications-templates.md +35 -0
- package/bundle/docs/applications/hcm/compensation-allowances-benefits.md +44 -0
- package/bundle/docs/applications/hcm/compensation-analytics.md +30 -0
- package/bundle/docs/applications/hcm/compensation-bonus-incentive.md +35 -0
- package/bundle/docs/applications/hcm/compensation-cycles.md +49 -0
- package/bundle/docs/applications/hcm/compensation-equity.md +36 -0
- package/bundle/docs/applications/hcm/compensation-salary.md +47 -0
- package/bundle/docs/applications/hcm/compliance-analytics.md +25 -0
- package/bundle/docs/applications/hcm/compliance-audits-calendar.md +52 -0
- package/bundle/docs/applications/hcm/compliance-employee-tracking.md +41 -0
- package/bundle/docs/applications/hcm/compliance-regulatory.md +31 -0
- package/bundle/docs/applications/hcm/country-packs-admin.md +33 -0
- package/bundle/docs/applications/hcm/country-packs-compliance.md +31 -0
- package/bundle/docs/applications/hcm/country-packs-localization.md +37 -0
- package/bundle/docs/applications/hcm/country-packs.md +102 -0
- package/bundle/docs/applications/hcm/dashboard.md +38 -0
- package/bundle/docs/applications/hcm/designations.md +32 -0
- package/bundle/docs/applications/hcm/emp-documents-analytics.md +27 -0
- package/bundle/docs/applications/hcm/emp-documents-core.md +33 -0
- package/bundle/docs/applications/hcm/emp-documents-letters.md +40 -0
- package/bundle/docs/applications/hcm/emp-documents-signatures.md +24 -0
- package/bundle/docs/applications/hcm/emp-documents-workflow.md +32 -0
- package/bundle/docs/applications/hcm/employee-directory.md +55 -0
- package/bundle/docs/applications/hcm/employee-documents.md +51 -0
- package/bundle/docs/applications/hcm/employee-info-background.md +41 -0
- package/bundle/docs/applications/hcm/employee-info-core.md +32 -0
- package/bundle/docs/applications/hcm/employee-info-documents.md +31 -0
- package/bundle/docs/applications/hcm/employee-info-other.md +21 -0
- package/bundle/docs/applications/hcm/employee-movements.md +46 -0
- package/bundle/docs/applications/hcm/employee-reference-data.md +40 -0
- package/bundle/docs/applications/hcm/employee-relations-analytics.md +23 -0
- package/bundle/docs/applications/hcm/employee-relations-cases.md +26 -0
- package/bundle/docs/applications/hcm/employee-relations-conflicts-feedback.md +25 -0
- package/bundle/docs/applications/hcm/employee-relations-processes.md +34 -0
- package/bundle/docs/applications/hcm/employee-services-daily.md +32 -0
- package/bundle/docs/applications/hcm/employee-services-manager.md +21 -0
- package/bundle/docs/applications/hcm/employee-services-more.md +41 -0
- package/bundle/docs/applications/hcm/employee-services-overview.md +43 -0
- package/bundle/docs/applications/hcm/expenses-analytics.md +26 -0
- package/bundle/docs/applications/hcm/expenses-claims.md +47 -0
- package/bundle/docs/applications/hcm/expenses-self-service.md +30 -0
- package/bundle/docs/applications/hcm/expenses-setup.md +43 -0
- package/bundle/docs/applications/hcm/health-safety-analytics.md +26 -0
- package/bundle/docs/applications/hcm/health-safety-incidents.md +44 -0
- package/bundle/docs/applications/hcm/health-safety-medical.md +25 -0
- package/bundle/docs/applications/hcm/health-safety-training.md +29 -0
- package/bundle/docs/applications/hcm/helpdesk.md +28 -0
- package/bundle/docs/applications/hcm/holiday-calendar.md +69 -0
- package/bundle/docs/applications/hcm/hr-policies.md +64 -0
- package/bundle/docs/applications/hcm/hr-settings.md +98 -0
- package/bundle/docs/applications/hcm/index.md +272 -0
- package/bundle/docs/applications/hcm/industry-it-software.md +52 -0
- package/bundle/docs/applications/hcm/job-classifications.md +48 -0
- package/bundle/docs/applications/hcm/learning-analytics.md +21 -0
- package/bundle/docs/applications/hcm/learning-assessments.md +22 -0
- package/bundle/docs/applications/hcm/learning-catalog.md +25 -0
- package/bundle/docs/applications/hcm/learning-certifications.md +27 -0
- package/bundle/docs/applications/hcm/learning-enrollment.md +26 -0
- package/bundle/docs/applications/hcm/learning-instructors-providers.md +21 -0
- package/bundle/docs/applications/hcm/leave-accrual-and-carryforward.md +30 -0
- package/bundle/docs/applications/hcm/leave-balance.md +26 -0
- package/bundle/docs/applications/hcm/leave-dashboard-and-reports.md +32 -0
- package/bundle/docs/applications/hcm/leave-encashment.md +27 -0
- package/bundle/docs/applications/hcm/leave-requests.md +26 -0
- package/bundle/docs/applications/hcm/leave-setup.md +30 -0
- package/bundle/docs/applications/hcm/leave-team-and-calendar.md +25 -0
- package/bundle/docs/applications/hcm/navigation-and-approvals.md +61 -0
- package/bundle/docs/applications/hcm/offboarding-and-exit.md +71 -0
- package/bundle/docs/applications/hcm/onboarding-documents-verification.md +43 -0
- package/bundle/docs/applications/hcm/onboarding-orientation-probation.md +65 -0
- package/bundle/docs/applications/hcm/onboarding-overview.md +47 -0
- package/bundle/docs/applications/hcm/onboarding-provisioning-assets.md +48 -0
- package/bundle/docs/applications/hcm/onboarding-reports.md +38 -0
- package/bundle/docs/applications/hcm/onboarding-templates-checklists.md +38 -0
- package/bundle/docs/applications/hcm/onboarding-to-confirmation.md +45 -0
- package/bundle/docs/applications/hcm/org-structure.md +53 -0
- package/bundle/docs/applications/hcm/org-units.md +86 -0
- package/bundle/docs/applications/hcm/organizations.md +81 -0
- package/bundle/docs/applications/hcm/payroll-analytics.md +31 -0
- package/bundle/docs/applications/hcm/payroll-post-run.md +40 -0
- package/bundle/docs/applications/hcm/payroll-runs.md +30 -0
- package/bundle/docs/applications/hcm/payroll-setup.md +38 -0
- package/bundle/docs/applications/hcm/payroll-tax.md +21 -0
- package/bundle/docs/applications/hcm/payroll-transactions.md +43 -0
- package/bundle/docs/applications/hcm/performance-analytics.md +23 -0
- package/bundle/docs/applications/hcm/performance-appraisals.md +50 -0
- package/bundle/docs/applications/hcm/performance-continuous-feedback.md +23 -0
- package/bundle/docs/applications/hcm/performance-cycles-and-goals.md +54 -0
- package/bundle/docs/applications/hcm/performance-okrs.md +33 -0
- package/bundle/docs/applications/hcm/performance-pips.md +29 -0
- package/bundle/docs/applications/hcm/performance-ratings.md +25 -0
- package/bundle/docs/applications/hcm/positions.md +67 -0
- package/bundle/docs/applications/hcm/recruitment-agencies-and-sources.md +26 -0
- package/bundle/docs/applications/hcm/recruitment-analytics.md +25 -0
- package/bundle/docs/applications/hcm/recruitment-candidates.md +33 -0
- package/bundle/docs/applications/hcm/recruitment-offers.md +32 -0
- package/bundle/docs/applications/hcm/recruitment-pipeline.md +46 -0
- package/bundle/docs/applications/hcm/recruitment-requisitions-and-openings.md +33 -0
- package/bundle/docs/applications/hcm/roles-permissions.md +129 -0
- package/bundle/docs/applications/hcm/separation-clearance.md +33 -0
- package/bundle/docs/applications/hcm/separation-documents.md +28 -0
- package/bundle/docs/applications/hcm/separation-final-settlement.md +24 -0
- package/bundle/docs/applications/hcm/separation-reports.md +23 -0
- package/bundle/docs/applications/hcm/separation-resignation.md +43 -0
- package/bundle/docs/applications/hcm/settings-extensibility.md +31 -0
- package/bundle/docs/applications/hcm/settings-general.md +29 -0
- package/bundle/docs/applications/hcm/settings-integrations.md +14 -0
- package/bundle/docs/applications/hcm/settings-module-defaults.md +41 -0
- package/bundle/docs/applications/hcm/settings-process.md +28 -0
- package/bundle/docs/applications/hcm/shift-scheduling.md +33 -0
- package/bundle/docs/applications/hcm/talent-analytics.md +26 -0
- package/bundle/docs/applications/hcm/talent-career.md +31 -0
- package/bundle/docs/applications/hcm/talent-competencies-skills.md +30 -0
- package/bundle/docs/applications/hcm/talent-profiles.md +40 -0
- package/bundle/docs/applications/hcm/talent-succession.md +42 -0
- package/bundle/docs/applications/hcm/teams-and-tags.md +46 -0
- package/bundle/docs/applications/hcm/timesheets.md +33 -0
- package/bundle/docs/applications/hcm/travel-advances-expenses.md +34 -0
- package/bundle/docs/applications/hcm/travel-analytics.md +28 -0
- package/bundle/docs/applications/hcm/travel-bookings.md +35 -0
- package/bundle/docs/applications/hcm/travel-requests.md +38 -0
- package/bundle/docs/applications/hcm/workforce-org-design.md +22 -0
- package/bundle/docs/applications/hcm/workforce-planning-analytics.md +26 -0
- package/bundle/docs/applications/hcm/workforce-planning-core.md +36 -0
- package/bundle/docs/applications/hcm/workforce-planning-scenarios.md +29 -0
- package/bundle/docs/docs.json +1 -0
- package/bundle/docs/guides/add-app-owned-roles-and-permissions.md +144 -0
- package/bundle/docs/guides/checkout-an-installed-plugin.md +151 -0
- package/bundle/docs/guides/extend-a-shipped-application.md +132 -0
- package/bundle/docs/guides/index.md +3 -0
- package/bundle/docs/reference/entity-aggregation-config.md +1 -1
- package/bundle/docs/reference/entity-document-generator-config.md +1 -1
- package/bundle/docs/tutorial/01-create-the-plugin.md +7 -1
- package/bundle/docs/tutorial/07-return-due-reminder-job.md +28 -5
- package/bundle/docs/tutorial/08-menus-i18n-publish.md +5 -5
- package/bundle/manifest.json +4 -4
- package/bundle/schemas/page.schema.json +13 -0
- package/bundle/schemas/plugin-manifest.schema.json +13 -0
- package/bundle/validators/block-engine.mjs +167 -7
- package/bundle/validators/page-engine.mjs +226 -21
- package/erp-cli/authoring-root.mjs +12 -1
- package/erp-cli/erp.mjs +679 -15
- package/package.json +1 -1
|
@@ -3384,6 +3384,25 @@ function endsWith(args) {
|
|
|
3384
3384
|
const [s, suffix] = args;
|
|
3385
3385
|
return rvBool(str(s).endsWith(str(suffix)));
|
|
3386
3386
|
}
|
|
3387
|
+
function displayString(v) {
|
|
3388
|
+
switch (v.type) {
|
|
3389
|
+
case "String":
|
|
3390
|
+
return v.value;
|
|
3391
|
+
case "Integer":
|
|
3392
|
+
return v.value.toString();
|
|
3393
|
+
case "Decimal":
|
|
3394
|
+
return v.value.toString();
|
|
3395
|
+
case "Boolean":
|
|
3396
|
+
return String(v.value);
|
|
3397
|
+
case "Null":
|
|
3398
|
+
return "";
|
|
3399
|
+
default:
|
|
3400
|
+
throw new EvaluationError(`concat() does not support ${v.type} operands`);
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
function concatFn(args) {
|
|
3404
|
+
return rvString(args.map(displayString).join(""));
|
|
3405
|
+
}
|
|
3387
3406
|
function registerStringFunctions(registry, version) {
|
|
3388
3407
|
registry.register(version, "upper", upper);
|
|
3389
3408
|
registry.register(version, "lower", lower);
|
|
@@ -3392,6 +3411,7 @@ function registerStringFunctions(registry, version) {
|
|
|
3392
3411
|
registry.register(version, "substring", substring);
|
|
3393
3412
|
registry.register(version, "startsWith", startsWith);
|
|
3394
3413
|
registry.register(version, "endsWith", endsWith);
|
|
3414
|
+
registry.register(version, "concat", concatFn);
|
|
3395
3415
|
}
|
|
3396
3416
|
function contains(args) {
|
|
3397
3417
|
const [collection, needle] = args;
|
|
@@ -3438,12 +3458,19 @@ function size(args) {
|
|
|
3438
3458
|
function isEmpty(args) {
|
|
3439
3459
|
return rvBool(sizeOf(args[0]) === 0);
|
|
3440
3460
|
}
|
|
3461
|
+
function pluck(args) {
|
|
3462
|
+
const [arr, key] = args;
|
|
3463
|
+
if (arr.type !== "Array") throw new EvaluationError(`pluck() expects an Array as its first argument, got ${arr.type}`);
|
|
3464
|
+
if (key.type !== "String") throw new EvaluationError("pluck() expects a String field name as its second argument");
|
|
3465
|
+
return rvArray(arr.value.map((row) => row.type === "Object" ? row.value[key.value] ?? RV_NULL : RV_NULL));
|
|
3466
|
+
}
|
|
3441
3467
|
function registerCollectionFunctions(registry, version) {
|
|
3442
3468
|
registry.register(version, "contains", contains);
|
|
3443
3469
|
registry.register(version, "first", first);
|
|
3444
3470
|
registry.register(version, "last", last);
|
|
3445
3471
|
registry.register(version, "size", size);
|
|
3446
3472
|
registry.register(version, "isEmpty", isEmpty);
|
|
3473
|
+
registry.register(version, "pluck", pluck);
|
|
3447
3474
|
}
|
|
3448
3475
|
function ifImpl(args) {
|
|
3449
3476
|
const [cond, thenValue, elseValue] = args;
|
|
@@ -3516,12 +3543,14 @@ var FUNCTION_CATALOG = [
|
|
|
3516
3543
|
c({ name: "substring", minArity: 2, maxArity: 3, argKinds: ["String", "Numeric", "Numeric"], returnType: "String", category: "String", description: "Part of a string from a 0-based start position, optionally limited to a length." }),
|
|
3517
3544
|
c({ name: "startsWith", minArity: 2, maxArity: 2, argKinds: ["String", "String"], returnType: "Boolean", category: "String", description: "True when the string begins with the given prefix." }),
|
|
3518
3545
|
c({ name: "endsWith", minArity: 2, maxArity: 2, argKinds: ["String", "String"], returnType: "Boolean", category: "String", description: "True when the string ends with the given suffix." }),
|
|
3546
|
+
c({ name: "concat", minArity: 2, maxArity: Infinity, argKinds: ["Any"], returnType: "String", category: "String", description: `Joins 2+ values' display text into one string, e.g. concat("Total: ", page.count).` }),
|
|
3519
3547
|
// Collection
|
|
3520
3548
|
c({ name: "contains", minArity: 2, maxArity: 2, argKinds: ["Collection", "Any"], returnType: "Boolean", category: "Collection", description: "True when a list, string, or object contains the given element/substring/key." }),
|
|
3521
3549
|
c({ name: "first", minArity: 1, maxArity: 1, argKinds: ["Array"], returnType: "Any", category: "Collection", description: "The first element of a list." }),
|
|
3522
3550
|
c({ name: "last", minArity: 1, maxArity: 1, argKinds: ["Array"], returnType: "Any", category: "Collection", description: "The last element of a list." }),
|
|
3523
3551
|
c({ name: "size", minArity: 1, maxArity: 1, argKinds: ["Collection"], returnType: "Integer", category: "Collection", description: "Number of elements in a list, characters in a string, or keys in an object." }),
|
|
3524
3552
|
c({ name: "isEmpty", minArity: 1, maxArity: 1, argKinds: ["Collection"], returnType: "Boolean", category: "Collection", description: "True when a list, string, or object has no elements." }),
|
|
3553
|
+
c({ name: "pluck", minArity: 2, maxArity: 2, argKinds: ["Array", "String"], returnType: "Array", category: "Collection", description: `pluck(rows, "fieldName") \u2014 a new list of that field's value from each object in the list, e.g. turning [{date, value}, ...] into [value, value, ...].` }),
|
|
3525
3554
|
// Utility
|
|
3526
3555
|
c({ name: "if", minArity: 3, maxArity: 3, argKinds: ["Boolean", "Any", "Any"], returnType: "Any", category: "Utility", description: "if(condition, thenValue, elseValue) \u2014 returns one of two values based on a condition." }),
|
|
3527
3556
|
c({ name: "coalesce", minArity: 1, maxArity: Infinity, argKinds: ["Any"], returnType: "Any", category: "Utility", description: "The first argument that is not null." }),
|
|
@@ -4744,9 +4773,9 @@ function resolvePath(scopes, rawPath) {
|
|
|
4744
4773
|
return value;
|
|
4745
4774
|
}
|
|
4746
4775
|
function resolveString(scopes, text) {
|
|
4747
|
-
const
|
|
4748
|
-
if (
|
|
4749
|
-
const [, path, field] =
|
|
4776
|
+
const pluck2 = PLUCK_TEMPLATE.exec(text);
|
|
4777
|
+
if (pluck2) {
|
|
4778
|
+
const [, path, field] = pluck2;
|
|
4750
4779
|
const resolved = runtimeValueToJs(resolvePath(scopes, path));
|
|
4751
4780
|
if (!Array.isArray(resolved)) {
|
|
4752
4781
|
throw new ActionError("unresolved-variable", `'${path}|pluck:${field}' requires an array at '${path}'`);
|
|
@@ -11057,6 +11086,26 @@ ${failedSteps}` : "")
|
|
|
11057
11086
|
});
|
|
11058
11087
|
if (faulted && this.phase === "ready") this.present();
|
|
11059
11088
|
}
|
|
11089
|
+
/**
|
|
11090
|
+
* A real, previously-undiscovered gap (2026-09-22, found live authoring the HCM Permissions
|
|
11091
|
+
* "Create Permission" dialog): a `core.text-input` (or any other inputClass block) whose page
|
|
11092
|
+
* JSON only ever sets `properties.value.binding` — the "obviously two-way" way EVERY such field
|
|
11093
|
+
* in this codebase's own already-shipped pages is authored (`hcm-users.json`'s Create User
|
|
11094
|
+
* dialog included) — never actually wrote a keystroke back to that bound page-scope key. `commit`
|
|
11095
|
+
* → `writeOutput` only ever consulted `config.outputs[name]`, a SEPARATE, explicit redirect
|
|
11096
|
+
* nothing in this codebase's own page-authoring convention ever sets; absent that, every write
|
|
11097
|
+
* landed in a `{scope:"block", scopeKey:this.instanceId}` bucket no page action chain can ever
|
|
11098
|
+
* read — the DOM still showed the typed characters (via `RevealableTextField`'s own local echo
|
|
11099
|
+
* state), so this was invisible in every purely-visual check, only surfacing when a create
|
|
11100
|
+
* dialog's Submit button's `${page.createX}` params were inspected against the REAL network
|
|
11101
|
+
* payload, which showed every field empty. Fixed by falling back to the SAME resolved binding
|
|
11102
|
+
* target `resolveProperties()` already computed for the identically-named PROPERTY's own read
|
|
11103
|
+
* side (`this.boundProps`) when no explicit `config.outputs[name]` redirect exists — making a
|
|
11104
|
+
* `source:"binding"` property's own binding genuinely two-way by default, matching what every
|
|
11105
|
+
* page author already (reasonably) assumed it meant. An explicit `config.outputs[name]` still
|
|
11106
|
+
* wins unchanged (e.g. `core.file-upload`'s own redirect to a different scope/key than its
|
|
11107
|
+
* `value` property reads from); this only fills in the previously-silent gap.
|
|
11108
|
+
*/
|
|
11060
11109
|
async writeOutput(name, value) {
|
|
11061
11110
|
const port = (this.type.definition.outputs ?? []).find((o) => o.name === name);
|
|
11062
11111
|
if (!port) {
|
|
@@ -11064,7 +11113,7 @@ ${failedSteps}` : "")
|
|
|
11064
11113
|
return;
|
|
11065
11114
|
}
|
|
11066
11115
|
const redirect = this.config.outputs?.[name];
|
|
11067
|
-
const target = redirect ? resolveBoundKey(redirect, this.bindingContext()) : { scope: "block", scopeKey: this.instanceId, key: name };
|
|
11116
|
+
const target = redirect ? resolveBoundKey(redirect, this.bindingContext()) : this.boundProps.get(name) ?? { scope: "block", scopeKey: this.instanceId, key: name };
|
|
11068
11117
|
await this.ctx.state.set(target, value);
|
|
11069
11118
|
}
|
|
11070
11119
|
/* ---------------- error isolation & standard states (W2-04) ---------------- */
|
|
@@ -12884,6 +12933,47 @@ var imageDefinition = definition({
|
|
|
12884
12933
|
type: "string",
|
|
12885
12934
|
sources: ["static"],
|
|
12886
12935
|
designer: { group: "content", editor: "text", labelKey: "core.image.property.height" }
|
|
12936
|
+
},
|
|
12937
|
+
{
|
|
12938
|
+
// 2026-09-17 (page-builder "Customize" panel parity) — a clip-path-class
|
|
12939
|
+
// shape, the same fixed vocabulary a real page-builder's own "Shape"
|
|
12940
|
+
// picker offers (never an arbitrary path — that stays a `style`
|
|
12941
|
+
// escape-hatch concern).
|
|
12942
|
+
name: "shape",
|
|
12943
|
+
type: "string",
|
|
12944
|
+
sources: ["static"],
|
|
12945
|
+
default: "none",
|
|
12946
|
+
constraints: { options: [{ value: "none", labelKey: "core.image.shape.none" }, { value: "circle", labelKey: "core.image.shape.circle" }, { value: "rounded", labelKey: "core.image.shape.rounded" }] },
|
|
12947
|
+
designer: { group: "content", editor: "select", labelKey: "core.image.property.shape" }
|
|
12948
|
+
},
|
|
12949
|
+
{
|
|
12950
|
+
/** A native browser tooltip (HTML `title` attribute) — distinct from `alt` (accessibility text, always present) and from a rich hover-card (out of scope here, a real different feature). */
|
|
12951
|
+
name: "tooltip",
|
|
12952
|
+
type: "string",
|
|
12953
|
+
sources: ["static"],
|
|
12954
|
+
designer: { group: "content", editor: "text", labelKey: "core.image.property.tooltip" }
|
|
12955
|
+
},
|
|
12956
|
+
{
|
|
12957
|
+
/** A raw CSS `transform` value, e.g. "rotate(4deg)" — same "text travels as data" posture as `PageTheme.customCss`, never `dangerouslySetInnerHTML`, just an inline style value. */
|
|
12958
|
+
name: "transform",
|
|
12959
|
+
type: "string",
|
|
12960
|
+
sources: ["static"],
|
|
12961
|
+
designer: { group: "content", editor: "text", labelKey: "core.image.property.transform" }
|
|
12962
|
+
},
|
|
12963
|
+
{
|
|
12964
|
+
name: "filter",
|
|
12965
|
+
type: "string",
|
|
12966
|
+
sources: ["static"],
|
|
12967
|
+
default: "none",
|
|
12968
|
+
constraints: {
|
|
12969
|
+
options: [
|
|
12970
|
+
{ value: "none", labelKey: "core.image.filter.none" },
|
|
12971
|
+
{ value: "grayscale", labelKey: "core.image.filter.grayscale" },
|
|
12972
|
+
{ value: "sepia", labelKey: "core.image.filter.sepia" },
|
|
12973
|
+
{ value: "blur", labelKey: "core.image.filter.blur" }
|
|
12974
|
+
]
|
|
12975
|
+
},
|
|
12976
|
+
designer: { group: "content", editor: "select", labelKey: "core.image.property.filter" }
|
|
12887
12977
|
}
|
|
12888
12978
|
],
|
|
12889
12979
|
events: [],
|
|
@@ -12905,7 +12995,11 @@ var imageLogic = {
|
|
|
12905
12995
|
src: String(io.props["src"] ?? ""),
|
|
12906
12996
|
alt: io.props["alt"] !== void 0 ? String(io.props["alt"]) : "",
|
|
12907
12997
|
width: io.props["width"] !== void 0 ? String(io.props["width"]) : void 0,
|
|
12908
|
-
height: io.props["height"] !== void 0 ? String(io.props["height"]) : void 0
|
|
12998
|
+
height: io.props["height"] !== void 0 ? String(io.props["height"]) : void 0,
|
|
12999
|
+
shape: io.props["shape"] !== void 0 ? String(io.props["shape"]) : void 0,
|
|
13000
|
+
tooltip: io.props["tooltip"] !== void 0 ? String(io.props["tooltip"]) : void 0,
|
|
13001
|
+
transform: io.props["transform"] !== void 0 ? String(io.props["transform"]) : void 0,
|
|
13002
|
+
filter: io.props["filter"] !== void 0 ? String(io.props["filter"]) : void 0
|
|
12909
13003
|
}
|
|
12910
13004
|
});
|
|
12911
13005
|
}
|
|
@@ -16275,6 +16369,31 @@ var gridDefinition = definition({
|
|
|
16275
16369
|
designer: { group: "data", editor: "binding", labelKey: "core.grid.property.refreshTrigger" }
|
|
16276
16370
|
}
|
|
16277
16371
|
],
|
|
16372
|
+
// 2026-09-23 — these are the block's own internal state (written via
|
|
16373
|
+
// `io.setOwnState` in this file's `query()`/handlers, read back into
|
|
16374
|
+
// `node.props` for the adapter), not authored properties: a page author
|
|
16375
|
+
// never sets them, but a plugin/theme developer inspecting `core.grid`
|
|
16376
|
+
// via `erp blocks list --type core.grid` had no way to discover they
|
|
16377
|
+
// exist at all — the empty-grid production bug this session traced back
|
|
16378
|
+
// to exactly this state (rows/total both came from the same query
|
|
16379
|
+
// response, one reached the render, the other didn't) took a live
|
|
16380
|
+
// Playwright session plus a source read to pin down, when this list
|
|
16381
|
+
// alone would have shown the full internal-state surface immediately.
|
|
16382
|
+
outputs: [
|
|
16383
|
+
{ name: "rows", type: "json" },
|
|
16384
|
+
{ name: "total", type: "number" },
|
|
16385
|
+
{ name: "pageIndex", type: "number" },
|
|
16386
|
+
{ name: "sort", type: "json" },
|
|
16387
|
+
{ name: "filter", type: "json" },
|
|
16388
|
+
{ name: "groups", type: "json" },
|
|
16389
|
+
{ name: "aggregates", type: "json" },
|
|
16390
|
+
{ name: "expandedGroups", type: "json" },
|
|
16391
|
+
{ name: "error", type: "string" },
|
|
16392
|
+
{ name: "circuitOpen", type: "boolean" },
|
|
16393
|
+
{ name: "selectedIds", type: "json" },
|
|
16394
|
+
{ name: "density", type: "string" },
|
|
16395
|
+
{ name: "groupBy", type: "string" }
|
|
16396
|
+
],
|
|
16278
16397
|
events: [
|
|
16279
16398
|
{ name: "selectionChanged", catalogType: "GridSelectionChanged" },
|
|
16280
16399
|
{ name: "rowClicked", catalogType: "GridRowClicked" },
|
|
@@ -17182,6 +17301,30 @@ var fileUploadDefinition = definition({
|
|
|
17182
17301
|
type: "string",
|
|
17183
17302
|
sources: ["static", "expression"],
|
|
17184
17303
|
designer: { group: "data", editor: "text", labelKey: "core.file-upload.property.cabinetId" }
|
|
17304
|
+
},
|
|
17305
|
+
{
|
|
17306
|
+
// 2026-09-21 (HCM Users spec gap-closing pass) — every pre-existing
|
|
17307
|
+
// page never sets this (undefined === "dms"), so it's a zero-risk
|
|
17308
|
+
// addition: `"dms"` keeps today's exact behavior (a real DMS cabinet
|
|
17309
|
+
// upload via the injected `FileUploadSource`, `value` becomes real
|
|
17310
|
+
// `UploadedFileRef`s). `"raw"` skips the DMS upload entirely — `value`
|
|
17311
|
+
// becomes the picked file's own `{filename, contentType, dataUrl}`
|
|
17312
|
+
// straight from the browser's file picker, with no `id`/`url` (nothing
|
|
17313
|
+
// was uploaded anywhere) — meant to feed a `callApi` action's
|
|
17314
|
+
// `multipart: true` body (see that handler's own doc comment), the
|
|
17315
|
+
// real fix for the disclosed "core.file-upload cannot emit raw bytes
|
|
17316
|
+
// into a callApi body" gap (`ai/patterns/import.md`).
|
|
17317
|
+
name: "mode",
|
|
17318
|
+
type: "string",
|
|
17319
|
+
sources: ["static"],
|
|
17320
|
+
default: "dms",
|
|
17321
|
+
constraints: {
|
|
17322
|
+
options: [
|
|
17323
|
+
{ value: "dms", labelKey: "core.file-upload.mode.dms" },
|
|
17324
|
+
{ value: "raw", labelKey: "core.file-upload.mode.raw" }
|
|
17325
|
+
]
|
|
17326
|
+
},
|
|
17327
|
+
designer: { group: "behavior", editor: "select", labelKey: "core.file-upload.property.mode" }
|
|
17185
17328
|
}
|
|
17186
17329
|
],
|
|
17187
17330
|
outputs: [{ name: "value", type: "json" }],
|
|
@@ -17194,15 +17337,23 @@ var fileUploadDefinition = definition({
|
|
|
17194
17337
|
category: "input",
|
|
17195
17338
|
propertyGroups: [
|
|
17196
17339
|
{ id: "data", titleKey: "core.designer.group.data" },
|
|
17197
|
-
{ id: "content", titleKey: "core.designer.group.content" }
|
|
17340
|
+
{ id: "content", titleKey: "core.designer.group.content" },
|
|
17341
|
+
{ id: "behavior", titleKey: "core.designer.group.behavior" }
|
|
17198
17342
|
],
|
|
17199
17343
|
allowedTargets: ["page", "form", "container", "dashboard"],
|
|
17200
17344
|
preview: { kind: "field" }
|
|
17201
17345
|
}
|
|
17202
17346
|
});
|
|
17347
|
+
var rawFileIdCounter = 0;
|
|
17348
|
+
function nextRawFileId() {
|
|
17349
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
17350
|
+
rawFileIdCounter += 1;
|
|
17351
|
+
return `raw-file-${Date.now()}-${rawFileIdCounter}`;
|
|
17352
|
+
}
|
|
17203
17353
|
function createFileUploadLogic(uploadSource) {
|
|
17204
17354
|
return {
|
|
17205
17355
|
render(io) {
|
|
17356
|
+
const mode = io.props["mode"] === "raw" ? "raw" : "dms";
|
|
17206
17357
|
const files = Array.isArray(io.props["value"]) ? io.props["value"] : [];
|
|
17207
17358
|
const accept = typeof io.props["accept"] === "string" ? io.props["accept"] : void 0;
|
|
17208
17359
|
const multiple = io.props["multiple"] !== false;
|
|
@@ -17211,8 +17362,17 @@ function createFileUploadLogic(uploadSource) {
|
|
|
17211
17362
|
props: { value: files, accept, multiple },
|
|
17212
17363
|
handlers: {
|
|
17213
17364
|
upload: async (payload) => {
|
|
17214
|
-
if (!uploadSource) return;
|
|
17215
17365
|
const incoming = payload ?? [];
|
|
17366
|
+
if (mode === "raw") {
|
|
17367
|
+
await io.runLoading(async () => {
|
|
17368
|
+
const picked = incoming.map((f) => ({ id: nextRawFileId(), ...f }));
|
|
17369
|
+
const next = [...files, ...picked];
|
|
17370
|
+
await io.writeOutput("value", next);
|
|
17371
|
+
await io.emit("committed", { field: "value", old: files, new: next });
|
|
17372
|
+
});
|
|
17373
|
+
return;
|
|
17374
|
+
}
|
|
17375
|
+
if (!uploadSource) return;
|
|
17216
17376
|
await io.runLoading(async () => {
|
|
17217
17377
|
const uploaded = [];
|
|
17218
17378
|
for (const f of incoming) uploaded.push(await uploadSource.upload(f, cabinetId));
|