@colixsystems/widget-sdk 0.221.0 → 0.222.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 +32 -2
- package/dist/card-photo.js +26 -0
- package/dist/card-photo.native.js +32 -0
- package/dist/card-style.js +72 -0
- package/dist/card-view.js +82 -8
- package/dist/card.js +2 -1
- package/dist/card.native.js +2 -1
- package/dist/contract.cjs +13 -3
- package/dist/contract.js +13 -3
- package/dist/hooks.js +7 -4
- package/dist/index.d.ts +20 -1
- package/dist/signing-hooks.js +6 -3
- package/package.json +1 -1
- package/src/contract.js +13 -3
package/README.md
CHANGED
|
@@ -84,7 +84,37 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
84
84
|
|
|
85
85
|
## Status
|
|
86
86
|
|
|
87
|
-
`v0.
|
|
87
|
+
`v0.222.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.222.0 (contract 1.178.0)
|
|
90
|
+
|
|
91
|
+
**A card can carry its picture as its background — `<Card image>`.** A small image with its title in a box underneath reads as a caption under a thumbnail. The same content with the picture filling the card and the title on it reads as designed, and until now a widget could only build that by hand: an absolutely positioned `Image`, its own translucent overlay, hand-picked light text. `<Card>` now does it.
|
|
92
|
+
|
|
93
|
+
```jsx
|
|
94
|
+
<Card
|
|
95
|
+
as={Pressable}
|
|
96
|
+
onPress={open}
|
|
97
|
+
image={urls ? urls.card : null}
|
|
98
|
+
scrim="bottom"
|
|
99
|
+
style={{ aspectRatio: 1, justifyContent: "flex-end" }}
|
|
100
|
+
>
|
|
101
|
+
{({ colors }) => <Text style={{ color: colors.onSurface }}>{record.name}</Text>}
|
|
102
|
+
</Card>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- **`image`** takes a URL or `{ uri }`. The picture fills the card to every edge, inside the card's own corner radius; the card's padding insets the content, not the picture.
|
|
106
|
+
- **`scrim`** names the edge the text sits on: `"bottom"` (default), `"top"`, `"left"`, `"right"`, `"full"` for centred text, or `"none"`. It is a light gradient that is clear over most of the picture.
|
|
107
|
+
- **`imagePosition`** (`"center"` default, `"top"`, `"bottom"`, `"left"`, `"right"`) keeps that part of the picture in frame when the crop is tight.
|
|
108
|
+
- **Children may be a function.** It receives `{ colors, onImage }`: the theme colours as they read where the content sits. Over a picture `colors.onSurface` and `colors.onSurfaceMuted` are light; on a card with no image they are the theme's own. A `<Button>` inside the card reads its backdrop without help.
|
|
109
|
+
- **No image, no change.** `image={null}` renders the ordinary card, so a record with an empty file cell needs no branch.
|
|
110
|
+
- **A photo card is a `View` or a `Pressable`.** Put a scroller inside it, never as it (`as={ScrollView}`): the picture would scroll away with the content.
|
|
111
|
+
- **Give it a size.** Nothing picture-shaped sizes a photo card, so `style` carries an `aspectRatio` or a `minHeight`. The keys that arrange content (`justifyContent`, `alignItems`, `gap`, `flexDirection`) apply to the content laid over the picture, also under `as={Pressable}`.
|
|
112
|
+
|
|
113
|
+
Both hosts paint it: the web Player with a CSS background, the exported app with `expo-image`, which every export already bundles. Additive: a card given no `image` renders exactly as before.
|
|
114
|
+
|
|
115
|
+
### What's new in 0.221.1 (contract 1.177.1)
|
|
116
|
+
|
|
117
|
+
**`useFileSignature`: one failed poll no longer outlives the next answer.** `error` is the last call that failed, and a `refresh()` or `verify()` that succeeds now clears it. While `status` is `'pending'` an `error` means a poll failed: keep polling and keep showing the order. Fix only; nothing was added or removed.
|
|
88
118
|
|
|
89
119
|
### What's new in 0.221.0 (contract 1.177.0)
|
|
90
120
|
|
|
@@ -2775,7 +2805,7 @@ A widget that works but looks unfinished is only half done. `useTheme()` is the
|
|
|
2775
2805
|
- **Respond to touch.** Give every `Pressable` the lift via the function-style `style={(state) => [base, ...pressableLift(state)]}` — see "Answer the touch" above. Never dim with `opacity`, which reads as the surface disabling itself.
|
|
2776
2806
|
- **Drag and drop — show what is being dragged.** A drag where the item stays put reads as broken. Three things change the moment a drag starts: the **drag proxy** (the item lifts and follows the finger — `...theme.elevation.lg`, `{ scale: 1.03 }`, `opacity: 0.9`; for a tall or full-width item drag a compact `primarySoft` pill with its icon + one line of label instead), the **source placeholder** (the vacated slot keeps its height as a quiet `colors.surfaceMuted` block so the list doesn't collapse), and the **drop target** (one slot at a time highlighted with `primarySoft` or a 2px `colors.primary` border). Always animate the release — settle into the new slot, or `Animated.spring(pan, { toValue: { x: 0, y: 0 }, useNativeDriver: false })` back to the origin on cancel. Build it with `Animated` + `PanResponder` from `react-native` (the only mechanism that behaves identically on both hosts) — never HTML5 drag events (`draggable` / `onDragStart` / `dataTransfer` are web-only, and `document` / `window` are banned) — and start the drag from a `GripVertical` grip handle whenever the row is also tappable or sits in a `ScrollView`. **A board (kanban, pipeline) drops onto ANOTHER column**, which needs four more things: the board's root renders the one proxy (a column clips whatever leaves it); you hit-test the drop yourself — `measureInWindow` every column and the board when the drag starts and compare the gesture's window `moveX` (never `onLayout` frames, which are relative to a padded or scrolled parent) — highlighting exactly one target; the drop WRITES (`update(id, { [statusField]: value })` through `useDatastoreMutation`, optimistic until `refetch()`, a toast and a spring-back on failure); and three or more columns go in a `horizontal` `ScrollView` when `widthTier(width) === "narrow"`, with the grip claiming the gesture (`onStartShouldSetPanResponder`, `onPanResponderTerminationRequest: () => false`) and carrying `touchAction: 'none'` on web so a finger drag moves the card instead of scrolling the page. And because `useRef(PanResponder.create(...))` runs ONCE, everything its callbacks read — the `useCanWrite` flag included, which starts `false` — goes through a ref refreshed every render (`latest.current.canDrag`); the publish gate `widget.staleResponder` rejects a value read directly.
|
|
2777
2807
|
- **Use icons for clarity.** Pair a `lucide-react-native` icon with its label at a consistent size, coloured from the theme. The label never repeats the icon as a character — with a `Plus` icon the button says "Add item", never "+ Add item" (that renders a doubled plus).
|
|
2778
|
-
- **Use imagery deliberately.** Render pictures with the `Image` primitive (`source` takes a URL or `{ uri }`); resolve workspace assets via `useAsset()`. Give every image a sized, `radii`-clipped container so it never renders as a raw rectangle, and never hardcode a credentialed image URL — expose an `image`-type property instead. The frame is your decision, never the picture's: size it for the role (a 40–56 square avatar, a 72–96 square row thumbnail, a `16 / 9` card
|
|
2808
|
+
- **Use imagery deliberately.** Render pictures with the `Image` primitive (`source` takes a URL or `{ uri }`); resolve workspace assets via `useAsset()`. Give every image a sized, `radii`-clipped container so it never renders as a raw rectangle, and never hardcode a credentialed image URL — expose an `image`-type property instead. The frame is your decision, never the picture's: size it for the role (a 40–56 square avatar, a 72–96 square row thumbnail, a square photo tile, a `16 / 9` photo card, a 160–240 tall band) and let `resizeMode="cover"` crop the photo into it — photos arrive at every size and ratio, so one left to its own proportions breaks the layout. Keep `contain` for art whose whole subject must stay visible (a logo, a diagram), inside a fixed frame. When an item has a photograph, make it the card's background with the title on it — `<Card image={url} scrim="bottom">` with a function child for the colours — rather than a small picture with its text in a box underneath; keep the stacked form for a card whose text is long or dense.
|
|
2779
2809
|
- **Design the empty, loading, and error states.** A blank box on a fresh install reads as broken — show a short helper line when a list is empty, a calm loading line, and a single human sentence in `colors.danger` on error.
|
|
2780
2810
|
|
|
2781
2811
|
**Honest ceilings:** the styling surface is React Native style objects, not full CSS. Gradients come from the `<Gradient>` primitive (not a CSS `linear-gradient` string), depth comes from `theme.elevation` (not arbitrary `box-shadow` stacks), and there are no custom CSS keyframe animations or `transition` strings (the hover/press lift comes built into `pressableLift` — never write your own), no `filter` / `backdrop-filter` / `clip-path` / `mask` / blend modes, and no opacity-faked tints (that's what `primarySoft` is for). Aim for clean, confident, professional polish within those bounds — lifted surfaces, generous corners, one accent moment.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// The picture behind a photo-backed `<Card>` (web). A CSS background, because
|
|
2
|
+
// `background-position` is what keeps the chosen part of a crop in frame.
|
|
3
|
+
|
|
4
|
+
import React from "react";
|
|
5
|
+
import * as ReactNative from "react-native-web";
|
|
6
|
+
|
|
7
|
+
// A quote, backslash or newline would end the CSS `url("…")` early.
|
|
8
|
+
function cssUrl(uri) {
|
|
9
|
+
const safe = uri.replace(/[\n\r\f]/g, "").replace(/\\/g, "%5C").replace(/"/g, "%22");
|
|
10
|
+
return `url("${safe}")`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function Photo({ uri, position, style }) {
|
|
14
|
+
return React.createElement(ReactNative.View, {
|
|
15
|
+
"aria-hidden": true,
|
|
16
|
+
style: [
|
|
17
|
+
style,
|
|
18
|
+
{
|
|
19
|
+
backgroundImage: cssUrl(uri),
|
|
20
|
+
backgroundSize: "cover",
|
|
21
|
+
backgroundPosition: position,
|
|
22
|
+
backgroundRepeat: "no-repeat",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// The picture behind a photo-backed `<Card>` (native). expo-image, because
|
|
2
|
+
// `contentPosition` is the only native equivalent of CSS background-position;
|
|
3
|
+
// React Native's own Image can only centre a `cover` crop.
|
|
4
|
+
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { Image } from "react-native";
|
|
7
|
+
|
|
8
|
+
// Pinned in every export; only a dev client older than the pin lacks it.
|
|
9
|
+
let ExpoImage = null;
|
|
10
|
+
try {
|
|
11
|
+
ExpoImage = require("expo-image").Image;
|
|
12
|
+
} catch (_) {
|
|
13
|
+
ExpoImage = null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Photo({ uri, position, style }) {
|
|
17
|
+
if (!ExpoImage) {
|
|
18
|
+
return React.createElement(Image, {
|
|
19
|
+
source: { uri },
|
|
20
|
+
resizeMode: "cover",
|
|
21
|
+
style,
|
|
22
|
+
accessible: false,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return React.createElement(ExpoImage, {
|
|
26
|
+
source: { uri },
|
|
27
|
+
contentFit: "cover",
|
|
28
|
+
contentPosition: position,
|
|
29
|
+
style,
|
|
30
|
+
accessible: false,
|
|
31
|
+
});
|
|
32
|
+
}
|
package/dist/card-style.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
normaliseComponentGradient,
|
|
12
12
|
componentGradientColors,
|
|
13
13
|
componentGradientMidpoint,
|
|
14
|
+
deriveSurfaceTokens,
|
|
14
15
|
} from "./contract.js";
|
|
15
16
|
import { normaliseCornerRadius, cornerRadiusStyle } from "./corner-radius.js";
|
|
16
17
|
import { normaliseSpacing, spacingStyle, isZeroSpacing } from "./spacing.js";
|
|
@@ -160,3 +161,74 @@ function isOpaqueHex(value) {
|
|
|
160
161
|
export function cardGradientProps(style) {
|
|
161
162
|
return gradientProps(style && style.cardGradient);
|
|
162
163
|
}
|
|
164
|
+
|
|
165
|
+
// ---- The photo-backed card: a picture edge to edge, content laid over it ----
|
|
166
|
+
|
|
167
|
+
export const CARD_SCRIMS = Object.freeze(["bottom", "top", "left", "right", "full", "none"]);
|
|
168
|
+
export const DEFAULT_CARD_SCRIM = "bottom";
|
|
169
|
+
|
|
170
|
+
export const CARD_IMAGE_POSITIONS = Object.freeze(["center", "top", "bottom", "left", "right"]);
|
|
171
|
+
|
|
172
|
+
// What content over a photo is inked against, and what shows while it loads.
|
|
173
|
+
export const CARD_PHOTO_BACKDROP = "#111827";
|
|
174
|
+
|
|
175
|
+
// The dark end sits on the named edge, where the text is.
|
|
176
|
+
const SCRIM_ANGLES = Object.freeze({ bottom: 180, top: 0, left: 270, right: 90 });
|
|
177
|
+
// Clear over most of the picture, so the photo stays a photo.
|
|
178
|
+
const SCRIM_EDGE = Object.freeze(["#00000000", "#00000026", "#000000b8"]);
|
|
179
|
+
const SCRIM_FULL = Object.freeze(["#00000066", "#00000066"]);
|
|
180
|
+
|
|
181
|
+
/** The picture's address from a URL or an RN `{ uri }` source, else null. */
|
|
182
|
+
export function cardPhotoUri(image) {
|
|
183
|
+
const raw = image && typeof image === "object" ? image.uri : image;
|
|
184
|
+
if (typeof raw !== "string") return null;
|
|
185
|
+
const uri = raw.trim();
|
|
186
|
+
return uri ? uri : null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The `<Gradient>` props that keep text readable on a photo; null for none. */
|
|
190
|
+
export function cardScrimProps(scrim) {
|
|
191
|
+
const name = CARD_SCRIMS.includes(scrim) ? scrim : DEFAULT_CARD_SCRIM;
|
|
192
|
+
if (name === "none") return null;
|
|
193
|
+
if (name === "full") return { colors: [...SCRIM_FULL], angle: 180 };
|
|
194
|
+
return { colors: [...SCRIM_EDGE], angle: SCRIM_ANGLES[name] };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Which part of the picture a cropped card keeps in frame. */
|
|
198
|
+
export function cardImagePosition(value) {
|
|
199
|
+
return CARD_IMAGE_POSITIONS.includes(value) ? value : "center";
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Theme colours as content over a photo reads them, for a card with no host. */
|
|
203
|
+
export function cardPhotoColors(colors) {
|
|
204
|
+
return {
|
|
205
|
+
...colors,
|
|
206
|
+
...deriveSurfaceTokens(CARD_PHOTO_BACKDROP),
|
|
207
|
+
backdrop: CARD_PHOTO_BACKDROP,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const RADIUS_FIELDS = Object.freeze([
|
|
212
|
+
"borderRadius", "borderTopLeftRadius", "borderTopRightRadius",
|
|
213
|
+
"borderBottomRightRadius", "borderBottomLeftRadius",
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The layer that holds a photo card's picture: it fills the card behind the
|
|
218
|
+
* content and rounds the picture to the card's corners. Its own layer, since
|
|
219
|
+
* iOS drops the shadow of a view that clips.
|
|
220
|
+
*
|
|
221
|
+
* @param {object|Array} styles - the card's composed container style.
|
|
222
|
+
*/
|
|
223
|
+
export function cardPhotoClip(styles) {
|
|
224
|
+
const flat = flattenStyle(styles);
|
|
225
|
+
const clip = {
|
|
226
|
+
position: "absolute", top: 0, right: 0, bottom: 0, left: 0,
|
|
227
|
+
overflow: "hidden",
|
|
228
|
+
pointerEvents: "none",
|
|
229
|
+
};
|
|
230
|
+
for (const key of RADIUS_FIELDS) {
|
|
231
|
+
if (flat[key] !== undefined) clip[key] = flat[key];
|
|
232
|
+
}
|
|
233
|
+
return clip;
|
|
234
|
+
}
|
package/dist/card-view.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
//
|
|
5
5
|
// A gradient is a COMPONENT on both hosts, never a style value, so the element
|
|
6
6
|
// swap lives here once rather than in each widget.
|
|
7
|
+
//
|
|
8
|
+
// A card given an `image` is a PHOTO card: the same element, with the picture
|
|
9
|
+
// and a readability scrim on a layer behind its content.
|
|
7
10
|
|
|
8
11
|
import React from "react";
|
|
9
12
|
import { useHostTheme, BackdropScope } from "./hooks.js";
|
|
@@ -15,6 +18,12 @@ import {
|
|
|
15
18
|
cardGradientProps,
|
|
16
19
|
cardLayoutStyle,
|
|
17
20
|
cardBackdrop,
|
|
21
|
+
cardPhotoUri,
|
|
22
|
+
cardScrimProps,
|
|
23
|
+
cardImagePosition,
|
|
24
|
+
cardPhotoClip,
|
|
25
|
+
cardPhotoColors,
|
|
26
|
+
CARD_PHOTO_BACKDROP,
|
|
18
27
|
} from "./card-style.js";
|
|
19
28
|
import { normaliseThemeComponents } from "./theme-components.js";
|
|
20
29
|
import { flattenStyle } from "./flatten-style.js";
|
|
@@ -31,6 +40,12 @@ const FILL_IN_GRADIENT = {
|
|
|
31
40
|
|
|
32
41
|
const CLIP_TO_RADIUS = { overflow: "hidden" };
|
|
33
42
|
|
|
43
|
+
// The fill shows while the picture loads, so light text never sits on white.
|
|
44
|
+
const PHOTO_BOX = { backgroundColor: CARD_PHOTO_BACKDROP };
|
|
45
|
+
const PHOTO_LAYER = { position: "absolute", top: 0, right: 0, bottom: 0, left: 0 };
|
|
46
|
+
// The theme's hairline reads as a stray outline around a photograph.
|
|
47
|
+
const NO_BORDER = { borderWidth: 0 };
|
|
48
|
+
|
|
34
49
|
const CARD_FIELDS = CONTRACT.themeComponents.card.universalFields;
|
|
35
50
|
|
|
36
51
|
// The convenience props, each named for the `card*` field it sets.
|
|
@@ -61,6 +76,19 @@ function themedFields(theme) {
|
|
|
61
76
|
return out;
|
|
62
77
|
}
|
|
63
78
|
|
|
79
|
+
// Inside the card's scope, so a function child reads the card's own inks.
|
|
80
|
+
function CardContent({ onImage, children }) {
|
|
81
|
+
const hosted = useHostTheme();
|
|
82
|
+
if (typeof children !== "function") return children;
|
|
83
|
+
// No host means no scope to re-ink through; the photo is dark regardless.
|
|
84
|
+
const colors = hosted
|
|
85
|
+
? hosted.colors
|
|
86
|
+
: onImage
|
|
87
|
+
? cardPhotoColors(DEFAULT_THEME_TOKENS.colors)
|
|
88
|
+
: DEFAULT_THEME_TOKENS.colors;
|
|
89
|
+
return children({ colors, onImage });
|
|
90
|
+
}
|
|
91
|
+
|
|
64
92
|
function themedBase(theme) {
|
|
65
93
|
const { colors, radii, spacing } = theme;
|
|
66
94
|
return {
|
|
@@ -75,10 +103,10 @@ function themedBase(theme) {
|
|
|
75
103
|
/**
|
|
76
104
|
* Bind the shared card to one platform's primitives.
|
|
77
105
|
*
|
|
78
|
-
* @param {{View: any, Gradient: any}} rn
|
|
106
|
+
* @param {{View: any, Gradient: any, Photo: any}} rn
|
|
79
107
|
*/
|
|
80
108
|
export function makeCard(rn) {
|
|
81
|
-
const { View, Gradient } = rn;
|
|
109
|
+
const { View, Gradient, Photo } = rn;
|
|
82
110
|
|
|
83
111
|
/**
|
|
84
112
|
* @param {object|Array} [props.style] — `card*` fields, `shadow`, `margin`
|
|
@@ -101,6 +129,16 @@ export function makeCard(rn) {
|
|
|
101
129
|
* element receiving the other props, so a Pressable's feedback lands.
|
|
102
130
|
* @param {boolean} [props.withoutMargin] — leave the margin off this surface
|
|
103
131
|
* for a widget that rides it on an outer root.
|
|
132
|
+
* @param {string|{uri: string}} [props.image] — a picture painted edge to
|
|
133
|
+
* edge BEHIND the content. With none (a record without a photo) the card
|
|
134
|
+
* is an ordinary card, so one widget serves both.
|
|
135
|
+
* @param {string} [props.imagePosition] — the part kept in frame when
|
|
136
|
+
* cropped (`CARD_IMAGE_POSITIONS`).
|
|
137
|
+
* @param {string} [props.scrim] — the edge the readability gradient darkens
|
|
138
|
+
* (`CARD_SCRIMS`); the edge the text sits on.
|
|
139
|
+
* @param {React.ReactNode|Function} [props.children] — a function is called
|
|
140
|
+
* with `{ colors, onImage }`: the theme colours as they read where the
|
|
141
|
+
* content sits, light over a photo and the theme's own without one.
|
|
104
142
|
*/
|
|
105
143
|
function Card(props) {
|
|
106
144
|
const {
|
|
@@ -110,6 +148,9 @@ export function makeCard(rn) {
|
|
|
110
148
|
fillInGradient = false,
|
|
111
149
|
containerStyle,
|
|
112
150
|
withoutMargin = false,
|
|
151
|
+
image,
|
|
152
|
+
imagePosition,
|
|
153
|
+
scrim,
|
|
113
154
|
children,
|
|
114
155
|
...others
|
|
115
156
|
} = props;
|
|
@@ -118,6 +159,7 @@ export function makeCard(rn) {
|
|
|
118
159
|
if (!(key in PROP_FIELDS)) rest[key] = value;
|
|
119
160
|
}
|
|
120
161
|
const theme = useHostTheme() || DEFAULT_THEME_TOKENS;
|
|
162
|
+
const uri = cardPhotoUri(image);
|
|
121
163
|
|
|
122
164
|
const explicit = {};
|
|
123
165
|
for (const [prop, field] of Object.entries(PROP_FIELDS)) {
|
|
@@ -137,17 +179,49 @@ export function makeCard(rn) {
|
|
|
137
179
|
const layout = standalone ? cardLayoutStyle(own) : null;
|
|
138
180
|
|
|
139
181
|
const overlay = withoutMargin ? cardSurfaceStyle(fields) : cardOverlay(fields);
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
182
|
+
|
|
183
|
+
const gradient = uri ? null : cardGradientProps(fields);
|
|
184
|
+
const basis = uri
|
|
185
|
+
? CARD_PHOTO_BACKDROP
|
|
186
|
+
: cardBackdrop(
|
|
187
|
+
[base, overlay, typeof containerStyle === "function" ? null : containerStyle],
|
|
188
|
+
gradient,
|
|
189
|
+
);
|
|
190
|
+
const scoped = React.createElement(
|
|
191
|
+
BackdropScope,
|
|
192
|
+
{ basis, always: Boolean(uri) },
|
|
193
|
+
React.createElement(CardContent, { onImage: Boolean(uri) }, children),
|
|
144
194
|
);
|
|
145
|
-
const scoped = React.createElement(BackdropScope, { basis }, children);
|
|
146
195
|
|
|
147
196
|
if (!gradient) {
|
|
197
|
+
const box = [
|
|
198
|
+
base,
|
|
199
|
+
overlay,
|
|
200
|
+
layout,
|
|
201
|
+
uri ? PHOTO_BOX : null,
|
|
202
|
+
uri && !fields.cardBorderColor ? NO_BORDER : null,
|
|
203
|
+
];
|
|
204
|
+
const scrimProps = uri ? cardScrimProps(scrim) : null;
|
|
205
|
+
const ownStyle = typeof containerStyle === "function" ? null : containerStyle;
|
|
206
|
+
// The layer keeps its slot with no photo, so content survives one arriving.
|
|
207
|
+
const layer = uri
|
|
208
|
+
? React.createElement(
|
|
209
|
+
View,
|
|
210
|
+
{ style: cardPhotoClip([box, ownStyle]) },
|
|
211
|
+
React.createElement(Photo, {
|
|
212
|
+
uri,
|
|
213
|
+
position: cardImagePosition(imagePosition),
|
|
214
|
+
style: PHOTO_LAYER,
|
|
215
|
+
}),
|
|
216
|
+
scrimProps
|
|
217
|
+
? React.createElement(Gradient, { ...scrimProps, style: PHOTO_LAYER })
|
|
218
|
+
: null,
|
|
219
|
+
)
|
|
220
|
+
: null;
|
|
148
221
|
return React.createElement(
|
|
149
222
|
Container,
|
|
150
|
-
{ style: composeStyle(
|
|
223
|
+
{ style: composeStyle(box, containerStyle), ...rest },
|
|
224
|
+
layer,
|
|
151
225
|
scoped,
|
|
152
226
|
);
|
|
153
227
|
}
|
package/dist/card.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import * as ReactNative from "react-native-web";
|
|
5
5
|
import { Gradient } from "./gradient.js";
|
|
6
|
+
import { Photo } from "./card-photo.js";
|
|
6
7
|
import { makeCard } from "./card-view.js";
|
|
7
8
|
|
|
8
|
-
export const Card = makeCard({ View: ReactNative.View, Gradient });
|
|
9
|
+
export const Card = makeCard({ View: ReactNative.View, Gradient, Photo });
|
package/dist/card.native.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { View } from "react-native";
|
|
5
5
|
import { Gradient } from "./gradient.native.js";
|
|
6
|
+
import { Photo } from "./card-photo.native.js";
|
|
6
7
|
import { makeCard } from "./card-view.js";
|
|
7
8
|
|
|
8
|
-
export const Card = makeCard({ View, Gradient });
|
|
9
|
+
export const Card = makeCard({ View, Gradient, Photo });
|
package/dist/contract.cjs
CHANGED
|
@@ -1559,7 +1559,10 @@ const HOOKS = [
|
|
|
1559
1559
|
"be cancelled. cancel() resolves to what became of the order, which is " +
|
|
1560
1560
|
"then still pending or already complete: read status after it, never " +
|
|
1561
1561
|
"assume 'cancelled'. nameRefusal is set when the order failed because " +
|
|
1562
|
-
"the signer's name cannot be shown on the document.
|
|
1562
|
+
"the signer's name cannot be shown on the document. error is the last " +
|
|
1563
|
+
"call that failed, and a refresh() or verify() that succeeds clears it: " +
|
|
1564
|
+
"while status is 'pending' an error means one poll failed, so keep " +
|
|
1565
|
+
"polling and keep showing the order. Reads " +
|
|
1563
1566
|
"ctx.filestore.signatures.{initiate,status,cancel,verify}.",
|
|
1564
1567
|
returnShape: {
|
|
1565
1568
|
status: "'pending' | 'complete' | 'failed' | 'cancelled' | null",
|
|
@@ -3242,7 +3245,7 @@ const PRIMITIVES = [
|
|
|
3242
3245
|
{
|
|
3243
3246
|
name: "Card",
|
|
3244
3247
|
description:
|
|
3245
|
-
'Themed card surface. `<Card>…</Card>` — THE way to draw a card, panel or tile: with no props it takes the app\'s card look (Theme Settings > Cards: background, border, corner radius, padding, shadow, gradient) so it matches every built-in card. Override one card with `background`, `borderColor`, `radius`, `padding` (a number or `{ top, right, bottom, left }`), `shadow` ("none" | "sm" | "md" | "lg") or `gradient` (`{ from, to, angle }`), or pass `style={props.style}` when your widget declares the `cardBackground` / `cardBorderColor` / `cardRadius` / `cardPadding` / `cardGradient` / `shadow` / `margin` style fields, so an author can restyle it. A card that brings no `baseStyle` also takes the box\'s LAYOUT from `style` — width, height, the flex properties, `alignSelf`, `gap`, position — so `style={{ width: \'100%\', maxWidth: 480, alignSelf: \'center\' }}` sizes the card and still keeps the app\'s card look; put anything else about the container on `containerStyle`. `style` may be an object or a React Native style array (`style={[{ flex: 1, minWidth: 0 }, align && { alignItems: align }]}`), read as the host flattens it: nested arrays and falsy entries are fine and a later entry wins. A gradient paints on both hosts. `as` renders the card as another container (e.g. `Pressable`, `ScrollView`) and every other prop lands on it. Never hand-roll a card from `View` + border + radius.',
|
|
3248
|
+
'Themed card surface. `<Card>…</Card>` — THE way to draw a card, panel or tile: with no props it takes the app\'s card look (Theme Settings > Cards: background, border, corner radius, padding, shadow, gradient) so it matches every built-in card. Override one card with `background`, `borderColor`, `radius`, `padding` (a number or `{ top, right, bottom, left }`), `shadow` ("none" | "sm" | "md" | "lg") or `gradient` (`{ from, to, angle }`), or pass `style={props.style}` when your widget declares the `cardBackground` / `cardBorderColor` / `cardRadius` / `cardPadding` / `cardGradient` / `shadow` / `margin` style fields, so an author can restyle it. A card that brings no `baseStyle` also takes the box\'s LAYOUT from `style` — width, height, the flex properties, `alignSelf`, `gap`, position — so `style={{ width: \'100%\', maxWidth: 480, alignSelf: \'center\' }}` sizes the card and still keeps the app\'s card look; put anything else about the container on `containerStyle`. `style` may be an object or a React Native style array (`style={[{ flex: 1, minWidth: 0 }, align && { alignItems: align }]}`), read as the host flattens it: nested arrays and falsy entries are fine and a later entry wins. A gradient paints on both hosts. `image` (a URL or `{ uri }`) makes it a PHOTO card: the picture fills the card edge to edge BEHIND the content, under a light gradient on the edge named by `scrim` ("bottom" default | "top" | "left" | "right" | "full" | "none") — the edge the text sits on — and cropped around `imagePosition` ("center" default | "top" | "bottom" | "left" | "right"). A photo card has no picture-shaped content to size it, so give it an `aspectRatio` or `minHeight` in `style`, and pass the children as a function — `{({ colors, onImage }) => …}` — to receive the theme colours as they read where the content sits (`colors.onSurface` for a title, `colors.onSurfaceMuted` for a supporting line): light over a picture, the theme\'s own on a card without one. `image={null}` renders an ordinary card, so a record with no photo needs no branch. A photo card is a `View` or, with `as`, a `Pressable`; put a scroller inside it, never as it. Use it whenever an item has a photograph, instead of a small `Image` with its text in a box underneath. `as` renders the card as another container (e.g. `Pressable`, `ScrollView`) and every other prop lands on it. Never hand-roll a card from `View` + border + radius.',
|
|
3246
3249
|
rnComponent: null,
|
|
3247
3250
|
docsUrl: null,
|
|
3248
3251
|
},
|
|
@@ -5868,7 +5871,14 @@ const CONTRACT = deepFreeze({
|
|
|
5868
5871
|
// and attach to it in one handler. A FILE column's file goes in its
|
|
5869
5872
|
// row's record space; useCamera / useImageEditor point at
|
|
5870
5873
|
// useFilestoreUpload, since widget code cannot reach ctx.assets.
|
|
5871
|
-
|
|
5874
|
+
// 1.177.1: fix (sc-10410) — a `refresh()` or `verify()` that succeeds
|
|
5875
|
+
// clears `useFileSignature`'s `error`; it used to keep one failed poll's
|
|
5876
|
+
// error for the rest of the order.
|
|
5877
|
+
// 1.178.0: additive (sc-11272) — `<Card image>` paints a picture edge to
|
|
5878
|
+
// edge behind the card's content, with a `scrim` on the text's edge and
|
|
5879
|
+
// an `imagePosition` for the crop. Children may be a function taking
|
|
5880
|
+
// `{ colors, onImage }`: the theme colours as they read over the picture.
|
|
5881
|
+
version: "1.178.0",
|
|
5872
5882
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
5873
5883
|
hooks: HOOKS,
|
|
5874
5884
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -1559,7 +1559,10 @@ const HOOKS = [
|
|
|
1559
1559
|
"be cancelled. cancel() resolves to what became of the order, which is " +
|
|
1560
1560
|
"then still pending or already complete: read status after it, never " +
|
|
1561
1561
|
"assume 'cancelled'. nameRefusal is set when the order failed because " +
|
|
1562
|
-
"the signer's name cannot be shown on the document.
|
|
1562
|
+
"the signer's name cannot be shown on the document. error is the last " +
|
|
1563
|
+
"call that failed, and a refresh() or verify() that succeeds clears it: " +
|
|
1564
|
+
"while status is 'pending' an error means one poll failed, so keep " +
|
|
1565
|
+
"polling and keep showing the order. Reads " +
|
|
1563
1566
|
"ctx.filestore.signatures.{initiate,status,cancel,verify}.",
|
|
1564
1567
|
returnShape: {
|
|
1565
1568
|
status: "'pending' | 'complete' | 'failed' | 'cancelled' | null",
|
|
@@ -3242,7 +3245,7 @@ const PRIMITIVES = [
|
|
|
3242
3245
|
{
|
|
3243
3246
|
name: "Card",
|
|
3244
3247
|
description:
|
|
3245
|
-
'Themed card surface. `<Card>…</Card>` — THE way to draw a card, panel or tile: with no props it takes the app\'s card look (Theme Settings > Cards: background, border, corner radius, padding, shadow, gradient) so it matches every built-in card. Override one card with `background`, `borderColor`, `radius`, `padding` (a number or `{ top, right, bottom, left }`), `shadow` ("none" | "sm" | "md" | "lg") or `gradient` (`{ from, to, angle }`), or pass `style={props.style}` when your widget declares the `cardBackground` / `cardBorderColor` / `cardRadius` / `cardPadding` / `cardGradient` / `shadow` / `margin` style fields, so an author can restyle it. A card that brings no `baseStyle` also takes the box\'s LAYOUT from `style` — width, height, the flex properties, `alignSelf`, `gap`, position — so `style={{ width: \'100%\', maxWidth: 480, alignSelf: \'center\' }}` sizes the card and still keeps the app\'s card look; put anything else about the container on `containerStyle`. `style` may be an object or a React Native style array (`style={[{ flex: 1, minWidth: 0 }, align && { alignItems: align }]}`), read as the host flattens it: nested arrays and falsy entries are fine and a later entry wins. A gradient paints on both hosts. `as` renders the card as another container (e.g. `Pressable`, `ScrollView`) and every other prop lands on it. Never hand-roll a card from `View` + border + radius.',
|
|
3248
|
+
'Themed card surface. `<Card>…</Card>` — THE way to draw a card, panel or tile: with no props it takes the app\'s card look (Theme Settings > Cards: background, border, corner radius, padding, shadow, gradient) so it matches every built-in card. Override one card with `background`, `borderColor`, `radius`, `padding` (a number or `{ top, right, bottom, left }`), `shadow` ("none" | "sm" | "md" | "lg") or `gradient` (`{ from, to, angle }`), or pass `style={props.style}` when your widget declares the `cardBackground` / `cardBorderColor` / `cardRadius` / `cardPadding` / `cardGradient` / `shadow` / `margin` style fields, so an author can restyle it. A card that brings no `baseStyle` also takes the box\'s LAYOUT from `style` — width, height, the flex properties, `alignSelf`, `gap`, position — so `style={{ width: \'100%\', maxWidth: 480, alignSelf: \'center\' }}` sizes the card and still keeps the app\'s card look; put anything else about the container on `containerStyle`. `style` may be an object or a React Native style array (`style={[{ flex: 1, minWidth: 0 }, align && { alignItems: align }]}`), read as the host flattens it: nested arrays and falsy entries are fine and a later entry wins. A gradient paints on both hosts. `image` (a URL or `{ uri }`) makes it a PHOTO card: the picture fills the card edge to edge BEHIND the content, under a light gradient on the edge named by `scrim` ("bottom" default | "top" | "left" | "right" | "full" | "none") — the edge the text sits on — and cropped around `imagePosition` ("center" default | "top" | "bottom" | "left" | "right"). A photo card has no picture-shaped content to size it, so give it an `aspectRatio` or `minHeight` in `style`, and pass the children as a function — `{({ colors, onImage }) => …}` — to receive the theme colours as they read where the content sits (`colors.onSurface` for a title, `colors.onSurfaceMuted` for a supporting line): light over a picture, the theme\'s own on a card without one. `image={null}` renders an ordinary card, so a record with no photo needs no branch. A photo card is a `View` or, with `as`, a `Pressable`; put a scroller inside it, never as it. Use it whenever an item has a photograph, instead of a small `Image` with its text in a box underneath. `as` renders the card as another container (e.g. `Pressable`, `ScrollView`) and every other prop lands on it. Never hand-roll a card from `View` + border + radius.',
|
|
3246
3249
|
rnComponent: null,
|
|
3247
3250
|
docsUrl: null,
|
|
3248
3251
|
},
|
|
@@ -5864,7 +5867,14 @@ const CONTRACT = deepFreeze({
|
|
|
5864
5867
|
// and attach to it in one handler. A FILE column's file goes in its
|
|
5865
5868
|
// row's record space; useCamera / useImageEditor point at
|
|
5866
5869
|
// useFilestoreUpload, since widget code cannot reach ctx.assets.
|
|
5867
|
-
|
|
5870
|
+
// 1.177.1: fix (sc-10410) — a `refresh()` or `verify()` that succeeds
|
|
5871
|
+
// clears `useFileSignature`'s `error`; it used to keep one failed poll's
|
|
5872
|
+
// error for the rest of the order.
|
|
5873
|
+
// 1.178.0: additive (sc-11272) — `<Card image>` paints a picture edge to
|
|
5874
|
+
// edge behind the card's content, with a `scrim` on the text's edge and
|
|
5875
|
+
// an `imagePosition` for the crop. Children may be a function taking
|
|
5876
|
+
// `{ colors, onImage }`: the theme colours as they read over the picture.
|
|
5877
|
+
version: "1.178.0",
|
|
5868
5878
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
5869
5879
|
hooks: HOOKS,
|
|
5870
5880
|
primitives: PRIMITIVES,
|
package/dist/hooks.js
CHANGED
|
@@ -72,17 +72,20 @@ export function WidgetContextProvider({ value, children }) {
|
|
|
72
72
|
* @internal — a surface painting its own fill inside a coloured section
|
|
73
73
|
* becomes the surface its content reads and its buttons are measured against
|
|
74
74
|
* (`<Card>` uses it), by the derivation a layout container applies.
|
|
75
|
-
* Outside such a section there is no backdrop and nothing changes
|
|
75
|
+
* Outside such a section there is no backdrop and nothing changes, unless
|
|
76
|
+
* `always`: a surface whose fill is fixed (a photograph) re-inks everywhere.
|
|
76
77
|
*/
|
|
77
|
-
export function BackdropScope({ basis, children }) {
|
|
78
|
+
export function BackdropScope({ basis, always = false, children }) {
|
|
78
79
|
const ctx = useContext(HostWidgetContext);
|
|
79
80
|
const theme = ctx && ctx.workspace ? ctx.workspace.theme : null;
|
|
80
81
|
const colors = theme && theme.colors;
|
|
81
|
-
const scoped = Boolean(
|
|
82
|
+
const scoped = Boolean(
|
|
83
|
+
basis && colors && (always || (colors.backdrop && colors.backdrop !== basis)),
|
|
84
|
+
);
|
|
82
85
|
const value = useMemo(
|
|
83
86
|
() =>
|
|
84
87
|
scoped
|
|
85
|
-
? { ...ctx, workspace: { ...ctx.workspace, theme: { ...theme, colors: { ...colors, ...deriveSurfaceTokens(basis), backdrop: basis } } } }
|
|
88
|
+
? { ...ctx, workspace: { ...ctx.workspace, theme: { ...theme, colors: { ...colors, ...deriveSurfaceTokens(basis), backdrop: basis, backdropBase: colors.backdropBase || colors.surface } } } }
|
|
86
89
|
: ctx,
|
|
87
90
|
[scoped, ctx, theme, colors, basis],
|
|
88
91
|
);
|
package/dist/index.d.ts
CHANGED
|
@@ -3697,6 +3697,9 @@ export function initialsFor(name: unknown): string;
|
|
|
3697
3697
|
* @example
|
|
3698
3698
|
* <Card><Text>…</Text></Card>
|
|
3699
3699
|
* <Card as={Pressable} onPress={open} radius={12} shadow="md">…</Card>
|
|
3700
|
+
* <Card image={url} scrim="bottom" style={{ aspectRatio: 1, justifyContent: "flex-end" }}>
|
|
3701
|
+
* {({ colors }) => <Text style={{ color: colors.onSurface }}>…</Text>}
|
|
3702
|
+
* </Card>
|
|
3700
3703
|
*/
|
|
3701
3704
|
export const Card: (props: {
|
|
3702
3705
|
background?: string;
|
|
@@ -3705,6 +3708,15 @@ export const Card: (props: {
|
|
|
3705
3708
|
padding?: number | { top?: number; right?: number; bottom?: number; left?: number };
|
|
3706
3709
|
shadow?: "none" | "sm" | "md" | "lg";
|
|
3707
3710
|
gradient?: { from: string; to: string; angle?: number };
|
|
3711
|
+
/**
|
|
3712
|
+
* A picture painted edge to edge behind the content. With none the card is
|
|
3713
|
+
* an ordinary card, so a record without a photo needs no branch.
|
|
3714
|
+
*/
|
|
3715
|
+
image?: string | { uri: string } | null;
|
|
3716
|
+
/** The part of the picture kept in frame when cropped. Default "center". */
|
|
3717
|
+
imagePosition?: CardImagePosition;
|
|
3718
|
+
/** The edge the readability gradient darkens. Default "bottom". */
|
|
3719
|
+
scrim?: CardScrim;
|
|
3708
3720
|
/**
|
|
3709
3721
|
* `card*` style fields, `shadow`, `margin` — a widget's per-instance style.
|
|
3710
3722
|
* With no `baseStyle` it also carries the box's layout (width, height, the
|
|
@@ -3721,10 +3733,17 @@ export const Card: (props: {
|
|
|
3721
3733
|
fillInGradient?: boolean;
|
|
3722
3734
|
containerStyle?: any;
|
|
3723
3735
|
withoutMargin?: boolean;
|
|
3724
|
-
|
|
3736
|
+
/**
|
|
3737
|
+
* A function is called with the theme colours as they read where the
|
|
3738
|
+
* content sits: light over a picture, the theme's own without one.
|
|
3739
|
+
*/
|
|
3740
|
+
children?: ReactNode | ((ink: { colors: Record<string, string>; onImage: boolean }) => ReactNode);
|
|
3725
3741
|
[prop: string]: any;
|
|
3726
3742
|
}) => any;
|
|
3727
3743
|
|
|
3744
|
+
export type CardScrim = "bottom" | "top" | "left" | "right" | "full" | "none";
|
|
3745
|
+
export type CardImagePosition = "center" | "top" | "bottom" | "left" | "right";
|
|
3746
|
+
|
|
3728
3747
|
export type ButtonVariant = "primary" | "secondary" | "outlined" | "danger";
|
|
3729
3748
|
export const BUTTON_VARIANTS: readonly ButtonVariant[];
|
|
3730
3749
|
|
package/dist/signing-hooks.js
CHANGED
|
@@ -40,7 +40,9 @@ const userIdOf = (ctx) => (ctx.user && ctx.user.id) || "";
|
|
|
40
40
|
* with `hintCode: "sealing"`. `hintCode` is where the order stands;
|
|
41
41
|
* `nameRefusal` (`{ reason, characters }`) is set when it failed because the
|
|
42
42
|
* signer's name cannot be shown. `autoStartToken` is set only while opening
|
|
43
|
-
* BankID can still help.
|
|
43
|
+
* BankID can still help. `error` is the last call that failed; a `refresh()`
|
|
44
|
+
* or `verify()` that succeeds clears it, so one failed poll does not outlive
|
|
45
|
+
* the next answer. Reads
|
|
44
46
|
* `ctx.filestore.signatures.{initiate,status,cancel,verify}`. When `fileId` is
|
|
45
47
|
* null/empty the hook collapses to a no-op.
|
|
46
48
|
*
|
|
@@ -128,7 +130,8 @@ export function useFileSignature(fileId, existingSignatureId = null) {
|
|
|
128
130
|
const res = await sigRef.current.status(id);
|
|
129
131
|
// A cancel or a new order since this poll was sent: its answer is old news.
|
|
130
132
|
if (answersRef.current !== asked) return;
|
|
131
|
-
|
|
133
|
+
// An answer means the order can be reached again: an earlier failure is over.
|
|
134
|
+
apply(forScope, (base) => ({ order: signOrderStateAfterAnswer(base.order, res), error: null }));
|
|
132
135
|
} catch (err) {
|
|
133
136
|
if (answersRef.current !== asked) return;
|
|
134
137
|
apply(forScope, () => ({ error: toPermissionError(err) }));
|
|
@@ -156,7 +159,7 @@ export function useFileSignature(fileId, existingSignatureId = null) {
|
|
|
156
159
|
if (!id) return null;
|
|
157
160
|
try {
|
|
158
161
|
const v = await sigRef.current.verify(id);
|
|
159
|
-
apply(forScope, () => ({ verdict: v }));
|
|
162
|
+
apply(forScope, () => ({ verdict: v, error: null }));
|
|
160
163
|
return v;
|
|
161
164
|
} catch (err) {
|
|
162
165
|
const refusal = toPermissionError(err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.222.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
|
@@ -1559,7 +1559,10 @@ const HOOKS = [
|
|
|
1559
1559
|
"be cancelled. cancel() resolves to what became of the order, which is " +
|
|
1560
1560
|
"then still pending or already complete: read status after it, never " +
|
|
1561
1561
|
"assume 'cancelled'. nameRefusal is set when the order failed because " +
|
|
1562
|
-
"the signer's name cannot be shown on the document.
|
|
1562
|
+
"the signer's name cannot be shown on the document. error is the last " +
|
|
1563
|
+
"call that failed, and a refresh() or verify() that succeeds clears it: " +
|
|
1564
|
+
"while status is 'pending' an error means one poll failed, so keep " +
|
|
1565
|
+
"polling and keep showing the order. Reads " +
|
|
1563
1566
|
"ctx.filestore.signatures.{initiate,status,cancel,verify}.",
|
|
1564
1567
|
returnShape: {
|
|
1565
1568
|
status: "'pending' | 'complete' | 'failed' | 'cancelled' | null",
|
|
@@ -3242,7 +3245,7 @@ const PRIMITIVES = [
|
|
|
3242
3245
|
{
|
|
3243
3246
|
name: "Card",
|
|
3244
3247
|
description:
|
|
3245
|
-
'Themed card surface. `<Card>…</Card>` — THE way to draw a card, panel or tile: with no props it takes the app\'s card look (Theme Settings > Cards: background, border, corner radius, padding, shadow, gradient) so it matches every built-in card. Override one card with `background`, `borderColor`, `radius`, `padding` (a number or `{ top, right, bottom, left }`), `shadow` ("none" | "sm" | "md" | "lg") or `gradient` (`{ from, to, angle }`), or pass `style={props.style}` when your widget declares the `cardBackground` / `cardBorderColor` / `cardRadius` / `cardPadding` / `cardGradient` / `shadow` / `margin` style fields, so an author can restyle it. A card that brings no `baseStyle` also takes the box\'s LAYOUT from `style` — width, height, the flex properties, `alignSelf`, `gap`, position — so `style={{ width: \'100%\', maxWidth: 480, alignSelf: \'center\' }}` sizes the card and still keeps the app\'s card look; put anything else about the container on `containerStyle`. `style` may be an object or a React Native style array (`style={[{ flex: 1, minWidth: 0 }, align && { alignItems: align }]}`), read as the host flattens it: nested arrays and falsy entries are fine and a later entry wins. A gradient paints on both hosts. `as` renders the card as another container (e.g. `Pressable`, `ScrollView`) and every other prop lands on it. Never hand-roll a card from `View` + border + radius.',
|
|
3248
|
+
'Themed card surface. `<Card>…</Card>` — THE way to draw a card, panel or tile: with no props it takes the app\'s card look (Theme Settings > Cards: background, border, corner radius, padding, shadow, gradient) so it matches every built-in card. Override one card with `background`, `borderColor`, `radius`, `padding` (a number or `{ top, right, bottom, left }`), `shadow` ("none" | "sm" | "md" | "lg") or `gradient` (`{ from, to, angle }`), or pass `style={props.style}` when your widget declares the `cardBackground` / `cardBorderColor` / `cardRadius` / `cardPadding` / `cardGradient` / `shadow` / `margin` style fields, so an author can restyle it. A card that brings no `baseStyle` also takes the box\'s LAYOUT from `style` — width, height, the flex properties, `alignSelf`, `gap`, position — so `style={{ width: \'100%\', maxWidth: 480, alignSelf: \'center\' }}` sizes the card and still keeps the app\'s card look; put anything else about the container on `containerStyle`. `style` may be an object or a React Native style array (`style={[{ flex: 1, minWidth: 0 }, align && { alignItems: align }]}`), read as the host flattens it: nested arrays and falsy entries are fine and a later entry wins. A gradient paints on both hosts. `image` (a URL or `{ uri }`) makes it a PHOTO card: the picture fills the card edge to edge BEHIND the content, under a light gradient on the edge named by `scrim` ("bottom" default | "top" | "left" | "right" | "full" | "none") — the edge the text sits on — and cropped around `imagePosition` ("center" default | "top" | "bottom" | "left" | "right"). A photo card has no picture-shaped content to size it, so give it an `aspectRatio` or `minHeight` in `style`, and pass the children as a function — `{({ colors, onImage }) => …}` — to receive the theme colours as they read where the content sits (`colors.onSurface` for a title, `colors.onSurfaceMuted` for a supporting line): light over a picture, the theme\'s own on a card without one. `image={null}` renders an ordinary card, so a record with no photo needs no branch. A photo card is a `View` or, with `as`, a `Pressable`; put a scroller inside it, never as it. Use it whenever an item has a photograph, instead of a small `Image` with its text in a box underneath. `as` renders the card as another container (e.g. `Pressable`, `ScrollView`) and every other prop lands on it. Never hand-roll a card from `View` + border + radius.',
|
|
3246
3249
|
rnComponent: null,
|
|
3247
3250
|
docsUrl: null,
|
|
3248
3251
|
},
|
|
@@ -5864,7 +5867,14 @@ const CONTRACT = deepFreeze({
|
|
|
5864
5867
|
// and attach to it in one handler. A FILE column's file goes in its
|
|
5865
5868
|
// row's record space; useCamera / useImageEditor point at
|
|
5866
5869
|
// useFilestoreUpload, since widget code cannot reach ctx.assets.
|
|
5867
|
-
|
|
5870
|
+
// 1.177.1: fix (sc-10410) — a `refresh()` or `verify()` that succeeds
|
|
5871
|
+
// clears `useFileSignature`'s `error`; it used to keep one failed poll's
|
|
5872
|
+
// error for the rest of the order.
|
|
5873
|
+
// 1.178.0: additive (sc-11272) — `<Card image>` paints a picture edge to
|
|
5874
|
+
// edge behind the card's content, with a `scrim` on the text's edge and
|
|
5875
|
+
// an `imagePosition` for the crop. Children may be a function taking
|
|
5876
|
+
// `{ colors, onImage }`: the theme colours as they read over the picture.
|
|
5877
|
+
version: "1.178.0",
|
|
5868
5878
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
5869
5879
|
hooks: HOOKS,
|
|
5870
5880
|
primitives: PRIMITIVES,
|