@colixsystems/widget-sdk 0.60.0 → 0.61.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/README.md CHANGED
@@ -21,6 +21,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
21
21
  | **CORE** | `useWidgetStyle()` | `{ [styleField]: value }` | `ctx.props.style` — no scope. The author-set per-widget style values declared in `manifest.styleSchema`; apply each onto whatever element you choose. |
22
22
  | **CORE** | `useUser()` | `{ id, email, displayName, roles, groupIds }` | `ctx.user` (host-built context, **camelCase** — not a wire payload; `id` null when anonymous) — no scope |
23
23
  | **CORE** | `useNavigation()` | `{ goTo, goBack, push, replace, back, currentRoute }` | `ctx.navigation` — no scope (external URLs use the `Linking` primitive) |
24
+ | **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. |
24
25
  | **CORE** | `useWidgetEvent(name)` | `(payload?) => void` | `ctx.events.emit` — no scope |
25
26
  | **CORE** | `useChildRenderer()` | `{ renderNode(node) }` | `ctx.renderer` — no scope (prefer the `WidgetTree` component) |
26
27
  | **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`. |
@@ -384,6 +385,7 @@ The "split-implementation + vetted package list" pivot.
384
385
  ### What's new in 0.11.0
385
386
 
386
387
  - **`useNavigation()` is wired.** Returns the host-provided navigation surface `{ goTo, goBack, push, replace, back, currentRoute }` for internal page-to-page navigation. Missing methods degrade to no-ops on the Studio canvas preview. Additive.
388
+ - **`useRouteParams()` reads the nav params.** Returns `currentRoute.params` — the bag a `goTo(pageId, params)` carried to this page. The flat accessor for master→detail: navigate with `goTo(detailPageId, { recordId: row.id })`, then read `const { recordId } = useRouteParams()`. It is an OBJECT — read a param off it, never call it. Empty object when the page was opened without params. Additive (v0.61.0).
387
389
  - **`Linking` primitive re-exported.** `Linking.openURL(url)` opens an external URL with the OS handler — web (`react-native-web`) maps to `window.open` / `location.href`; native hands off to the system. Use this for external URLs; use `useNavigation().goTo(pageId)` for internal pages.
388
390
 
389
391
  ### What's new in 0.10.0
@@ -454,7 +456,7 @@ import { defineWidget, validateManifest, useDatastoreQuery, Text, View } from "@
454
456
 
455
457
  - `defineWidget({ manifest, component })` — validates the manifest and produces a widget module the host can register.
456
458
  - `validateManifest(m)` / `validatePropertySchema(s)` / `validateProps(schema, props)` — shape validation; no third-party deps.
457
- - `useDatastoreQuery`, `useDatastoreRecord`, `useDatastoreSchema`, `useDatastoreMutation`, `useDirectory`, `useUsers`, `useGroups`, `useRecordPermissions`, `useAsset`, `useWidgetEvent`, `usePayments`, `useSendNotification`, `useTheme`, `useI18n`, `useUser`, `useNavigation`, `useChildRenderer`, `useClipboard`, `useToast` — hooks that read from the host-provided `WidgetContext` (or, for `useClipboard`, the platform clipboard API directly). `useDirectory(query?)` returns `{ users, loading, error, refetch }` (each user `{ id, name, role }`) and requires the `directory.read:users` scope. `useUsers(query?)` returns `{ users, loading, error, refetch, invite, deactivate, reactivate, remove }` and requires `users.read:*` (mutations also need `users.write:*`); rejections are a `DirectoryError`. `useGroups(query?)` returns `{ groups, loading, error, refetch, create, remove, addMember, removeMember }` and requires `groups.read:*` (mutations also need `groups.write:*`). `usePayments()` returns `{ requestPayment, getPayment }` and requires the `payments.charge:appUser` scope; `requestPayment(...)` rejects with a `PaymentError`. `useSendNotification()` returns `{ send, sending, error }` and requires the `notifications.send:appUser` scope; `send({ recipient_user_id, title, body, link?, payload? })` notifies one app user in the same workspace (cross-workspace `recipient_user_id` is rejected), must be called from an event handler rather than render, and rejects with a `NotificationError`. `useUser()` returns the active end-user identity `{ id, email, displayName, roles, groupIds }` (camelCase — the host-built context object, not a wire payload; `id` is `null` for anonymous / preview). `useNavigation()` returns `{ goTo, goBack, push, replace, back, currentRoute }` for internal page navigation — for external URLs use the `Linking` primitive (`Linking.openURL(url)`). `useDatastoreRecord(tableId, recordId)` returns `{ data, loading, error, refetch }` for a single record (data is one row or null). `useDatastoreSchema(tableId)` returns `{ schema, loading, error, refetch }` where `schema` is `{ id, name, columns: [{ id, name, data_type, required, relation_type, target_table_id, is_identification }] }` (structure only, no row data; snake_case verbatim) — use it to resolve a stored `columnId` to its column type at runtime; requires the `datastore.read:<table>` scope. `useAsset(fileId)` returns `{ url, file, loading, error, refetch }` — the `url` is an absolute URL composed against the host's API base. `useChildRenderer()` returns `{ renderNode(node) }` — container widgets call it to render arbitrary child page-tree nodes (prefer the `WidgetTree` component for the common case).
459
+ - `useDatastoreQuery`, `useDatastoreRecord`, `useDatastoreSchema`, `useDatastoreMutation`, `useDirectory`, `useUsers`, `useGroups`, `useRecordPermissions`, `useAsset`, `useWidgetEvent`, `usePayments`, `useSendNotification`, `useTheme`, `useI18n`, `useUser`, `useNavigation`, `useRouteParams`, `useChildRenderer`, `useClipboard`, `useToast` — hooks that read from the host-provided `WidgetContext` (or, for `useClipboard`, the platform clipboard API directly). `useDirectory(query?)` returns `{ users, loading, error, refetch }` (each user `{ id, name, role }`) and requires the `directory.read:users` scope. `useUsers(query?)` returns `{ users, loading, error, refetch, invite, deactivate, reactivate, remove }` and requires `users.read:*` (mutations also need `users.write:*`); rejections are a `DirectoryError`. `useGroups(query?)` returns `{ groups, loading, error, refetch, create, remove, addMember, removeMember }` and requires `groups.read:*` (mutations also need `groups.write:*`). `usePayments()` returns `{ requestPayment, getPayment }` and requires the `payments.charge:appUser` scope; `requestPayment(...)` rejects with a `PaymentError`. `useSendNotification()` returns `{ send, sending, error }` and requires the `notifications.send:appUser` scope; `send({ recipient_user_id, title, body, link?, payload? })` notifies one app user in the same workspace (cross-workspace `recipient_user_id` is rejected), must be called from an event handler rather than render, and rejects with a `NotificationError`. `useUser()` returns the active end-user identity `{ id, email, displayName, roles, groupIds }` (camelCase — the host-built context object, not a wire payload; `id` is `null` for anonymous / preview). `useNavigation()` returns `{ goTo, goBack, push, replace, back, currentRoute }` for internal page navigation — for external URLs use the `Linking` primitive (`Linking.openURL(url)`). `useRouteParams()` returns the current route's params object (`currentRoute.params`) — the flat master→detail accessor; read a param off it (e.g. `recordId`), never call it. `useDatastoreRecord(tableId, recordId)` returns `{ data, loading, error, refetch }` for a single record (data is one row or null). `useDatastoreSchema(tableId)` returns `{ schema, loading, error, refetch }` where `schema` is `{ id, name, columns: [{ id, name, data_type, required, relation_type, target_table_id, is_identification }] }` (structure only, no row data; snake_case verbatim) — use it to resolve a stored `columnId` to its column type at runtime; requires the `datastore.read:<table>` scope. `useAsset(fileId)` returns `{ url, file, loading, error, refetch }` — the `url` is an absolute URL composed against the host's API base. `useChildRenderer()` returns `{ renderNode(node) }` — container widgets call it to render arbitrary child page-tree nodes (prefer the `WidgetTree` component for the common case).
458
460
  - `WidgetTree({ node })` — component that renders an author-authored child node through the host's renderer; used by Tabs / Card / custom containers to host arbitrary child widgets.
459
461
  - `Text`, `View`, `Pressable`, `Image`, `ScrollView`, `TextInput`, `FlatList`, `SectionList`, `ActivityIndicator`, `Switch`, `StyleSheet`, `Linking`, `Icon`, `DateTimePicker` — re-exported from `react-native` (the RN primitives) or implemented in the SDK (`Icon` wraps `lucide-react-native`; `DateTimePicker` wraps `@react-native-community/datetimepicker` on native and renders `<input type="date|time|datetime-local">` directly on web because the RN library has no react-native-web mapping). The web build aliases `react-native` to `react-native-web` so the RN-re-exported primitives render in the browser without any per-platform code; the exported Expo app's Metro bundler resolves the real `react-native` library. `Linking` is a static API (`Linking.openURL(url)`) — use it for external URLs, and use `useNavigation().goTo(pageId)` for internal page navigation. See https://reactnative.dev/docs/ for per-component props.
460
462
  - `WidgetContextProvider` — React context provider that the host (Studio, Player, exported app) wraps widgets with.
package/dist/contract.cjs CHANGED
@@ -150,6 +150,16 @@ const HOOKS = [
150
150
  requiredContextSlice: ["navigation"],
151
151
  scopes: null,
152
152
  },
153
+ {
154
+ name: "useRouteParams",
155
+ signature: "useRouteParams()",
156
+ returnShape: {
157
+ "<paramKey>":
158
+ "a navigation param value the previous page passed via goTo(pageId, params) — usually a string id, e.g. recordId on a detail page",
159
+ },
160
+ requiredContextSlice: ["navigation"],
161
+ scopes: null,
162
+ },
153
163
  {
154
164
  name: "useDatastoreRecord",
155
165
  signature: "useDatastoreRecord(tableId, recordId)",
package/dist/contract.js CHANGED
@@ -150,6 +150,16 @@ const HOOKS = [
150
150
  requiredContextSlice: ["navigation"],
151
151
  scopes: null,
152
152
  },
153
+ {
154
+ name: "useRouteParams",
155
+ signature: "useRouteParams()",
156
+ returnShape: {
157
+ "<paramKey>":
158
+ "a navigation param value the previous page passed via goTo(pageId, params) — usually a string id, e.g. recordId on a detail page",
159
+ },
160
+ requiredContextSlice: ["navigation"],
161
+ scopes: null,
162
+ },
153
163
  {
154
164
  name: "useDatastoreRecord",
155
165
  signature: "useDatastoreRecord(tableId, recordId)",
package/dist/hooks.js CHANGED
@@ -249,6 +249,27 @@ export function useNavigation() {
249
249
  return ctx.navigation;
250
250
  }
251
251
 
252
+ const EMPTY_PARAMS = Object.freeze({});
253
+
254
+ /**
255
+ * Returns the current route's navigation params — the bag a `goTo(pageId, params)`
256
+ * carried to this page (a query string on web, react-navigation route params on
257
+ * native). This is the flat accessor for the master→detail pattern: a list widget
258
+ * navigates with `goTo(detailPageId, { recordId: row.id })`, and the detail widget
259
+ * reads `const { recordId } = useRouteParams()`. Returns an empty object on a page
260
+ * opened without params (the Studio canvas, a direct deep link), so reads are
261
+ * always safe. Equivalent to `useNavigation().currentRoute.params`, but without the
262
+ * deep chain — read a param off it, never call it.
263
+ */
264
+ export function useRouteParams() {
265
+ const ctx = useWidgetContextOrThrow("useRouteParams");
266
+ const nav = ctx.navigation || {};
267
+ const route = nav.currentRoute || {};
268
+ // Stable reference on the empty path so `useEffect([params])` doesn't re-fire
269
+ // every render on a paramless page.
270
+ return route.params || EMPTY_PARAMS;
271
+ }
272
+
252
273
  /**
253
274
  * Returns { t, locale }. `t(key, fallback)` resolves `{{t:key}}` against
254
275
  * the host's translation table and falls back to `fallback ?? key` when
package/dist/index.d.ts CHANGED
@@ -925,6 +925,15 @@ export function useNavigation(): {
925
925
  currentRoute: { pageId: string; params: Record<string, unknown> };
926
926
  };
927
927
 
928
+ /**
929
+ * Returns the current route's navigation params — the bag a `goTo(pageId, params)`
930
+ * carried to this page. The flat accessor for master→detail: navigate with
931
+ * `goTo(detailPageId, { recordId: row.id })`, then read
932
+ * `const { recordId } = useRouteParams()`. Empty object when the page was opened
933
+ * without params. Equivalent to `useNavigation().currentRoute.params`.
934
+ */
935
+ export function useRouteParams(): Record<string, unknown>;
936
+
928
937
  /**
929
938
  * Static API for external URLs. `openURL(url)` opens a URL with the OS
930
939
  * handler (web: react-native-web maps to `window.open` / `location.href`;
package/dist/index.js CHANGED
@@ -42,6 +42,7 @@ export {
42
42
  useUser,
43
43
  useFill,
44
44
  useNavigation,
45
+ useRouteParams,
45
46
  useChildRenderer,
46
47
  useRefresh,
47
48
  useGeolocation,
@@ -42,6 +42,7 @@ export {
42
42
  useUser,
43
43
  useFill,
44
44
  useNavigation,
45
+ useRouteParams,
45
46
  useChildRenderer,
46
47
  useRefresh,
47
48
  useGeolocation,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.60.0",
3
+ "version": "0.61.0",
4
4
  "description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",