@colixsystems/widget-sdk 0.231.0 → 0.233.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 +9 -1
- package/dist/contract.cjs +7 -1
- package/dist/contract.js +7 -1
- package/dist/host.d.ts +33 -0
- package/dist/host.js +3 -0
- package/dist/navigation.cjs +56 -5
- package/dist/navigation.js +55 -4
- package/package.json +1 -1
- package/src/contract.js +7 -1
package/README.md
CHANGED
|
@@ -84,7 +84,15 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
84
84
|
|
|
85
85
|
## Status
|
|
86
86
|
|
|
87
|
-
`v0.
|
|
87
|
+
`v0.233.0` — 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**.
|
|
88
|
+
|
|
89
|
+
### What's new in 0.233.0 (contract 1.188.0)
|
|
90
|
+
|
|
91
|
+
**A floating bottom navigation bar is frosted glass by default (host side only).** `resolveFooterTokens` from `@colixsystems/widget-sdk/host` now also resolves `glass`, true for a floating bar unless the theme's `footer.glass` is `false` (a docked bar is never frosted, as nothing scrolls behind it), and `glassTint`, the 8-digit hex surface laid over the blur: the bar's `backgroundColor` at 72%, or its own alpha when it already has one. Nothing a widget imports changes.
|
|
92
|
+
|
|
93
|
+
### What's new in 0.232.0 (contract 1.187.0)
|
|
94
|
+
|
|
95
|
+
**The app's bottom navigation bar floats by default (host side only).** `resolveFooterTokens` from `@colixsystems/widget-sdk/host` now also resolves the bar's shape: `shape` is `"floating"` (a rounded bar inset from the screen's sides, with the page visible around and beneath it) unless the theme's `footer.shape` is `"docked"` (the edge-to-edge strip). `radius` (0–40, default 28), `sideMargin` (0–48, default 12) and `bottomSpacing` (0–32, default 8) are px, clamped onto their ranges, and `itemRadius` is a tab's corner radius inside that bar. `FOOTER_SIZES` exports each measurement's bound and default, `clampFooterSize(value, key)` clamps one onto it, and `FOOTER_HOME_LINE_CLEARANCE_PX` (14) is how much of the iOS bottom inset a bar keeps clear. Nothing a widget imports changes.
|
|
88
96
|
|
|
89
97
|
### What's new in 0.231.0 (contract 1.186.0)
|
|
90
98
|
|
package/dist/contract.cjs
CHANGED
|
@@ -6040,7 +6040,13 @@ const CONTRACT = deepFreeze({
|
|
|
6040
6040
|
// `fitScreenFoot` host hint: a self-scrolling Fit to screen surface that
|
|
6041
6041
|
// calls it runs under the bottom safe area on native and pads its
|
|
6042
6042
|
// controls by it. Hosts without the hint read zeros.
|
|
6043
|
-
|
|
6043
|
+
// 1.187.0: additive (sc-11678) — `resolveFooterTokens` gains the bottom
|
|
6044
|
+
// bar's `shape` (unset floats), `radius`, `itemRadius`, `sideMargin` and
|
|
6045
|
+
// `bottomSpacing`; host exports `FOOTER_SIZES`, `clampFooterSize` and
|
|
6046
|
+
// `FOOTER_HOME_LINE_CLEARANCE_PX`. No widget-facing surface changes.
|
|
6047
|
+
// 1.188.0: additive (sc-11692) — `resolveFooterTokens` gains `glass` (a
|
|
6048
|
+
// floating bar is frosted unless `footer.glass` is false) and `glassTint`.
|
|
6049
|
+
version: "1.188.0",
|
|
6044
6050
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6045
6051
|
hooks: HOOKS,
|
|
6046
6052
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -6036,7 +6036,13 @@ const CONTRACT = deepFreeze({
|
|
|
6036
6036
|
// `fitScreenFoot` host hint: a self-scrolling Fit to screen surface that
|
|
6037
6037
|
// calls it runs under the bottom safe area on native and pads its
|
|
6038
6038
|
// controls by it. Hosts without the hint read zeros.
|
|
6039
|
-
|
|
6039
|
+
// 1.187.0: additive (sc-11678) — `resolveFooterTokens` gains the bottom
|
|
6040
|
+
// bar's `shape` (unset floats), `radius`, `itemRadius`, `sideMargin` and
|
|
6041
|
+
// `bottomSpacing`; host exports `FOOTER_SIZES`, `clampFooterSize` and
|
|
6042
|
+
// `FOOTER_HOME_LINE_CLEARANCE_PX`. No widget-facing surface changes.
|
|
6043
|
+
// 1.188.0: additive (sc-11692) — `resolveFooterTokens` gains `glass` (a
|
|
6044
|
+
// floating bar is frosted unless `footer.glass` is false) and `glassTint`.
|
|
6045
|
+
version: "1.188.0",
|
|
6040
6046
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6041
6047
|
hooks: HOOKS,
|
|
6042
6048
|
primitives: PRIMITIVES,
|
package/dist/host.d.ts
CHANGED
|
@@ -265,8 +265,41 @@ export interface FooterTokens {
|
|
|
265
265
|
/** The active tab's surface, or `null` for none — which is what "accent"
|
|
266
266
|
* resolves to unless the author names one. */
|
|
267
267
|
activeSurface: string | null;
|
|
268
|
+
/** sc-11692: a floating bar's frosted glass, on unless `glass: false`. */
|
|
269
|
+
glass: boolean;
|
|
270
|
+
/** The see-through surface laid over the blur (8-digit hex), or `null`
|
|
271
|
+
* when the bar is not frosted. */
|
|
272
|
+
glassTint: string | null;
|
|
273
|
+
/** sc-11678: a rounded bar inset from the screen's sides, the page showing
|
|
274
|
+
* around and beneath it, or the edge-to-edge strip. Unset is "floating". */
|
|
275
|
+
shape: "floating" | "docked";
|
|
276
|
+
/** A floating bar's corner radius, px (0–40). */
|
|
277
|
+
radius: number;
|
|
278
|
+
/** A tab's corner radius, px: a floating bar's curve one row inset in
|
|
279
|
+
* (min 6) for a filled tab; 6 for an accent tab or a docked bar. */
|
|
280
|
+
itemRadius: number;
|
|
281
|
+
/** A floating bar's distance from the screen's sides, px (0–48). */
|
|
282
|
+
sideMargin: number;
|
|
283
|
+
/** Space below the tabs, px (0–32), on top of the system's clearance. */
|
|
284
|
+
bottomSpacing: number;
|
|
268
285
|
}
|
|
269
286
|
|
|
287
|
+
/** sc-11678: each bar measurement's upper bound and default, px (min 0). */
|
|
288
|
+
export const FOOTER_SIZES: Readonly<
|
|
289
|
+
Record<"radius" | "sideMargin" | "bottomSpacing", Readonly<{ max: number; fallback: number }>>
|
|
290
|
+
>;
|
|
291
|
+
|
|
292
|
+
/** sc-11678: one bar measurement clamped onto its `FOOTER_SIZES` range, or
|
|
293
|
+
* `undefined` when the value is not a finite number. */
|
|
294
|
+
export function clampFooterSize(
|
|
295
|
+
value: unknown,
|
|
296
|
+
key: "radius" | "sideMargin" | "bottomSpacing",
|
|
297
|
+
): number | undefined;
|
|
298
|
+
|
|
299
|
+
/** sc-11678: the part of the iOS bottom inset a bar keeps clear (the home
|
|
300
|
+
* line's band), px. Android's inset is cleared whole. */
|
|
301
|
+
export const FOOTER_HOME_LINE_CLEARANCE_PX: number;
|
|
302
|
+
|
|
270
303
|
/**
|
|
271
304
|
* Resolves the footer strip's tokens from a whole `theme_config`. It reads
|
|
272
305
|
* `theme.footer` and NOTHING else (sc-8033): the colours used to fall back to
|
package/dist/host.js
CHANGED
package/dist/navigation.cjs
CHANGED
|
@@ -83,10 +83,11 @@ function menuItemCap(menuType) {
|
|
|
83
83
|
// what each workspace was inheriting, so no shipped app lost its look when the
|
|
84
84
|
// chain went (migration `20260918000000_footer_tokens_uncouple`).
|
|
85
85
|
//
|
|
86
|
-
// The
|
|
87
|
-
//
|
|
86
|
+
// The authorable keys are the controls the Design panel offers, one for one:
|
|
87
|
+
// `backgroundColor`, `textColor`, `tabActiveBackgroundColor`,
|
|
88
88
|
// `tabActiveTextColor`, `borderColor`, `borderWidth`, `activeStyle`,
|
|
89
|
-
// `tabLayout
|
|
89
|
+
// `tabLayout`, (sc-11678) `shape`, `radius`, `sideMargin`, `bottomSpacing`
|
|
90
|
+
// and (sc-11692) `glass`.
|
|
90
91
|
function resolveFooterTokens(theme) {
|
|
91
92
|
const config = isPlainObject(theme) ? theme : {};
|
|
92
93
|
const footer = isPlainObject(config.footer) ? config.footer : {};
|
|
@@ -103,8 +104,17 @@ function resolveFooterTokens(theme) {
|
|
|
103
104
|
// draw nothing.
|
|
104
105
|
const borderWidth = borderColor ? footerBorderWidthOr(footer.borderWidth) : 0;
|
|
105
106
|
const divider = borderWidth > 0;
|
|
107
|
+
const floats = footer.shape !== "docked";
|
|
108
|
+
const radius = footerSizeOr(footer, "radius");
|
|
109
|
+
const backgroundColor = hexOr(footer.backgroundColor, DEFAULT_FOOTER_SURFACE);
|
|
110
|
+
// sc-11692: frosted unless switched off; only a floating bar has a page
|
|
111
|
+
// behind it to frost.
|
|
112
|
+
const glass = floats && footer.glass !== false;
|
|
106
113
|
return {
|
|
107
|
-
backgroundColor
|
|
114
|
+
backgroundColor,
|
|
115
|
+
glass,
|
|
116
|
+
// The surface over the blur: the bar's colour, see-through.
|
|
117
|
+
glassTint: glass ? translucentHex(backgroundColor) : null,
|
|
108
118
|
textColor: hexOr(footer.textColor, DEFAULT_CHROME_TEXT),
|
|
109
119
|
borderColor: divider ? borderColor : null,
|
|
110
120
|
borderWidth: divider ? borderWidth : null,
|
|
@@ -128,9 +138,50 @@ function resolveFooterTokens(theme) {
|
|
|
128
138
|
footer.tabActiveTextColor,
|
|
129
139
|
accent ? brandPrimary(config) : FILLED_TAB_LABEL,
|
|
130
140
|
),
|
|
141
|
+
// sc-11678: unset floats, so every app gets the rounded, inset bar.
|
|
142
|
+
shape: floats ? "floating" : "docked",
|
|
143
|
+
radius,
|
|
144
|
+
// A filled tab follows a floating bar's curve one row inset (8px) in,
|
|
145
|
+
// never sharper than the docked 6px chip. An accent tab paints no surface,
|
|
146
|
+
// and a curved top edge would bend its marker into an arc.
|
|
147
|
+
itemRadius:
|
|
148
|
+
floats && !accent ? Math.max(DOCKED_TAB_RADIUS, radius - 8) : DOCKED_TAB_RADIUS,
|
|
149
|
+
sideMargin: footerSizeOr(footer, "sideMargin"),
|
|
150
|
+
bottomSpacing: footerSizeOr(footer, "bottomSpacing"),
|
|
131
151
|
};
|
|
132
152
|
}
|
|
133
153
|
|
|
154
|
+
// sc-11678: the bar's measurements, in px. `bottomSpacing` sits ABOVE what the
|
|
155
|
+
// system needs (see FOOTER_HOME_LINE_CLEARANCE_PX). Clamped, never dropped.
|
|
156
|
+
const FOOTER_SIZES = Object.freeze({
|
|
157
|
+
radius: Object.freeze({ max: 40, fallback: 28 }),
|
|
158
|
+
sideMargin: Object.freeze({ max: 48, fallback: 12 }),
|
|
159
|
+
bottomSpacing: Object.freeze({ max: 32, fallback: 8 }),
|
|
160
|
+
});
|
|
161
|
+
const DOCKED_TAB_RADIUS = 6;
|
|
162
|
+
|
|
163
|
+
// sc-11692: a colour that already carries its own alpha keeps it; an opaque
|
|
164
|
+
// one is laid over the blur at 72%.
|
|
165
|
+
const GLASS_TINT_ALPHA = "b8";
|
|
166
|
+
function translucentHex(hex) {
|
|
167
|
+
const digits = hex.slice(1);
|
|
168
|
+
if (digits.length === 8) return hex;
|
|
169
|
+
const six = digits.length === 3 ? digits.replace(/./g, (d) => d + d) : digits;
|
|
170
|
+
return `#${six}${GLASS_TINT_ALPHA}`;
|
|
171
|
+
}
|
|
172
|
+
function clampFooterSize(value, key) {
|
|
173
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
|
174
|
+
return Math.min(Math.max(Math.round(value), 0), FOOTER_SIZES[key].max);
|
|
175
|
+
}
|
|
176
|
+
function footerSizeOr(footer, key) {
|
|
177
|
+
return clampFooterSize(footer[key], key) ?? FOOTER_SIZES[key].fallback;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// sc-11678: of the iOS bottom inset, only the home line's band is kept clear;
|
|
181
|
+
// a bar may sit in the rest of it, as Apple's and most apps' tab bars do.
|
|
182
|
+
// Android's inset is its navigation bar, so there the whole inset is cleared.
|
|
183
|
+
const FOOTER_HOME_LINE_CLEARANCE_PX = 14;
|
|
184
|
+
|
|
134
185
|
// REQ-THEME-14 — the rail's and the app bar's tokens, resolved once for both
|
|
135
186
|
// hosts.
|
|
136
187
|
//
|
|
@@ -844,4 +895,4 @@ function borderWidthOr(...values) {
|
|
|
844
895
|
return BORDER_WIDTH_MIN;
|
|
845
896
|
}
|
|
846
897
|
|
|
847
|
-
module.exports = { normaliseNavigation, menuItemCap, resolveFooterTokens, resolveSidebarTokens, resolveTopStripeLayout, resolveTopBarTokens, topStripeShowKey, TOP_STRIPE_ALWAYS_SHOWN_SLOTS, TOP_STRIPE_SLOTS, MIN_TOP_STRIPE_HEIGHT_PX, MAX_TOP_STRIPE_HEIGHT_PX, MIN_TOP_STRIPE_PADDING_X_PX, MAX_TOP_STRIPE_PADDING_X_PX, MAX_TOP_STRIPE_OFFSET_Y_PX, DEFAULT_TOP_STRIPE_HEIGHT_PX, DEFAULT_TOP_STRIPE_PADDING_X_PX, resolveSidebarUnfold, resolveLocalNavModel, drawsLocalNavAnywhere, isDarkSurface, resolveAppLogo };
|
|
898
|
+
module.exports = { normaliseNavigation, menuItemCap, resolveFooterTokens, FOOTER_SIZES, clampFooterSize, FOOTER_HOME_LINE_CLEARANCE_PX, resolveSidebarTokens, resolveTopStripeLayout, resolveTopBarTokens, topStripeShowKey, TOP_STRIPE_ALWAYS_SHOWN_SLOTS, TOP_STRIPE_SLOTS, MIN_TOP_STRIPE_HEIGHT_PX, MAX_TOP_STRIPE_HEIGHT_PX, MIN_TOP_STRIPE_PADDING_X_PX, MAX_TOP_STRIPE_PADDING_X_PX, MAX_TOP_STRIPE_OFFSET_Y_PX, DEFAULT_TOP_STRIPE_HEIGHT_PX, DEFAULT_TOP_STRIPE_PADDING_X_PX, resolveSidebarUnfold, resolveLocalNavModel, drawsLocalNavAnywhere, isDarkSurface, resolveAppLogo };
|
package/dist/navigation.js
CHANGED
|
@@ -74,10 +74,11 @@ export function menuItemCap(menuType) {
|
|
|
74
74
|
// what each workspace was inheriting, so no shipped app lost its look when the
|
|
75
75
|
// chain went (migration `20260918000000_footer_tokens_uncouple`).
|
|
76
76
|
//
|
|
77
|
-
// The
|
|
78
|
-
//
|
|
77
|
+
// The authorable keys are the controls the Design panel offers, one for one:
|
|
78
|
+
// `backgroundColor`, `textColor`, `tabActiveBackgroundColor`,
|
|
79
79
|
// `tabActiveTextColor`, `borderColor`, `borderWidth`, `activeStyle`,
|
|
80
|
-
// `tabLayout
|
|
80
|
+
// `tabLayout`, (sc-11678) `shape`, `radius`, `sideMargin`, `bottomSpacing`
|
|
81
|
+
// and (sc-11692) `glass`.
|
|
81
82
|
export function resolveFooterTokens(theme) {
|
|
82
83
|
const config = isPlainObject(theme) ? theme : {};
|
|
83
84
|
const footer = isPlainObject(config.footer) ? config.footer : {};
|
|
@@ -94,8 +95,17 @@ export function resolveFooterTokens(theme) {
|
|
|
94
95
|
// draw nothing.
|
|
95
96
|
const borderWidth = borderColor ? footerBorderWidthOr(footer.borderWidth) : 0;
|
|
96
97
|
const divider = borderWidth > 0;
|
|
98
|
+
const floats = footer.shape !== "docked";
|
|
99
|
+
const radius = footerSizeOr(footer, "radius");
|
|
100
|
+
const backgroundColor = hexOr(footer.backgroundColor, DEFAULT_FOOTER_SURFACE);
|
|
101
|
+
// sc-11692: frosted unless switched off; only a floating bar has a page
|
|
102
|
+
// behind it to frost.
|
|
103
|
+
const glass = floats && footer.glass !== false;
|
|
97
104
|
return {
|
|
98
|
-
backgroundColor
|
|
105
|
+
backgroundColor,
|
|
106
|
+
glass,
|
|
107
|
+
// The surface over the blur: the bar's colour, see-through.
|
|
108
|
+
glassTint: glass ? translucentHex(backgroundColor) : null,
|
|
99
109
|
textColor: hexOr(footer.textColor, DEFAULT_CHROME_TEXT),
|
|
100
110
|
borderColor: divider ? borderColor : null,
|
|
101
111
|
borderWidth: divider ? borderWidth : null,
|
|
@@ -119,9 +129,50 @@ export function resolveFooterTokens(theme) {
|
|
|
119
129
|
footer.tabActiveTextColor,
|
|
120
130
|
accent ? brandPrimary(config) : FILLED_TAB_LABEL,
|
|
121
131
|
),
|
|
132
|
+
// sc-11678: unset floats, so every app gets the rounded, inset bar.
|
|
133
|
+
shape: floats ? "floating" : "docked",
|
|
134
|
+
radius,
|
|
135
|
+
// A filled tab follows a floating bar's curve one row inset (8px) in,
|
|
136
|
+
// never sharper than the docked 6px chip. An accent tab paints no surface,
|
|
137
|
+
// and a curved top edge would bend its marker into an arc.
|
|
138
|
+
itemRadius:
|
|
139
|
+
floats && !accent ? Math.max(DOCKED_TAB_RADIUS, radius - 8) : DOCKED_TAB_RADIUS,
|
|
140
|
+
sideMargin: footerSizeOr(footer, "sideMargin"),
|
|
141
|
+
bottomSpacing: footerSizeOr(footer, "bottomSpacing"),
|
|
122
142
|
};
|
|
123
143
|
}
|
|
124
144
|
|
|
145
|
+
// sc-11678: the bar's measurements, in px. `bottomSpacing` sits ABOVE what the
|
|
146
|
+
// system needs (see FOOTER_HOME_LINE_CLEARANCE_PX). Clamped, never dropped.
|
|
147
|
+
export const FOOTER_SIZES = Object.freeze({
|
|
148
|
+
radius: Object.freeze({ max: 40, fallback: 28 }),
|
|
149
|
+
sideMargin: Object.freeze({ max: 48, fallback: 12 }),
|
|
150
|
+
bottomSpacing: Object.freeze({ max: 32, fallback: 8 }),
|
|
151
|
+
});
|
|
152
|
+
const DOCKED_TAB_RADIUS = 6;
|
|
153
|
+
|
|
154
|
+
// sc-11692: a colour that already carries its own alpha keeps it; an opaque
|
|
155
|
+
// one is laid over the blur at 72%.
|
|
156
|
+
const GLASS_TINT_ALPHA = "b8";
|
|
157
|
+
function translucentHex(hex) {
|
|
158
|
+
const digits = hex.slice(1);
|
|
159
|
+
if (digits.length === 8) return hex;
|
|
160
|
+
const six = digits.length === 3 ? digits.replace(/./g, (d) => d + d) : digits;
|
|
161
|
+
return `#${six}${GLASS_TINT_ALPHA}`;
|
|
162
|
+
}
|
|
163
|
+
export function clampFooterSize(value, key) {
|
|
164
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
|
165
|
+
return Math.min(Math.max(Math.round(value), 0), FOOTER_SIZES[key].max);
|
|
166
|
+
}
|
|
167
|
+
function footerSizeOr(footer, key) {
|
|
168
|
+
return clampFooterSize(footer[key], key) ?? FOOTER_SIZES[key].fallback;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// sc-11678: of the iOS bottom inset, only the home line's band is kept clear;
|
|
172
|
+
// a bar may sit in the rest of it, as Apple's and most apps' tab bars do.
|
|
173
|
+
// Android's inset is its navigation bar, so there the whole inset is cleared.
|
|
174
|
+
export const FOOTER_HOME_LINE_CLEARANCE_PX = 14;
|
|
175
|
+
|
|
125
176
|
// REQ-THEME-14 — the rail's and the app bar's tokens, resolved once for both
|
|
126
177
|
// hosts.
|
|
127
178
|
//
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.233.0",
|
|
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",
|
package/src/contract.js
CHANGED
|
@@ -6036,7 +6036,13 @@ const CONTRACT = deepFreeze({
|
|
|
6036
6036
|
// `fitScreenFoot` host hint: a self-scrolling Fit to screen surface that
|
|
6037
6037
|
// calls it runs under the bottom safe area on native and pads its
|
|
6038
6038
|
// controls by it. Hosts without the hint read zeros.
|
|
6039
|
-
|
|
6039
|
+
// 1.187.0: additive (sc-11678) — `resolveFooterTokens` gains the bottom
|
|
6040
|
+
// bar's `shape` (unset floats), `radius`, `itemRadius`, `sideMargin` and
|
|
6041
|
+
// `bottomSpacing`; host exports `FOOTER_SIZES`, `clampFooterSize` and
|
|
6042
|
+
// `FOOTER_HOME_LINE_CLEARANCE_PX`. No widget-facing surface changes.
|
|
6043
|
+
// 1.188.0: additive (sc-11692) — `resolveFooterTokens` gains `glass` (a
|
|
6044
|
+
// floating bar is frosted unless `footer.glass` is false) and `glassTint`.
|
|
6045
|
+
version: "1.188.0",
|
|
6040
6046
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6041
6047
|
hooks: HOOKS,
|
|
6042
6048
|
primitives: PRIMITIVES,
|