@colixsystems/widget-sdk 0.179.0 → 0.180.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 +17 -3
- package/dist/button-style.js +94 -0
- package/dist/button-view.js +15 -4
- package/dist/card-style.js +30 -1
- package/dist/card-view.js +10 -4
- package/dist/contract.cjs +27 -3
- package/dist/contract.js +27 -3
- package/dist/hooks.js +22 -0
- package/dist/host.d.ts +15 -0
- package/dist/host.js +1 -1
- package/dist/index.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
|
|
|
57
57
|
| **FILES** (`ctx.assets`) | `useAsset(id)` | `{ url, file, loading, error, refetch }` | `ctx.assets.get` — no scope |
|
|
58
58
|
| **FILES** | `useAssetsByTag(tag, { type })` (optional: the whole argument, and `type` within it) | `{ assets, loading, error, refetch }` | `ctx.assets.list` (unwraps `{ data, meta }` to `assets`) — no scope. `type` defaults to `"image"`; pass `"all"` / `"audio"` / `"video"` / `"document"` to widen. Falsy `tag` collapses to `assets: []` without a round-trip. |
|
|
59
59
|
| **DIRECTORY** (`ctx.directory`) | `useDirectory(query)` (optional: query) | `{ users, loading, error, refetch }` | `directory.users.list` — `directory.read:users` |
|
|
60
|
-
| **DIRECTORY** | `useUsers(query)` (optional: query) | `{ users, loading, error, refetch, invite, deactivate, reactivate, remove, sendPasswordReset }` | `directory.users.*` — `users.read:*` (edits, incl. `sendPasswordReset()`, also `users.write:*`; `remove()` also `users.delete:*`) |
|
|
60
|
+
| **DIRECTORY** | `useUsers(query)` (optional: query) | `{ users, loading, error, refetch, invite, deactivate, reactivate, remove, sendPasswordReset }` | `directory.users.*` — `users.read:*` plus the `users.read` (or `users.write`) capability, else `FORBIDDEN` (edits, incl. `sendPasswordReset()`, also `users.write:*`; `remove()` also `users.delete:*`) |
|
|
61
61
|
| **DIRECTORY** | `useGroups(query)` (optional: query) | `{ groups, loading, error, refetch, create, remove, addMember, removeMember }` | `directory.groups.*` — `groups.read:*` (mutations also `groups.write:*`) |
|
|
62
62
|
| **DIRECTORY** | `useInvites(query)` (optional: query) | `{ invites, loading, error, refetch, resend, revoke }` | `directory.invites.*` — `users.write:*` + the SystemAcl `users.write` capability (the whole invite surface, list included). `query` is `{ status?, limit?, offset? }` with `status` ∈ `pending \| accepted \| revoked \| expired \| all` (endpoint default `all`). |
|
|
63
63
|
| **DIRECTORY** | `useBankIdLink()` | `{ linked, available, status, qr, message, startLink, refresh, cancel, unlink, refetchStatus, … }` | `directory.bankid.*` — no scope (JWT-gated self-service) |
|
|
@@ -74,7 +74,21 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
74
74
|
|
|
75
75
|
## Status
|
|
76
76
|
|
|
77
|
-
`v0.
|
|
77
|
+
`v0.180.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**.
|
|
78
|
+
|
|
79
|
+
### What's new in 0.180.1 (contract 1.141.0 — unchanged)
|
|
80
|
+
|
|
81
|
+
**A widget built in the Studio turns on the device capability it uses.** The `react-native-nfc-manager` notes said the publisher must enable the NFC tags capability in Publish settings, and told the widget to say so from its error state. Nobody reads a widget's error state before building: a workspace whose widget imported the package shipped an Android build without the `NFC` permission, and the library — which touches the adapter on every activity pause — closed the app at each permission prompt, file picker and camera open. Publishing a widget from Mason or the widget agent now turns on every device capability its source uses (NFC tags for this package; step counting and activity data for `Pedometer` from `expo-sensors`, and only for `Pedometer`). A widget installed any other way is unchanged: its publisher still enables the capability in Publish settings. The notes now say both, and the error state says what is true on the phone — this build does not include NFC and needs a new one. No export, type or rule changed.
|
|
82
|
+
|
|
83
|
+
### What's new in 0.180.0 (contract 1.141.0)
|
|
84
|
+
|
|
85
|
+
**`<Button>` stays readable on a coloured section.** A section that paints its own fill (a card or band on the theme's colours, or a gradient wash) already gave its text a colour that reads there. Its buttons kept the page's paint, so an `outlined` button drew its label and border in the primary colour on a band washed in that same colour, and a `secondary` fill disappeared into it. A button there is now measured against the section by the same rule as its text: a colour that already reads is kept, and one the section makes unreadable takes the section's text colour, but only when that reads better. "The section makes it unreadable" is measured against the surface the look was designed for: a colour moves only when the section costs it at least a third of the contrast it had there, so a white or lightly tinted panel changes nothing.
|
|
86
|
+
|
|
87
|
+
- `outlined`: a label under 4.5:1 and a border under 3:1 against the section are re-inked.
|
|
88
|
+
- `secondary`: a fill under 3:1 against the section becomes the section's outlined button.
|
|
89
|
+
- `primary` and `danger`: a fill under 3:1 turns inverse, with the section's text colour as its fill and its own colour as its label, so it still leads the quieter buttons. Where that label would not reach 4.5:1, or you set the label colour, the fill is kept and gains an edge in the section's text colour instead.
|
|
90
|
+
|
|
91
|
+
A colour you pass in `fields`, `style` (`backgroundColor`, `borderColor`) or `labelStyle` (`color`), or that the app's button look sets, is never changed. A `<Card>` inside such a section becomes the surface its content reads: it re-derives `surface`, `onSurface`, `border` and the rest from its own fill, as a layout container does, and its buttons are measured against it. Inside a section `useTheme().colors.backdrop` is the fill your widget sits on and `colors.backdropBase` the surface the look was designed for; on the page itself both are absent.
|
|
78
92
|
|
|
79
93
|
### What's new in 0.179.0 (contract 1.140.0)
|
|
80
94
|
|
|
@@ -634,7 +648,7 @@ Also tightened in this release: the `publicGrant` flags are now type-checked. Th
|
|
|
634
648
|
**NFC tags are vetted, native-only (`react-native-nfc-manager`).** A widget can now read and write NFC tags on the Expo export — NDEF over `NfcManager.requestTechnology(NfcTech.Ndef)`, plus the raw technologies a badge, asset tag or transit card uses: `NfcTech.IsoDep` on both hosts, `NfcTech.MifareClassic` on Android, `NfcTech.MifareIOS` / `FelicaIOS` / `Iso15693IOS` on iOS. (There is no `NfcTech.FeliCa` — check `index.d.ts` before naming one.) This is the last common "tap a physical thing" input the allowlist was missing; barcode and QR already had `useBarcodeScanner()`.
|
|
635
649
|
|
|
636
650
|
- **Native-only, and the browser is why.** Web NFC ships in Chrome on Android alone — Safari, Firefox and every desktop browser omit it — so there is no web build to pair with. Put the import in `widget.native.jsx` and give `widget.web.jsx` a real way in: a typed code field, or the SDK's `useBarcodeScanner()` QR path. The linter's `import-platform-mismatch` enforces the split.
|
|
637
|
-
- **
|
|
651
|
+
- **A per-workspace device capability — and `isSupported()` cannot see it.** The export claims the iOS NFC entitlement and the Android `NFC` permission only while the workspace's **NFC tags** device capability is on. A widget built in the Studio by Mason or the widget agent turns it on when it is published; for any other widget the publisher enables it in Publish settings. Either way it reaches a phone only with the next native build. `isSupported()` probes the **device** (`readingAvailable` on iOS, `hasSystemFeature` on Android), not the gate: on a capable phone running a build made without the capability, it returns `true` and the read then fails — Android throws a `SecurityException` from `registerTagEvent`, iOS cannot open the reader session. So wrap `start()` / `requestTechnology()` in a `try` and render a clear **error state** from the `catch` — say this build of the app does not include NFC and that it needs a new build. That `catch` is an error path, **not** the web build's typed-code substitute: do not give `widget.native.jsx` a permanent manual-entry field beside the tap target, because in a correctly-configured app the radio works and the field is the typing the tag was meant to replace.
|
|
638
652
|
- **Clean up the session.** `cancelTechnologyRequest()` in your effect cleanup; an abandoned request leaves the reader sheet up on iOS.
|
|
639
653
|
- **It is a write capability too.** Read with `getNdefMessage()`; write with `writeNdefMessage(bytes)`, `formatNdef(bytes)` (Android only, for a blank tag) and `transceive(bytes)` on the `IsoDep` / `NfcA` / `NfcV` handlers. One capability covers both — Android expresses read and write through the single `android.permission.NFC` — so the workspace owner who enables it is consenting to every widget in the workspace being able to rewrite the tags their users tap. Raw ISO7816 `transceive` on iOS additionally needs `select-identifiers` on the entitlement, which the export does not declare; NDEF read and write work on both hosts.
|
|
640
654
|
- **`makeReadOnly()` is irreversible.** It permanently locks the tag — no later write will ever succeed, on any device, by any app. Call it only where the feature genuinely wants a one-time-programmable tag, and never as a cleanup step after writing.
|
package/dist/button-style.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// the built-in widgets (frontend `_shared/buttonStyle.js` re-exports these).
|
|
3
3
|
// Platform-free, so both hosts resolve a button identically.
|
|
4
4
|
|
|
5
|
+
import { INK_CONTRAST, contrastRatio, inkForBackdrop, isHexColor } from "./contract.js";
|
|
6
|
+
|
|
5
7
|
export const BUTTON_PADDING_MAX = 64;
|
|
6
8
|
export const BUTTON_BORDER_WIDTH_MAX = 8;
|
|
7
9
|
|
|
@@ -58,3 +60,95 @@ export function selfAlignStyle(style) {
|
|
|
58
60
|
const alignSelf = SELF_ALIGNMENTS[style && style.align];
|
|
59
61
|
return alignSelf ? { alignSelf } : null;
|
|
60
62
|
}
|
|
63
|
+
|
|
64
|
+
// How much of its page contrast a section must cost a colour before it moves:
|
|
65
|
+
// a tint or neutral panel never does, a coloured band always does.
|
|
66
|
+
const SECTION_LOSS = 1.5;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A variant's own paint, measured against the painted section it sits on
|
|
70
|
+
* (`colors.backdrop`) by the rule that section's text follows. A colour moves
|
|
71
|
+
* only where it is unreadable there AND the section is what broke it, measured
|
|
72
|
+
* against the surface the look was designed for (`colors.backdropBase`). Only
|
|
73
|
+
* what the look painted moves; `owned` names what the maker set.
|
|
74
|
+
*
|
|
75
|
+
* A transparent button re-inks its label and border separately. A fill the
|
|
76
|
+
* section swallows is replaced: a quiet `secondary` becomes the section's
|
|
77
|
+
* outlined button, and an emphatic fill turns inverse (the section's ink as
|
|
78
|
+
* fill, its own colour as label), so it still outranks the quiet ones. Where
|
|
79
|
+
* that label would not read, or the maker chose it, the fill gains an edge.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} variant
|
|
82
|
+
* @param {{container: object, ink: string}} paint
|
|
83
|
+
* @param {object} colors - the surface-aware theme colours.
|
|
84
|
+
* @param {{fill?: boolean, label?: boolean, border?: boolean}} [owned]
|
|
85
|
+
* @returns {{container: object, ink: string}} `paint` itself when nothing moves.
|
|
86
|
+
*/
|
|
87
|
+
export function paintOnBackdrop(variant, paint, colors, owned = {}) {
|
|
88
|
+
const { backdrop, backdropBase, onSurface: ink } = colors || {};
|
|
89
|
+
if (owned.fill || ![backdrop, backdropBase, ink].every(isHexColor)) return paint;
|
|
90
|
+
const reink = (colour, minimum) =>
|
|
91
|
+
isHexColor(colour) && brokenBySection(colour, backdrop, backdropBase, minimum)
|
|
92
|
+
? inkForBackdrop(colour, ink, backdrop, minimum)
|
|
93
|
+
: colour;
|
|
94
|
+
const { container } = paint;
|
|
95
|
+
const fill = container.backgroundColor;
|
|
96
|
+
if (fill === "transparent") return reinkOutline(paint, reink, owned);
|
|
97
|
+
if (reink(fill, INK_CONTRAST.boundary) === fill) return paint;
|
|
98
|
+
if (!owned.label && variant === "secondary") {
|
|
99
|
+
return {
|
|
100
|
+
container: { backgroundColor: "transparent", borderWidth: 1, borderColor: ink },
|
|
101
|
+
ink,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (!owned.label && contrastRatio(fill, ink) >= INK_CONTRAST.text) {
|
|
105
|
+
const edge = container.borderColor ? { borderColor: ink } : null;
|
|
106
|
+
return { container: { ...container, backgroundColor: ink, ...edge }, ink: fill };
|
|
107
|
+
}
|
|
108
|
+
if (owned.border) return paint;
|
|
109
|
+
return { container: { ...container, borderWidth: 1, borderColor: ink }, ink: paint.ink };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function brokenBySection(colour, backdrop, base, minimum) {
|
|
113
|
+
const here = contrastRatio(colour, backdrop);
|
|
114
|
+
return here < minimum && here * SECTION_LOSS <= contrastRatio(colour, base);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function reinkOutline(paint, reink, owned) {
|
|
118
|
+
const { borderColor } = paint.container;
|
|
119
|
+
const label = owned.label ? paint.ink : reink(paint.ink, INK_CONTRAST.text);
|
|
120
|
+
const border = owned.border ? borderColor : reink(borderColor, INK_CONTRAST.boundary);
|
|
121
|
+
if (label === paint.ink && border === borderColor) return paint;
|
|
122
|
+
return { container: { ...paint.container, borderColor: border }, ink: label };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const SIDE_BORDER_COLORS = [
|
|
126
|
+
"borderColor",
|
|
127
|
+
"borderTopColor",
|
|
128
|
+
"borderRightColor",
|
|
129
|
+
"borderBottomColor",
|
|
130
|
+
"borderLeftColor",
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Which of a button's fill, label and border its caller set, so
|
|
135
|
+
* `paintOnBackdrop` never replaces them: the surface `fields`, and colours in
|
|
136
|
+
* the flattened container `style` and `labelStyle`.
|
|
137
|
+
*/
|
|
138
|
+
export function ownedButtonPaint(variant, fields, style, labelStyle) {
|
|
139
|
+
const f = fields || {};
|
|
140
|
+
const s = style || {};
|
|
141
|
+
const danger = variant === "danger";
|
|
142
|
+
return {
|
|
143
|
+
fill: Boolean(
|
|
144
|
+
f.background || f.gradient || s.backgroundColor || (danger && f.dangerBackground),
|
|
145
|
+
),
|
|
146
|
+
label: Boolean(f.textColor || (labelStyle && labelStyle.color) || (danger && f.dangerTextColor)),
|
|
147
|
+
border: Boolean(
|
|
148
|
+
f.borderColor ||
|
|
149
|
+
f.ringColor ||
|
|
150
|
+
SIDE_BORDER_COLORS.some((key) => s[key]) ||
|
|
151
|
+
(danger && f.dangerBorderColor),
|
|
152
|
+
),
|
|
153
|
+
};
|
|
154
|
+
}
|
package/dist/button-view.js
CHANGED
|
@@ -8,7 +8,12 @@ import { DEFAULT_THEME_TOKENS } from "./_theme-tokens.js";
|
|
|
8
8
|
import { lucideGlyph } from "./icon.js";
|
|
9
9
|
import { applyThemeComponentStyle } from "./theme-components.js";
|
|
10
10
|
import { cornerRadiusOverlay, shadowStyle, gradientProps } from "./card-style.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
buttonSurfaceOverlay,
|
|
13
|
+
buttonSurfaceTextOverlay,
|
|
14
|
+
ownedButtonPaint,
|
|
15
|
+
paintOnBackdrop,
|
|
16
|
+
} from "./button-style.js";
|
|
12
17
|
|
|
13
18
|
export const BUTTON_VARIANTS = Object.freeze(["primary", "secondary", "outlined", "danger"]);
|
|
14
19
|
|
|
@@ -39,8 +44,8 @@ function variantPaint(variant, colors) {
|
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
// Precedence, weakest first: variant, danger scope, ring, then the fields.
|
|
42
|
-
function containerLayers(variant, f, colors) {
|
|
43
|
-
const paint = variantPaint(variant, colors);
|
|
47
|
+
function containerLayers(variant, f, colors, owned) {
|
|
48
|
+
const paint = paintOnBackdrop(variant, variantPaint(variant, colors), colors, owned);
|
|
44
49
|
let ink = paint.ink;
|
|
45
50
|
const layers = [paint.container];
|
|
46
51
|
if (variant === "danger") {
|
|
@@ -115,7 +120,13 @@ export function makeButton(rn) {
|
|
|
115
120
|
? fields || {}
|
|
116
121
|
: applyThemeComponentStyle(THEMED_AS, theme, { style: fields }).style || {};
|
|
117
122
|
const resolvedVariant = BUTTON_VARIANTS.includes(variant) ? variant : "primary";
|
|
118
|
-
const
|
|
123
|
+
const owned = ownedButtonPaint(
|
|
124
|
+
resolvedVariant,
|
|
125
|
+
f,
|
|
126
|
+
StyleSheet.flatten(style),
|
|
127
|
+
StyleSheet.flatten(labelStyle),
|
|
128
|
+
);
|
|
129
|
+
const { layers, ink } = containerLayers(resolvedVariant, f, colors, owned);
|
|
119
130
|
|
|
120
131
|
const fontSize =
|
|
121
132
|
typeof f.fontSize === "number" && Number.isFinite(f.fontSize)
|
package/dist/card-style.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Every resolver emits ONLY the fields that are set, so a widget's own base
|
|
6
6
|
// style keeps whatever the author left alone.
|
|
7
7
|
|
|
8
|
-
import { CONTRACT, normaliseComponentGradient } from "./contract.js";
|
|
8
|
+
import { CONTRACT, isHexColor, mixHex, normaliseComponentGradient } from "./contract.js";
|
|
9
9
|
import { normaliseCornerRadius, cornerRadiusStyle } from "./corner-radius.js";
|
|
10
10
|
import { normaliseSpacing, spacingStyle, isZeroSpacing } from "./spacing.js";
|
|
11
11
|
|
|
@@ -118,6 +118,35 @@ export function gradientProps(value) {
|
|
|
118
118
|
: null;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* The colour a card's buttons are measured against: its opaque flat fill, or
|
|
123
|
+
* a gradient's midpoint, as a layout container's ink basis is read. `null`
|
|
124
|
+
* (a translucent or unknown fill) keeps the backdrop the card inherited.
|
|
125
|
+
*
|
|
126
|
+
* @param {object|Array} styles - the card's container style, as composed.
|
|
127
|
+
* @param {{colors: string[]}|null} gradient - its `gradientProps`.
|
|
128
|
+
*/
|
|
129
|
+
export function cardBackdrop(styles, gradient) {
|
|
130
|
+
if (gradient) {
|
|
131
|
+
const [from, to] = gradient.colors;
|
|
132
|
+
return isOpaqueHex(from) && isOpaqueHex(to) ? mixHex(from, to, 0.5) : null;
|
|
133
|
+
}
|
|
134
|
+
let fill = null;
|
|
135
|
+
for (const style of [styles].flat(Infinity)) {
|
|
136
|
+
if (style && typeof style === "object" && style.backgroundColor !== undefined) {
|
|
137
|
+
fill = style.backgroundColor;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return isOpaqueHex(fill) ? fill : null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// The colour maths ignores alpha, so a see-through fill cannot be judged.
|
|
144
|
+
function isOpaqueHex(value) {
|
|
145
|
+
if (!isHexColor(value)) return false;
|
|
146
|
+
const hex = value.slice(1);
|
|
147
|
+
return hex.length !== 8 || hex.slice(6).toLowerCase() === "ff";
|
|
148
|
+
}
|
|
149
|
+
|
|
121
150
|
/** The card-surface gradient (`cardGradient`). */
|
|
122
151
|
export function cardGradientProps(style) {
|
|
123
152
|
return gradientProps(style && style.cardGradient);
|
package/dist/card-view.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// swap lives here once rather than in each widget.
|
|
7
7
|
|
|
8
8
|
import React from "react";
|
|
9
|
-
import { useHostTheme } from "./hooks.js";
|
|
9
|
+
import { useHostTheme, BackdropScope } from "./hooks.js";
|
|
10
10
|
import { DEFAULT_THEME_TOKENS } from "./_theme-tokens.js";
|
|
11
11
|
import { CONTRACT } from "./contract.js";
|
|
12
12
|
import {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
cardSurfaceStyle,
|
|
15
15
|
cardGradientProps,
|
|
16
16
|
cardLayoutStyle,
|
|
17
|
+
cardBackdrop,
|
|
17
18
|
} from "./card-style.js";
|
|
18
19
|
import { normaliseThemeComponents } from "./theme-components.js";
|
|
19
20
|
|
|
@@ -134,12 +135,17 @@ export function makeCard(rn) {
|
|
|
134
135
|
|
|
135
136
|
const overlay = withoutMargin ? cardSurfaceStyle(fields) : cardOverlay(fields);
|
|
136
137
|
const gradient = cardGradientProps(fields);
|
|
138
|
+
const basis = cardBackdrop(
|
|
139
|
+
[base, overlay, typeof containerStyle === "function" ? null : containerStyle],
|
|
140
|
+
gradient,
|
|
141
|
+
);
|
|
142
|
+
const scoped = React.createElement(BackdropScope, { basis }, children);
|
|
137
143
|
|
|
138
144
|
if (!gradient) {
|
|
139
145
|
return React.createElement(
|
|
140
146
|
Container,
|
|
141
147
|
{ style: composeStyle([base, overlay, layout], containerStyle), ...rest },
|
|
142
|
-
|
|
148
|
+
scoped,
|
|
143
149
|
);
|
|
144
150
|
}
|
|
145
151
|
|
|
@@ -156,7 +162,7 @@ export function makeCard(rn) {
|
|
|
156
162
|
style: composeStyle([base, paint, layout], containerStyle),
|
|
157
163
|
...rest,
|
|
158
164
|
},
|
|
159
|
-
|
|
165
|
+
scoped,
|
|
160
166
|
);
|
|
161
167
|
}
|
|
162
168
|
|
|
@@ -179,7 +185,7 @@ export function makeCard(rn) {
|
|
|
179
185
|
),
|
|
180
186
|
...rest,
|
|
181
187
|
},
|
|
182
|
-
|
|
188
|
+
scoped,
|
|
183
189
|
),
|
|
184
190
|
);
|
|
185
191
|
}
|
package/dist/contract.cjs
CHANGED
|
@@ -2480,7 +2480,7 @@ const PRIMITIVES = [
|
|
|
2480
2480
|
{
|
|
2481
2481
|
name: "Button",
|
|
2482
2482
|
description:
|
|
2483
|
-
'Themed button. `<Button title="Save" onPress={save} />` — THE way to draw a button: its fill, label colour, border, corner radius, padding, weight, shadow and gradient come from the app\'s button look (Theme Settings > Buttons, and Destructive / Focus ring for the danger variant and emphasis border), so it matches the built-in Button widget. Props: `title`, `onPress` (omit it for a purely visual button inside something else that is pressable), `variant` ("primary" default | "secondary" | "outlined" | "danger"), `icon` (a lucide name) with `iconPosition` ("leading" | "trailing"), `disabled`, `accessibilityLabel`, `testID`, `style` (extra container style, e.g. `alignSelf`) and `labelStyle`. Pass `fields` — `{ background, textColor, borderColor, borderWidth, paddingX, paddingY, radius, fontWeight, fontSize, shadow, gradient }` — to override one button, e.g. from style fields your widget declares. Never hand-roll a button from `Pressable` + background + radius.',
|
|
2483
|
+
'Themed button. `<Button title="Save" onPress={save} />` — THE way to draw a button: its fill, label colour, border, corner radius, padding, weight, shadow and gradient come from the app\'s button look (Theme Settings > Buttons, and Destructive / Focus ring for the danger variant and emphasis border), so it matches the built-in Button widget. Props: `title`, `onPress` (omit it for a purely visual button inside something else that is pressable), `variant` ("primary" default | "secondary" | "outlined" | "danger"), `icon` (a lucide name) with `iconPosition` ("leading" | "trailing"), `disabled`, `accessibilityLabel`, `testID`, `style` (extra container style, e.g. `alignSelf`) and `labelStyle`. Pass `fields` — `{ background, textColor, borderColor, borderWidth, paddingX, paddingY, radius, fontWeight, fontSize, shadow, gradient }` — to override one button, e.g. from style fields your widget declares. On a section that paints its own fill (`theme.colors.backdrop` there), a variant colour that the section makes unreadable takes that section\'s text colour, as its text does: an outlined label and border, a secondary whose fill disappears becomes outlined, and a primary or danger fill turns inverse (that text colour as its fill, its own colour as its label). A white or lightly tinted panel changes nothing, and a button inside a `<Card>` is measured against that card. A colour you pass in `fields`, `style` or `labelStyle`, or that the app\'s button look sets, is never changed. Never hand-roll a button from `Pressable` + background + radius.',
|
|
2484
2484
|
rnComponent: null,
|
|
2485
2485
|
docsUrl: null,
|
|
2486
2486
|
},
|
|
@@ -3371,7 +3371,7 @@ const VETTED_IMPORTS = [
|
|
|
3371
3371
|
platforms: ["native"],
|
|
3372
3372
|
category: "system",
|
|
3373
3373
|
description:
|
|
3374
|
-
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO
|
|
3374
|
+
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO a per-workspace device capability: the export claims the iOS NFC entitlement and the Android NFC permission only while the workspace's 'nfc' device capability is on. A widget built in the Studio by Mason or the widget agent turns it on when it is published; for any other widget the publisher enables it in Publish settings. Either way it reaches a phone only with the next native build. isSupported() does NOT see that gate - it probes the DEVICE (readingAvailable on iOS, hasSystemFeature on Android), so on a capable phone running a build made without the capability it returns true and the read then fails: Android throws a SecurityException from registerTagEvent, iOS cannot open the reader session. So wrap start() / requestTechnology() in a try and render a clear ERROR STATE from the catch - say this build of the app does not include NFC and that it needs a new build. That catch is an ERROR path, NOT the web build's typed-code substitute: do not give widget.native.jsx a permanent manual-entry field beside the tap target, because in a correctly-configured app the radio works and the field is the typing the tag was meant to replace. Pinned at 3.17.2, the current stable release (4.x is still beta); a community package, so Expo bundledNativeModules has no version opinion on it.",
|
|
3375
3375
|
},
|
|
3376
3376
|
{
|
|
3377
3377
|
specifier: "decimal.js",
|
|
@@ -4764,7 +4764,10 @@ const CONTRACT = deepFreeze({
|
|
|
4764
4764
|
// (boolean, default false): the widget draws live figures about its
|
|
4765
4765
|
// records, so the app-builder counts it as the page's figures when it
|
|
4766
4766
|
// reuses it. Existing manifests omit it and read false.
|
|
4767
|
-
|
|
4767
|
+
// 1.141.0: additive (sc-9346) — inside a section that paints its own fill,
|
|
4768
|
+
// `theme.colors.backdrop` is that fill, and `<Button>` re-inks a variant
|
|
4769
|
+
// colour that cannot be read against it by the rule its text follows.
|
|
4770
|
+
version: "1.141.0",
|
|
4768
4771
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4769
4772
|
hooks: HOOKS,
|
|
4770
4773
|
primitives: PRIMITIVES,
|
|
@@ -5037,6 +5040,25 @@ function deriveSurfaceTokens(backgroundColor) {
|
|
|
5037
5040
|
};
|
|
5038
5041
|
}
|
|
5039
5042
|
|
|
5043
|
+
// sc-8370: WCAG 1.4.3 for text, 1.4.11 for a control's boundary.
|
|
5044
|
+
const INK_CONTRAST = Object.freeze({ text: 4.5, boundary: 3 });
|
|
5045
|
+
|
|
5046
|
+
/**
|
|
5047
|
+
* The measured re-ink rule every painted section applies — to its text tiers
|
|
5048
|
+
* and to its buttons alike. `current` is kept while it reaches `minimum`
|
|
5049
|
+
* against `backdrop`; below that, `candidate` replaces it only when it is
|
|
5050
|
+
* genuinely more readable there. A backdrop with no good answer keeps what it
|
|
5051
|
+
* had rather than committing to a colour that is wrong for half of it.
|
|
5052
|
+
*/
|
|
5053
|
+
function inkForBackdrop(current, candidate, backdrop, minimum) {
|
|
5054
|
+
if (!isHexColor(current) || !isHexColor(candidate) || !isHexColor(backdrop)) {
|
|
5055
|
+
return current;
|
|
5056
|
+
}
|
|
5057
|
+
const held = contrastRatio(current, backdrop);
|
|
5058
|
+
if (held >= minimum) return current;
|
|
5059
|
+
return contrastRatio(candidate, backdrop) > held ? candidate : current;
|
|
5060
|
+
}
|
|
5061
|
+
|
|
5040
5062
|
/**
|
|
5041
5063
|
* Map a CSS-style gradient angle (0 = to top, 90 = to right) to the
|
|
5042
5064
|
* `{ start, end }` unit vectors `expo-linear-gradient` expects (origin at
|
|
@@ -5119,6 +5141,8 @@ module.exports = {
|
|
|
5119
5141
|
readableTextColor,
|
|
5120
5142
|
deriveAccentTints,
|
|
5121
5143
|
deriveSurfaceTokens,
|
|
5144
|
+
inkForBackdrop,
|
|
5145
|
+
INK_CONTRAST,
|
|
5122
5146
|
gradientAngleToVector,
|
|
5123
5147
|
normaliseComponentGradient,
|
|
5124
5148
|
isThemeColorTokenRef,
|
package/dist/contract.js
CHANGED
|
@@ -2480,7 +2480,7 @@ const PRIMITIVES = [
|
|
|
2480
2480
|
{
|
|
2481
2481
|
name: "Button",
|
|
2482
2482
|
description:
|
|
2483
|
-
'Themed button. `<Button title="Save" onPress={save} />` — THE way to draw a button: its fill, label colour, border, corner radius, padding, weight, shadow and gradient come from the app\'s button look (Theme Settings > Buttons, and Destructive / Focus ring for the danger variant and emphasis border), so it matches the built-in Button widget. Props: `title`, `onPress` (omit it for a purely visual button inside something else that is pressable), `variant` ("primary" default | "secondary" | "outlined" | "danger"), `icon` (a lucide name) with `iconPosition` ("leading" | "trailing"), `disabled`, `accessibilityLabel`, `testID`, `style` (extra container style, e.g. `alignSelf`) and `labelStyle`. Pass `fields` — `{ background, textColor, borderColor, borderWidth, paddingX, paddingY, radius, fontWeight, fontSize, shadow, gradient }` — to override one button, e.g. from style fields your widget declares. Never hand-roll a button from `Pressable` + background + radius.',
|
|
2483
|
+
'Themed button. `<Button title="Save" onPress={save} />` — THE way to draw a button: its fill, label colour, border, corner radius, padding, weight, shadow and gradient come from the app\'s button look (Theme Settings > Buttons, and Destructive / Focus ring for the danger variant and emphasis border), so it matches the built-in Button widget. Props: `title`, `onPress` (omit it for a purely visual button inside something else that is pressable), `variant` ("primary" default | "secondary" | "outlined" | "danger"), `icon` (a lucide name) with `iconPosition` ("leading" | "trailing"), `disabled`, `accessibilityLabel`, `testID`, `style` (extra container style, e.g. `alignSelf`) and `labelStyle`. Pass `fields` — `{ background, textColor, borderColor, borderWidth, paddingX, paddingY, radius, fontWeight, fontSize, shadow, gradient }` — to override one button, e.g. from style fields your widget declares. On a section that paints its own fill (`theme.colors.backdrop` there), a variant colour that the section makes unreadable takes that section\'s text colour, as its text does: an outlined label and border, a secondary whose fill disappears becomes outlined, and a primary or danger fill turns inverse (that text colour as its fill, its own colour as its label). A white or lightly tinted panel changes nothing, and a button inside a `<Card>` is measured against that card. A colour you pass in `fields`, `style` or `labelStyle`, or that the app\'s button look sets, is never changed. Never hand-roll a button from `Pressable` + background + radius.',
|
|
2484
2484
|
rnComponent: null,
|
|
2485
2485
|
docsUrl: null,
|
|
2486
2486
|
},
|
|
@@ -3367,7 +3367,7 @@ const VETTED_IMPORTS = [
|
|
|
3367
3367
|
platforms: ["native"],
|
|
3368
3368
|
category: "system",
|
|
3369
3369
|
description:
|
|
3370
|
-
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO
|
|
3370
|
+
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO a per-workspace device capability: the export claims the iOS NFC entitlement and the Android NFC permission only while the workspace's 'nfc' device capability is on. A widget built in the Studio by Mason or the widget agent turns it on when it is published; for any other widget the publisher enables it in Publish settings. Either way it reaches a phone only with the next native build. isSupported() does NOT see that gate - it probes the DEVICE (readingAvailable on iOS, hasSystemFeature on Android), so on a capable phone running a build made without the capability it returns true and the read then fails: Android throws a SecurityException from registerTagEvent, iOS cannot open the reader session. So wrap start() / requestTechnology() in a try and render a clear ERROR STATE from the catch - say this build of the app does not include NFC and that it needs a new build. That catch is an ERROR path, NOT the web build's typed-code substitute: do not give widget.native.jsx a permanent manual-entry field beside the tap target, because in a correctly-configured app the radio works and the field is the typing the tag was meant to replace. Pinned at 3.17.2, the current stable release (4.x is still beta); a community package, so Expo bundledNativeModules has no version opinion on it.",
|
|
3371
3371
|
},
|
|
3372
3372
|
{
|
|
3373
3373
|
specifier: "decimal.js",
|
|
@@ -4760,7 +4760,10 @@ const CONTRACT = deepFreeze({
|
|
|
4760
4760
|
// (boolean, default false): the widget draws live figures about its
|
|
4761
4761
|
// records, so the app-builder counts it as the page's figures when it
|
|
4762
4762
|
// reuses it. Existing manifests omit it and read false.
|
|
4763
|
-
|
|
4763
|
+
// 1.141.0: additive (sc-9346) — inside a section that paints its own fill,
|
|
4764
|
+
// `theme.colors.backdrop` is that fill, and `<Button>` re-inks a variant
|
|
4765
|
+
// colour that cannot be read against it by the rule its text follows.
|
|
4766
|
+
version: "1.141.0",
|
|
4764
4767
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4765
4768
|
hooks: HOOKS,
|
|
4766
4769
|
primitives: PRIMITIVES,
|
|
@@ -5033,6 +5036,25 @@ function deriveSurfaceTokens(backgroundColor) {
|
|
|
5033
5036
|
};
|
|
5034
5037
|
}
|
|
5035
5038
|
|
|
5039
|
+
// sc-8370: WCAG 1.4.3 for text, 1.4.11 for a control's boundary.
|
|
5040
|
+
const INK_CONTRAST = Object.freeze({ text: 4.5, boundary: 3 });
|
|
5041
|
+
|
|
5042
|
+
/**
|
|
5043
|
+
* The measured re-ink rule every painted section applies — to its text tiers
|
|
5044
|
+
* and to its buttons alike. `current` is kept while it reaches `minimum`
|
|
5045
|
+
* against `backdrop`; below that, `candidate` replaces it only when it is
|
|
5046
|
+
* genuinely more readable there. A backdrop with no good answer keeps what it
|
|
5047
|
+
* had rather than committing to a colour that is wrong for half of it.
|
|
5048
|
+
*/
|
|
5049
|
+
function inkForBackdrop(current, candidate, backdrop, minimum) {
|
|
5050
|
+
if (!isHexColor(current) || !isHexColor(candidate) || !isHexColor(backdrop)) {
|
|
5051
|
+
return current;
|
|
5052
|
+
}
|
|
5053
|
+
const held = contrastRatio(current, backdrop);
|
|
5054
|
+
if (held >= minimum) return current;
|
|
5055
|
+
return contrastRatio(candidate, backdrop) > held ? candidate : current;
|
|
5056
|
+
}
|
|
5057
|
+
|
|
5036
5058
|
/**
|
|
5037
5059
|
* Map a CSS-style gradient angle (0 = to top, 90 = to right) to the
|
|
5038
5060
|
* `{ start, end }` unit vectors `expo-linear-gradient` expects (origin at
|
|
@@ -5115,6 +5137,8 @@ export {
|
|
|
5115
5137
|
readableTextColor,
|
|
5116
5138
|
deriveAccentTints,
|
|
5117
5139
|
deriveSurfaceTokens,
|
|
5140
|
+
inkForBackdrop,
|
|
5141
|
+
INK_CONTRAST,
|
|
5118
5142
|
gradientAngleToVector,
|
|
5119
5143
|
normaliseComponentGradient,
|
|
5120
5144
|
isThemeColorTokenRef,
|
package/dist/hooks.js
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
// sc-5717 — "is this key still at its declared default", shared with the
|
|
47
47
|
// host codec so the hook and the encoder can never disagree about it.
|
|
48
48
|
sameWidgetRouteValue,
|
|
49
|
+
deriveSurfaceTokens,
|
|
49
50
|
} from "./contract.js";
|
|
50
51
|
|
|
51
52
|
/** @internal — host-injected context value of shape WidgetContext (see index.d.ts). */
|
|
@@ -67,6 +68,27 @@ export function WidgetContextProvider({ value, children }) {
|
|
|
67
68
|
return React.createElement(HostWidgetContext.Provider, { value }, children);
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* @internal — a surface painting its own fill inside a coloured section
|
|
73
|
+
* becomes the surface its content reads and its buttons are measured against
|
|
74
|
+
* (`<Card>` uses it), by the derivation a layout container applies.
|
|
75
|
+
* Outside such a section there is no backdrop and nothing changes.
|
|
76
|
+
*/
|
|
77
|
+
export function BackdropScope({ basis, children }) {
|
|
78
|
+
const ctx = useContext(HostWidgetContext);
|
|
79
|
+
const theme = ctx && ctx.workspace ? ctx.workspace.theme : null;
|
|
80
|
+
const colors = theme && theme.colors;
|
|
81
|
+
const scoped = Boolean(basis && colors && colors.backdrop && colors.backdrop !== basis);
|
|
82
|
+
const value = useMemo(
|
|
83
|
+
() =>
|
|
84
|
+
scoped
|
|
85
|
+
? { ...ctx, workspace: { ...ctx.workspace, theme: { ...theme, colors: { ...colors, ...deriveSurfaceTokens(basis), backdrop: basis } } } }
|
|
86
|
+
: ctx,
|
|
87
|
+
[scoped, ctx, theme, colors, basis],
|
|
88
|
+
);
|
|
89
|
+
return React.createElement(HostWidgetContext.Provider, { value }, children);
|
|
90
|
+
}
|
|
91
|
+
|
|
70
92
|
// Exported for ./toast.js + future hook modules outside hooks.js. Internal
|
|
71
93
|
// utility — widget code should not import this directly.
|
|
72
94
|
export function useWidgetContextOrThrow(hookName) {
|
package/dist/host.d.ts
CHANGED
|
@@ -39,6 +39,21 @@ export type ThemeWidgetStyles = Record<string, ThemeComponentStyle>;
|
|
|
39
39
|
*/
|
|
40
40
|
export function normaliseThemeComponents(raw: unknown): ThemeComponents;
|
|
41
41
|
|
|
42
|
+
/** Minimum contrast: `text` for labels (WCAG 1.4.3), `boundary` for edges (1.4.11). */
|
|
43
|
+
export const INK_CONTRAST: Readonly<{ text: number; boundary: number }>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The measured re-ink rule a painted section applies to its text and buttons:
|
|
47
|
+
* `current` while it reaches `minimum` against `backdrop`, else `candidate`
|
|
48
|
+
* when that reads better there, else `current`.
|
|
49
|
+
*/
|
|
50
|
+
export function inkForBackdrop(
|
|
51
|
+
current: string,
|
|
52
|
+
candidate: string,
|
|
53
|
+
backdrop: string,
|
|
54
|
+
minimum: number,
|
|
55
|
+
): string;
|
|
56
|
+
|
|
42
57
|
/**
|
|
43
58
|
* REQ-THEME-ELEMENT host helper: validates a raw `themeConfig.widgetStyles` blob.
|
|
44
59
|
* Structural only — the key space is the workspace's widget catalog, not the
|
package/dist/host.js
CHANGED
|
@@ -41,7 +41,7 @@ export {
|
|
|
41
41
|
// surface — the page background and every container that carries its own fill.
|
|
42
42
|
// Host plumbing, deliberately off the author entry point: a widget reads the
|
|
43
43
|
// already-correct `useTheme().colors`, it never derives them itself.
|
|
44
|
-
export { deriveSurfaceTokens } from "./contract.js";
|
|
44
|
+
export { deriveSurfaceTokens, inkForBackdrop, INK_CONTRAST } from "./contract.js";
|
|
45
45
|
|
|
46
46
|
// REQ-WSDK-PLATFORM §6: the host half of `useToast()` — the queue, the
|
|
47
47
|
// auto-dismiss timing, and the themed values a toast is painted with. The
|
package/dist/index.d.ts
CHANGED
|
@@ -393,6 +393,11 @@ export interface ThemeTokens {
|
|
|
393
393
|
onSurface: string;
|
|
394
394
|
danger: string;
|
|
395
395
|
[k: string]: string;
|
|
396
|
+
} & {
|
|
397
|
+
/** Inside a section painting its own fill: that fill. Absent on the page. */
|
|
398
|
+
backdrop?: string;
|
|
399
|
+
/** The surface the look was designed for, which `backdrop` is judged against. */
|
|
400
|
+
backdropBase?: string;
|
|
396
401
|
};
|
|
397
402
|
/** sc-3696: the shared elevation vocabulary. Spread a level into a style. */
|
|
398
403
|
elevation: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.180.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",
|