@colixsystems/widget-sdk 0.172.0 → 0.173.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -2
- package/dist/contract.cjs +13 -3
- package/dist/contract.js +13 -3
- package/dist/index.d.ts +11 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
|
|
|
25
25
|
| **CORE** | `useRouteParams()` | `{ [paramKey]: value }` | `ctx.navigation.currentRoute.params` — no scope. The nav params the previous page passed via `goTo(pageId, params)`; the flat accessor for master→detail (read `recordId` on a detail page). Empty object when none. |
|
|
26
26
|
| **CORE** | `usePageContext()` | `{ params, records }` | `ctx.pageContext` — no scope. The page's DECLARED parameters, resolved once by the host: `params` are coerced to their declared types, `records` holds the row already fetched for each `record` param (read it instead of fetching again). Both empty when the page declares none. |
|
|
27
27
|
| **CORE** | `useWidgetRoute(initial)` | `[state, setState]` | `ctx.widgetRoute` — no scope. Where YOUR WIDGET is, persisted by the host so it survives a reload and travels in a shared link (the `w_<instanceId>` query key on web, the screen's route params natively): the folder a browser has opened, a wizard step, a selected tab, a list's sort and search. `useState` semantics over an object — writes MERGE, `null` clears a key back to its `initial`, and `initial` is read once. Values are scalars or flat arrays of scalars, size- and length-capped; anything else is not stored. NOT history (Back still leaves the page, on both platforms). Degrades to component state on the Studio canvas. |
|
|
28
|
-
| **CORE** | `useWidgetEvent(name)` | `(payload?) => void` | `ctx.events.emit` — no scope. The hook IS the emitter: `const emitSlot = useWidgetEvent("slotChosen")`, then `emitSlot(payload)`. Never destructure the result — there is no `emit` member. |
|
|
28
|
+
| **CORE** | `useWidgetEvent(name)` | `(payload?) => void` | `ctx.events.emit` — no scope. The hook IS the emitter: `const emitSlot = useWidgetEvent("slotChosen")`, then `emitSlot(payload)`. Never destructure the result — there is no `emit` member. A sibling's input reads the payload, and a page action the author binds to the event (Run action, Navigate) runs with it — fired from one control, only that control runs it. |
|
|
29
29
|
| **CORE** | `useWidgetInput(inputName)` | the published payload, or `undefined` | `ctx.inputs` — no scope. Reads a value ANOTHER widget on the same page published with `useWidgetEvent`. Declare the input in `manifest.inputs`; the page author wires it to one sibling's declared event. The channel retains the last payload, so a widget that mounts later still reads it. `undefined` while unwired or before the first publish — always render a sensible default. Page-scoped and ephemeral: use `useRouteParams()` for state that must survive navigation, the datastore for state that must persist. |
|
|
30
30
|
| **CORE** | `useChildRenderer()` | `{ renderNode(node) }` | `ctx.renderer` — no scope (prefer the `WidgetTree` component) |
|
|
31
31
|
| **CORE** | `useFill()` | `boolean` | `ctx.fill` — no scope. `true` when the host sized this widget to fill its page-grid tile's reserved height (containers + media fill by default; the author can override per tile). Media-style widgets switch to a `flex: 1` / `height: "100%"` layout; others ignore it. Defaults `false`. |
|
|
@@ -73,7 +73,23 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
73
73
|
|
|
74
74
|
## Status
|
|
75
75
|
|
|
76
|
-
`v0.
|
|
76
|
+
`v0.173.1` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
|
|
77
|
+
|
|
78
|
+
### What's new in 0.173.1 (contract 1.134.1)
|
|
79
|
+
|
|
80
|
+
**An event your widget fires can run a page action, from the one control that fires it.** The author (or Mason) can bind Run action or Navigate to any event your manifest declares, and it runs when `useWidgetEvent` fires it. Fire the event from one control and only that control runs the action:
|
|
81
|
+
|
|
82
|
+
```jsx
|
|
83
|
+
// manifest: events: [{ name: "onBookPressed", payloadSchema: { classId: { type: "string" } } }]
|
|
84
|
+
const emitBook = useWidgetEvent("onBookPressed");
|
|
85
|
+
<Button title="Book" onPress={() => emitBook({ classId })} />
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Never declare your own event as `onPress`: an `onPress` binding makes the whole widget one tap target, so a tap anywhere on it runs the action. Documentation only — both hosts already dispatched a named event's binding.
|
|
89
|
+
|
|
90
|
+
### What's new in 0.173.0 (contract 1.134.0)
|
|
91
|
+
|
|
92
|
+
**A `datastoreTemplate` RELATION may name any table in the template.** The installer now creates every table before any column, so `targetSuffix` may point at a table declared later in `tables`, and two tables may relate to each other both ways. A suffix no table in the template has is still rejected at publish. No change is needed to an existing template.
|
|
77
93
|
|
|
78
94
|
### What's new in 0.172.0 (contract 1.133.0)
|
|
79
95
|
|
package/dist/contract.cjs
CHANGED
|
@@ -1558,6 +1558,12 @@ const HOOKS = [
|
|
|
1558
1558
|
{
|
|
1559
1559
|
name: "useWidgetEvent",
|
|
1560
1560
|
signature: "useWidgetEvent(eventName)",
|
|
1561
|
+
description:
|
|
1562
|
+
"Fires one of the events the manifest declares. The page wires it two " +
|
|
1563
|
+
"ways: a sibling widget's input reads the payload, and an author-bound " +
|
|
1564
|
+
"page action (Run action, Navigate) runs with it. Fire a named event " +
|
|
1565
|
+
"from ONE control to have only that control run the action; an onPress " +
|
|
1566
|
+
"binding instead makes the whole widget the tap target.",
|
|
1561
1567
|
// sc-4753 — the hook IS the emitter, so its return is declared as a bare
|
|
1562
1568
|
// callable. Naming an `emit` member here made every derived surface tell
|
|
1563
1569
|
// authors to destructure a function, which yields undefined at run time.
|
|
@@ -2633,7 +2639,10 @@ const MANIFEST_SCHEMA = {
|
|
|
2633
2639
|
"{ type, label? } — the SAME shape as propertySchema, NOT JSON Schema — " +
|
|
2634
2640
|
"where type is one of string, number, boolean, object, array. The payload reaches " +
|
|
2635
2641
|
"any sibling widget on the page whose inputs entry the page author wired " +
|
|
2636
|
-
"to this event."
|
|
2642
|
+
"to this event. An event can also run a page action the author binds to " +
|
|
2643
|
+
"it (Run action, Navigate): fired from one control with useWidgetEvent, " +
|
|
2644
|
+
"only that control runs the action. Never declare it as onPress, which " +
|
|
2645
|
+
"the host binds as a tap target over the whole widget.",
|
|
2637
2646
|
default: [],
|
|
2638
2647
|
},
|
|
2639
2648
|
inputs: {
|
|
@@ -2652,7 +2661,7 @@ const MANIFEST_SCHEMA = {
|
|
|
2652
2661
|
type: "object",
|
|
2653
2662
|
required: false,
|
|
2654
2663
|
description:
|
|
2655
|
-
"Optional. Tables the widget needs, seeded into the workspace at install time. Authors wire them into the widget's `tableRef` properties via the Properties Panel — the SDK does not auto-bind. Limits: 8 tables, 24 columns per table. RELATION columns address siblings by `targetSuffix` (
|
|
2664
|
+
"Optional. Tables the widget needs, seeded into the workspace at install time. Authors wire them into the widget's `tableRef` properties via the Properties Panel — the SDK does not auto-bind. Limits: 8 tables, 24 columns per table. RELATION columns address siblings by `targetSuffix` (any table in the same array, in any order; tables may relate to each other both ways). Tables persist across uninstalls. A `tableRef` property may also carry `sharedTable: true`, declaring that the table is the APP'S OWN — the author picks an existing table and the widget seeds nothing for it. That is the correct shape for a widget that reads data it does not own (a chart, a metric, a manager view over another widget's table). `sharedTable` exempts the property from needing a seeded table at publish (`manifest.requiredTableRefsHaveTemplate`) WITHOUT making it optional: the author must still bind one, and the host still refuses to place the widget unbound. Use it instead of dropping `required` — a widget that silently renders an empty tile is the defect it looks like a fix for. sc-7530: a table may also declare `roleGrants` — group-scoped access named SYMBOLICALLY, because a group id belongs to the workspace that installs the widget and could never travel in the manifest. Declare the audiences once at template level as `roles: [{ key, label, description? }]` (key is lowercase kebab, max 8 roles), then per table `roleGrants: [{ role, canRead?, canWrite?, canDelete? }]`. The installing workspace binds each role to one of its own user groups (or has one created) during the install consent step, so a permission model where an admin group does everything and signed-in users only read survives the trip between workspaces. Use `publicGrant` for the two id-less audiences (everyone / any signed-in user) and `roleGrants` for anything that names a group. A role nobody binds simply writes no grant — it never fails the install. sc-7557: a column may declare `encrypted: true` — its values are stored AES-256-GCM encrypted at rest under a per-workspace subkey. The widget still reads and writes PLAINTEXT (the platform encrypts on write, decrypts for end users); studio users see `🔒` instead, and the column cannot be searched, filtered or sorted on. Sample `rows` you seed for it are encrypted the same way. Not valid on `RELATION` — its value is a foreign key the backend must resolve — and publishing one is rejected.",
|
|
2656
2665
|
},
|
|
2657
2666
|
translations: {
|
|
2658
2667
|
type: "object",
|
|
@@ -4661,7 +4670,8 @@ const CONTRACT = deepFreeze({
|
|
|
4661
4670
|
// 1.133.0: additive (sc-9002) — `ListHeader` (column labels on `ListRow`'s frame);
|
|
4662
4671
|
// a `ListRow`'s lone `View` child spans the row; a selected row's bar
|
|
4663
4672
|
// no longer shifts its content. Other rows render as before.
|
|
4664
|
-
|
|
4673
|
+
// 1.134.1: docs (sc-9122) — an emitted event can run a bound action.
|
|
4674
|
+
version: "1.134.1",
|
|
4665
4675
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4666
4676
|
hooks: HOOKS,
|
|
4667
4677
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -1558,6 +1558,12 @@ const HOOKS = [
|
|
|
1558
1558
|
{
|
|
1559
1559
|
name: "useWidgetEvent",
|
|
1560
1560
|
signature: "useWidgetEvent(eventName)",
|
|
1561
|
+
description:
|
|
1562
|
+
"Fires one of the events the manifest declares. The page wires it two " +
|
|
1563
|
+
"ways: a sibling widget's input reads the payload, and an author-bound " +
|
|
1564
|
+
"page action (Run action, Navigate) runs with it. Fire a named event " +
|
|
1565
|
+
"from ONE control to have only that control run the action; an onPress " +
|
|
1566
|
+
"binding instead makes the whole widget the tap target.",
|
|
1561
1567
|
// sc-4753 — the hook IS the emitter, so its return is declared as a bare
|
|
1562
1568
|
// callable. Naming an `emit` member here made every derived surface tell
|
|
1563
1569
|
// authors to destructure a function, which yields undefined at run time.
|
|
@@ -2629,7 +2635,10 @@ const MANIFEST_SCHEMA = {
|
|
|
2629
2635
|
"{ type, label? } — the SAME shape as propertySchema, NOT JSON Schema — " +
|
|
2630
2636
|
"where type is one of string, number, boolean, object, array. The payload reaches " +
|
|
2631
2637
|
"any sibling widget on the page whose inputs entry the page author wired " +
|
|
2632
|
-
"to this event."
|
|
2638
|
+
"to this event. An event can also run a page action the author binds to " +
|
|
2639
|
+
"it (Run action, Navigate): fired from one control with useWidgetEvent, " +
|
|
2640
|
+
"only that control runs the action. Never declare it as onPress, which " +
|
|
2641
|
+
"the host binds as a tap target over the whole widget.",
|
|
2633
2642
|
default: [],
|
|
2634
2643
|
},
|
|
2635
2644
|
inputs: {
|
|
@@ -2648,7 +2657,7 @@ const MANIFEST_SCHEMA = {
|
|
|
2648
2657
|
type: "object",
|
|
2649
2658
|
required: false,
|
|
2650
2659
|
description:
|
|
2651
|
-
"Optional. Tables the widget needs, seeded into the workspace at install time. Authors wire them into the widget's `tableRef` properties via the Properties Panel — the SDK does not auto-bind. Limits: 8 tables, 24 columns per table. RELATION columns address siblings by `targetSuffix` (
|
|
2660
|
+
"Optional. Tables the widget needs, seeded into the workspace at install time. Authors wire them into the widget's `tableRef` properties via the Properties Panel — the SDK does not auto-bind. Limits: 8 tables, 24 columns per table. RELATION columns address siblings by `targetSuffix` (any table in the same array, in any order; tables may relate to each other both ways). Tables persist across uninstalls. A `tableRef` property may also carry `sharedTable: true`, declaring that the table is the APP'S OWN — the author picks an existing table and the widget seeds nothing for it. That is the correct shape for a widget that reads data it does not own (a chart, a metric, a manager view over another widget's table). `sharedTable` exempts the property from needing a seeded table at publish (`manifest.requiredTableRefsHaveTemplate`) WITHOUT making it optional: the author must still bind one, and the host still refuses to place the widget unbound. Use it instead of dropping `required` — a widget that silently renders an empty tile is the defect it looks like a fix for. sc-7530: a table may also declare `roleGrants` — group-scoped access named SYMBOLICALLY, because a group id belongs to the workspace that installs the widget and could never travel in the manifest. Declare the audiences once at template level as `roles: [{ key, label, description? }]` (key is lowercase kebab, max 8 roles), then per table `roleGrants: [{ role, canRead?, canWrite?, canDelete? }]`. The installing workspace binds each role to one of its own user groups (or has one created) during the install consent step, so a permission model where an admin group does everything and signed-in users only read survives the trip between workspaces. Use `publicGrant` for the two id-less audiences (everyone / any signed-in user) and `roleGrants` for anything that names a group. A role nobody binds simply writes no grant — it never fails the install. sc-7557: a column may declare `encrypted: true` — its values are stored AES-256-GCM encrypted at rest under a per-workspace subkey. The widget still reads and writes PLAINTEXT (the platform encrypts on write, decrypts for end users); studio users see `🔒` instead, and the column cannot be searched, filtered or sorted on. Sample `rows` you seed for it are encrypted the same way. Not valid on `RELATION` — its value is a foreign key the backend must resolve — and publishing one is rejected.",
|
|
2652
2661
|
},
|
|
2653
2662
|
translations: {
|
|
2654
2663
|
type: "object",
|
|
@@ -4657,7 +4666,8 @@ const CONTRACT = deepFreeze({
|
|
|
4657
4666
|
// 1.133.0: additive (sc-9002) — `ListHeader` (column labels on `ListRow`'s frame);
|
|
4658
4667
|
// a `ListRow`'s lone `View` child spans the row; a selected row's bar
|
|
4659
4668
|
// no longer shifts its content. Other rows render as before.
|
|
4660
|
-
|
|
4669
|
+
// 1.134.1: docs (sc-9122) — an emitted event can run a bound action.
|
|
4670
|
+
version: "1.134.1",
|
|
4661
4671
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4662
4672
|
hooks: HOOKS,
|
|
4663
4673
|
primitives: PRIMITIVES,
|
package/dist/index.d.ts
CHANGED
|
@@ -160,9 +160,10 @@ export interface WidgetInputDescriptor {
|
|
|
160
160
|
* - At most 24 columns per table. Same reason.
|
|
161
161
|
*
|
|
162
162
|
* RELATION columns reference siblings within the same template via
|
|
163
|
-
* `targetSuffix` — that suffix MUST belong to a table
|
|
164
|
-
*
|
|
165
|
-
*
|
|
163
|
+
* `targetSuffix` — that suffix MUST belong to a table in the same array,
|
|
164
|
+
* declared anywhere in it; tables may relate to each other both ways.
|
|
165
|
+
* There is intentionally no way to reference a table outside the
|
|
166
|
+
* widget's own template.
|
|
166
167
|
*/
|
|
167
168
|
export interface WidgetDatastoreTemplateColumn {
|
|
168
169
|
name: string;
|
|
@@ -896,6 +897,13 @@ export interface DirectoryResult {
|
|
|
896
897
|
*/
|
|
897
898
|
export function useDirectory(query?: DirectoryQuery): DirectoryResult;
|
|
898
899
|
|
|
900
|
+
/**
|
|
901
|
+
* The emitter for an event the manifest declares. A sibling's wired input
|
|
902
|
+
* reads the payload, and a page action the author binds to the event (Run
|
|
903
|
+
* action, Navigate) runs with it — fired from one control, only that control
|
|
904
|
+
* runs it. Never declare an event named `onPress`: that binding makes the
|
|
905
|
+
* whole widget one tap target.
|
|
906
|
+
*/
|
|
899
907
|
export function useWidgetEvent(name: string): (payload?: unknown) => void;
|
|
900
908
|
|
|
901
909
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.173.1",
|
|
4
4
|
"description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
|
|
5
5
|
"homepage": "https://github.com/Colix-AB/AppStudio",
|
|
6
6
|
"type": "module",
|