@colixsystems/widget-sdk 0.231.0 → 0.232.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 +5 -1
- package/dist/contract.cjs +5 -1
- package/dist/contract.js +5 -1
- package/dist/host.d.ts +28 -0
- package/dist/host.js +3 -0
- package/dist/navigation.cjs +37 -4
- package/dist/navigation.js +36 -3
- package/package.json +1 -1
- package/src/contract.js +5 -1
package/README.md
CHANGED
|
@@ -84,7 +84,11 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
84
84
|
|
|
85
85
|
## Status
|
|
86
86
|
|
|
87
|
-
`v0.
|
|
87
|
+
`v0.232.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.232.0 (contract 1.187.0)
|
|
90
|
+
|
|
91
|
+
**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
92
|
|
|
89
93
|
### What's new in 0.231.0 (contract 1.186.0)
|
|
90
94
|
|
package/dist/contract.cjs
CHANGED
|
@@ -6040,7 +6040,11 @@ 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
|
+
version: "1.187.0",
|
|
6044
6048
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6045
6049
|
hooks: HOOKS,
|
|
6046
6050
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -6036,7 +6036,11 @@ 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
|
+
version: "1.187.0",
|
|
6040
6044
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6041
6045
|
hooks: HOOKS,
|
|
6042
6046
|
primitives: PRIMITIVES,
|
package/dist/host.d.ts
CHANGED
|
@@ -265,8 +265,36 @@ 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-11678: a rounded bar inset from the screen's sides, the page showing
|
|
269
|
+
* around and beneath it, or the edge-to-edge strip. Unset is "floating". */
|
|
270
|
+
shape: "floating" | "docked";
|
|
271
|
+
/** A floating bar's corner radius, px (0–40). */
|
|
272
|
+
radius: number;
|
|
273
|
+
/** A tab's corner radius, px: a floating bar's curve one row inset in
|
|
274
|
+
* (min 6) for a filled tab; 6 for an accent tab or a docked bar. */
|
|
275
|
+
itemRadius: number;
|
|
276
|
+
/** A floating bar's distance from the screen's sides, px (0–48). */
|
|
277
|
+
sideMargin: number;
|
|
278
|
+
/** Space below the tabs, px (0–32), on top of the system's clearance. */
|
|
279
|
+
bottomSpacing: number;
|
|
268
280
|
}
|
|
269
281
|
|
|
282
|
+
/** sc-11678: each bar measurement's upper bound and default, px (min 0). */
|
|
283
|
+
export const FOOTER_SIZES: Readonly<
|
|
284
|
+
Record<"radius" | "sideMargin" | "bottomSpacing", Readonly<{ max: number; fallback: number }>>
|
|
285
|
+
>;
|
|
286
|
+
|
|
287
|
+
/** sc-11678: one bar measurement clamped onto its `FOOTER_SIZES` range, or
|
|
288
|
+
* `undefined` when the value is not a finite number. */
|
|
289
|
+
export function clampFooterSize(
|
|
290
|
+
value: unknown,
|
|
291
|
+
key: "radius" | "sideMargin" | "bottomSpacing",
|
|
292
|
+
): number | undefined;
|
|
293
|
+
|
|
294
|
+
/** sc-11678: the part of the iOS bottom inset a bar keeps clear (the home
|
|
295
|
+
* line's band), px. Android's inset is cleared whole. */
|
|
296
|
+
export const FOOTER_HOME_LINE_CLEARANCE_PX: number;
|
|
297
|
+
|
|
270
298
|
/**
|
|
271
299
|
* Resolves the footer strip's tokens from a whole `theme_config`. It reads
|
|
272
300
|
* `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,10 @@ 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`, and (sc-11678) `shape`, `radius`, `sideMargin`, `bottomSpacing`.
|
|
90
90
|
function resolveFooterTokens(theme) {
|
|
91
91
|
const config = isPlainObject(theme) ? theme : {};
|
|
92
92
|
const footer = isPlainObject(config.footer) ? config.footer : {};
|
|
@@ -103,6 +103,8 @@ function resolveFooterTokens(theme) {
|
|
|
103
103
|
// draw nothing.
|
|
104
104
|
const borderWidth = borderColor ? footerBorderWidthOr(footer.borderWidth) : 0;
|
|
105
105
|
const divider = borderWidth > 0;
|
|
106
|
+
const floats = footer.shape !== "docked";
|
|
107
|
+
const radius = footerSizeOr(footer, "radius");
|
|
106
108
|
return {
|
|
107
109
|
backgroundColor: hexOr(footer.backgroundColor, DEFAULT_FOOTER_SURFACE),
|
|
108
110
|
textColor: hexOr(footer.textColor, DEFAULT_CHROME_TEXT),
|
|
@@ -128,9 +130,40 @@ function resolveFooterTokens(theme) {
|
|
|
128
130
|
footer.tabActiveTextColor,
|
|
129
131
|
accent ? brandPrimary(config) : FILLED_TAB_LABEL,
|
|
130
132
|
),
|
|
133
|
+
// sc-11678: unset floats, so every app gets the rounded, inset bar.
|
|
134
|
+
shape: floats ? "floating" : "docked",
|
|
135
|
+
radius,
|
|
136
|
+
// A filled tab follows a floating bar's curve one row inset (8px) in,
|
|
137
|
+
// never sharper than the docked 6px chip. An accent tab paints no surface,
|
|
138
|
+
// and a curved top edge would bend its marker into an arc.
|
|
139
|
+
itemRadius:
|
|
140
|
+
floats && !accent ? Math.max(DOCKED_TAB_RADIUS, radius - 8) : DOCKED_TAB_RADIUS,
|
|
141
|
+
sideMargin: footerSizeOr(footer, "sideMargin"),
|
|
142
|
+
bottomSpacing: footerSizeOr(footer, "bottomSpacing"),
|
|
131
143
|
};
|
|
132
144
|
}
|
|
133
145
|
|
|
146
|
+
// sc-11678: the bar's measurements, in px. `bottomSpacing` sits ABOVE what the
|
|
147
|
+
// system needs (see FOOTER_HOME_LINE_CLEARANCE_PX). Clamped, never dropped.
|
|
148
|
+
const FOOTER_SIZES = Object.freeze({
|
|
149
|
+
radius: Object.freeze({ max: 40, fallback: 28 }),
|
|
150
|
+
sideMargin: Object.freeze({ max: 48, fallback: 12 }),
|
|
151
|
+
bottomSpacing: Object.freeze({ max: 32, fallback: 8 }),
|
|
152
|
+
});
|
|
153
|
+
const DOCKED_TAB_RADIUS = 6;
|
|
154
|
+
function clampFooterSize(value, key) {
|
|
155
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
|
156
|
+
return Math.min(Math.max(Math.round(value), 0), FOOTER_SIZES[key].max);
|
|
157
|
+
}
|
|
158
|
+
function footerSizeOr(footer, key) {
|
|
159
|
+
return clampFooterSize(footer[key], key) ?? FOOTER_SIZES[key].fallback;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// sc-11678: of the iOS bottom inset, only the home line's band is kept clear;
|
|
163
|
+
// a bar may sit in the rest of it, as Apple's and most apps' tab bars do.
|
|
164
|
+
// Android's inset is its navigation bar, so there the whole inset is cleared.
|
|
165
|
+
const FOOTER_HOME_LINE_CLEARANCE_PX = 14;
|
|
166
|
+
|
|
134
167
|
// REQ-THEME-14 — the rail's and the app bar's tokens, resolved once for both
|
|
135
168
|
// hosts.
|
|
136
169
|
//
|
|
@@ -844,4 +877,4 @@ function borderWidthOr(...values) {
|
|
|
844
877
|
return BORDER_WIDTH_MIN;
|
|
845
878
|
}
|
|
846
879
|
|
|
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 };
|
|
880
|
+
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,10 @@ 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`, and (sc-11678) `shape`, `radius`, `sideMargin`, `bottomSpacing`.
|
|
81
81
|
export function resolveFooterTokens(theme) {
|
|
82
82
|
const config = isPlainObject(theme) ? theme : {};
|
|
83
83
|
const footer = isPlainObject(config.footer) ? config.footer : {};
|
|
@@ -94,6 +94,8 @@ export function resolveFooterTokens(theme) {
|
|
|
94
94
|
// draw nothing.
|
|
95
95
|
const borderWidth = borderColor ? footerBorderWidthOr(footer.borderWidth) : 0;
|
|
96
96
|
const divider = borderWidth > 0;
|
|
97
|
+
const floats = footer.shape !== "docked";
|
|
98
|
+
const radius = footerSizeOr(footer, "radius");
|
|
97
99
|
return {
|
|
98
100
|
backgroundColor: hexOr(footer.backgroundColor, DEFAULT_FOOTER_SURFACE),
|
|
99
101
|
textColor: hexOr(footer.textColor, DEFAULT_CHROME_TEXT),
|
|
@@ -119,9 +121,40 @@ export function resolveFooterTokens(theme) {
|
|
|
119
121
|
footer.tabActiveTextColor,
|
|
120
122
|
accent ? brandPrimary(config) : FILLED_TAB_LABEL,
|
|
121
123
|
),
|
|
124
|
+
// sc-11678: unset floats, so every app gets the rounded, inset bar.
|
|
125
|
+
shape: floats ? "floating" : "docked",
|
|
126
|
+
radius,
|
|
127
|
+
// A filled tab follows a floating bar's curve one row inset (8px) in,
|
|
128
|
+
// never sharper than the docked 6px chip. An accent tab paints no surface,
|
|
129
|
+
// and a curved top edge would bend its marker into an arc.
|
|
130
|
+
itemRadius:
|
|
131
|
+
floats && !accent ? Math.max(DOCKED_TAB_RADIUS, radius - 8) : DOCKED_TAB_RADIUS,
|
|
132
|
+
sideMargin: footerSizeOr(footer, "sideMargin"),
|
|
133
|
+
bottomSpacing: footerSizeOr(footer, "bottomSpacing"),
|
|
122
134
|
};
|
|
123
135
|
}
|
|
124
136
|
|
|
137
|
+
// sc-11678: the bar's measurements, in px. `bottomSpacing` sits ABOVE what the
|
|
138
|
+
// system needs (see FOOTER_HOME_LINE_CLEARANCE_PX). Clamped, never dropped.
|
|
139
|
+
export const FOOTER_SIZES = Object.freeze({
|
|
140
|
+
radius: Object.freeze({ max: 40, fallback: 28 }),
|
|
141
|
+
sideMargin: Object.freeze({ max: 48, fallback: 12 }),
|
|
142
|
+
bottomSpacing: Object.freeze({ max: 32, fallback: 8 }),
|
|
143
|
+
});
|
|
144
|
+
const DOCKED_TAB_RADIUS = 6;
|
|
145
|
+
export function clampFooterSize(value, key) {
|
|
146
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
|
147
|
+
return Math.min(Math.max(Math.round(value), 0), FOOTER_SIZES[key].max);
|
|
148
|
+
}
|
|
149
|
+
function footerSizeOr(footer, key) {
|
|
150
|
+
return clampFooterSize(footer[key], key) ?? FOOTER_SIZES[key].fallback;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// sc-11678: of the iOS bottom inset, only the home line's band is kept clear;
|
|
154
|
+
// a bar may sit in the rest of it, as Apple's and most apps' tab bars do.
|
|
155
|
+
// Android's inset is its navigation bar, so there the whole inset is cleared.
|
|
156
|
+
export const FOOTER_HOME_LINE_CLEARANCE_PX = 14;
|
|
157
|
+
|
|
125
158
|
// REQ-THEME-14 — the rail's and the app bar's tokens, resolved once for both
|
|
126
159
|
// hosts.
|
|
127
160
|
//
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.232.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,11 @@ 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
|
+
version: "1.187.0",
|
|
6040
6044
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6041
6045
|
hooks: HOOKS,
|
|
6042
6046
|
primitives: PRIMITIVES,
|