@colixsystems/widget-sdk 0.266.0 → 0.267.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 +12 -6
- package/dist/contract.js +12 -6
- package/dist/hooks.js +3 -3
- package/dist/index.d.ts +13 -0
- package/dist/property-schema.js +3 -0
- package/dist/web-modal.js +28 -0
- package/package.json +1 -1
- package/src/contract.js +12 -6
package/README.md
CHANGED
|
@@ -86,7 +86,15 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
86
86
|
|
|
87
87
|
## Status
|
|
88
88
|
|
|
89
|
-
`v0.
|
|
89
|
+
`v0.267.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**.
|
|
90
|
+
|
|
91
|
+
### What's new in 0.267.0 (contract 1.217.0)
|
|
92
|
+
|
|
93
|
+
**Every text field can raise the right keyboard.** The `TextInput` primitive (and `<TextField>`, which passes every `TextInput` prop through) now documents `keyboardType` — `"email-address"`, `"phone-pad"`, `"url"`, `"number-pad"`, `"decimal-pad"` — plus `autoCapitalize`, `autoCorrect`, `autoComplete` and `secureTextEntry`. They already worked on both hosts (react-native-web turns `keyboardType` into the input's `type` / `inputMode`); now the contract and the AI widget agent say to set them. A `useDatastoreSchema` column's `text_format` (`"EMAIL"` | `"PHONE"` | `"URL"` | a JSON format | `null`) is typed on `DatastoreSchemaColumn`, so a typed widget can pick the keyboard from it.
|
|
94
|
+
|
|
95
|
+
### What's new in 0.266.1 (contract 1.216.0)
|
|
96
|
+
|
|
97
|
+
**An open `<Modal>` or `<Overlay>` holds the page still on the web.** Scrolling past the end of a dialog's body, or over a dialog with nothing to scroll, used to scroll the page behind it. The page now stays put until the dialog closes, as it already did in the phone app. Nothing changes for a widget. An anchored `<Menu>`, `<Popover>` or `<Tooltip>` still closes when the page scrolls under it.
|
|
90
98
|
|
|
91
99
|
### What's new in 0.266.0 (contract 1.216.0)
|
|
92
100
|
|
package/dist/contract.cjs
CHANGED
|
@@ -2028,13 +2028,16 @@ const HOOKS = [
|
|
|
2028
2028
|
"widget can resolve a stored columnId to the column's name / dataType / " +
|
|
2029
2029
|
"relation target at runtime (e.g. a form that renders inputs by column " +
|
|
2030
2030
|
"type). Returns { schema, loading, error, refetch } where schema is " +
|
|
2031
|
-
"{ id, name, columns: [{ id, name,
|
|
2032
|
-
"
|
|
2031
|
+
"{ id, name, columns: [{ id, name, data_type, required, relation_type, " +
|
|
2032
|
+
"target_table_id, is_identification, text_format }] } (null until " +
|
|
2033
|
+
"loaded; snake_case as the server sends it). `text_format` is " +
|
|
2034
|
+
"\"EMAIL\" | \"PHONE\" | \"URL\" (or a JSON format, or null) and names the " +
|
|
2035
|
+
"keyboard a field for that column should raise. Reads only the " +
|
|
2033
2036
|
"structure, never row data; gated by the same datastore.read ACL as " +
|
|
2034
2037
|
"record reads, so a public-grant table resolves for anonymous visitors.",
|
|
2035
2038
|
returnShape: {
|
|
2036
2039
|
schema:
|
|
2037
|
-
"{ id, name, columns: [{ id, name,
|
|
2040
|
+
"{ id, name, columns: [{ id, name, data_type, required, relation_type, target_table_id, is_identification, text_format }] } | null",
|
|
2038
2041
|
loading: "boolean",
|
|
2039
2042
|
error: "DatastoreError | null",
|
|
2040
2043
|
refetch: "() => Promise<void>",
|
|
@@ -3248,7 +3251,7 @@ const PRIMITIVES = [
|
|
|
3248
3251
|
{
|
|
3249
3252
|
name: "TextInput",
|
|
3250
3253
|
description:
|
|
3251
|
-
"Single-line or multi-line text field. Controlled — pass `value` + `onChangeText`. Set `multiline` for a textarea-style field (`numberOfLines` controls visible rows). See https://reactnative.dev/docs/textinput.",
|
|
3254
|
+
"Single-line or multi-line text field. Controlled — pass `value` + `onChangeText`. Set `multiline` for a textarea-style field (`numberOfLines` controls visible rows). Pick the on-screen keyboard from what the field holds — the default alphabetic keyboard is wrong for most structured values: `keyboardType=\"email-address\"` for an email, `\"phone-pad\"` for a phone number, `\"url\"` for a link, `\"number-pad\"` for whole digits (a card or code), `\"decimal-pad\"` for an amount. For an email, link, username or code also pass `autoCapitalize=\"none\"` and `autoCorrect={false}`, so the keyboard never rewrites the value; `autoComplete` (\"email\", \"tel\", \"name\", \"one-time-code\") lets the device offer a saved value, and `secureTextEntry` masks a password. The same props reach the web: react-native-web turns `keyboardType` into the input's `type` / `inputMode`. A column's declared format (`text_format` on a `useDatastoreSchema` column: \"EMAIL\" | \"PHONE\" | \"URL\") names the keyboard for a field bound to it. See https://reactnative.dev/docs/textinput.",
|
|
3252
3255
|
rnComponent: "TextInput",
|
|
3253
3256
|
docsUrl: "https://reactnative.dev/docs/textinput",
|
|
3254
3257
|
},
|
|
@@ -3607,7 +3610,7 @@ const PRIMITIVES = [
|
|
|
3607
3610
|
{
|
|
3608
3611
|
name: "TextField",
|
|
3609
3612
|
description:
|
|
3610
|
-
'Themed text-entry field. `<TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />` — every `TextInput` prop works, and its background, text colour, border, corner radius and height come from the app\'s input look (Design > Components > Inputs), so a widget\'s fields match the built-in forms. `multiline` grows the field to a ceiling and then scrolls inside it. Override one field with `background`, `textColor`, `borderColor`, `radius` or `height`; `style` is merged last. `label` draws a label above the field and names it for a screen reader, `helper` a hint below, `error` a message in the helper\'s place with a danger border (`true` marks it without a message), `disabled` makes it read-only and dimmed; `containerStyle` styles the box around a labelled field, or around the field and its icons. An icon goes INSIDE the field, never beside it: `leadingIcon` / `trailingIcon` take a lucide name (`<TextField trailingIcon="map-pin" … />`), and `onTrailingPress` with a `trailingAccessibilityLabel` makes the trailing one a button (a clear control, "use my location"). Never wrap a field in a bordered row of your own to put an icon or a button beside it: the field draws its own border, so that shows a box inside a box. Use it by default instead of styling a bare `TextInput`; a field it cannot draw (a one-time code split into digit boxes, an amount with a stepper inside its border) is your own build that keeps a label, the input colours of the theme and an error message.',
|
|
3613
|
+
'Themed text-entry field. `<TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />` — every `TextInput` prop works (set `keyboardType` from what the field holds: `"email-address"`, `"phone-pad"`, `"url"`, `"number-pad"`, `"decimal-pad"`), and its background, text colour, border, corner radius and height come from the app\'s input look (Design > Components > Inputs), so a widget\'s fields match the built-in forms. `multiline` grows the field to a ceiling and then scrolls inside it. Override one field with `background`, `textColor`, `borderColor`, `radius` or `height`; `style` is merged last. `label` draws a label above the field and names it for a screen reader, `helper` a hint below, `error` a message in the helper\'s place with a danger border (`true` marks it without a message), `disabled` makes it read-only and dimmed; `containerStyle` styles the box around a labelled field, or around the field and its icons. An icon goes INSIDE the field, never beside it: `leadingIcon` / `trailingIcon` take a lucide name (`<TextField trailingIcon="map-pin" … />`), and `onTrailingPress` with a `trailingAccessibilityLabel` makes the trailing one a button (a clear control, "use my location"). Never wrap a field in a bordered row of your own to put an icon or a button beside it: the field draws its own border, so that shows a box inside a box. Use it by default instead of styling a bare `TextInput`; a field it cannot draw (a one-time code split into digit boxes, an amount with a stepper inside its border) is your own build that keeps a label, the input colours of the theme and an error message.',
|
|
3611
3614
|
rnComponent: null,
|
|
3612
3615
|
docsUrl: null,
|
|
3613
3616
|
},
|
|
@@ -6466,7 +6469,10 @@ const CONTRACT = deepFreeze({
|
|
|
6466
6469
|
// `useUsers().invite` takes `send_invitation` (false stores the invite
|
|
6467
6470
|
// unsent), invite rows carry `sent_at`, `not_sent` is a status, and
|
|
6468
6471
|
// `useInvites().resend` performs the first send.
|
|
6469
|
-
|
|
6472
|
+
// 1.217.0: additive (sc-4747) — `TextInput` documents `keyboardType`,
|
|
6473
|
+
// `autoCapitalize`, `autoCorrect`, `autoComplete`, `secureTextEntry`;
|
|
6474
|
+
// `useDatastoreSchema` columns document `text_format` (snake_case wire).
|
|
6475
|
+
version: "1.217.0",
|
|
6470
6476
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6471
6477
|
hooks: HOOKS,
|
|
6472
6478
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -2025,13 +2025,16 @@ const HOOKS = [
|
|
|
2025
2025
|
"widget can resolve a stored columnId to the column's name / dataType / " +
|
|
2026
2026
|
"relation target at runtime (e.g. a form that renders inputs by column " +
|
|
2027
2027
|
"type). Returns { schema, loading, error, refetch } where schema is " +
|
|
2028
|
-
"{ id, name, columns: [{ id, name,
|
|
2029
|
-
"
|
|
2028
|
+
"{ id, name, columns: [{ id, name, data_type, required, relation_type, " +
|
|
2029
|
+
"target_table_id, is_identification, text_format }] } (null until " +
|
|
2030
|
+
"loaded; snake_case as the server sends it). `text_format` is " +
|
|
2031
|
+
"\"EMAIL\" | \"PHONE\" | \"URL\" (or a JSON format, or null) and names the " +
|
|
2032
|
+
"keyboard a field for that column should raise. Reads only the " +
|
|
2030
2033
|
"structure, never row data; gated by the same datastore.read ACL as " +
|
|
2031
2034
|
"record reads, so a public-grant table resolves for anonymous visitors.",
|
|
2032
2035
|
returnShape: {
|
|
2033
2036
|
schema:
|
|
2034
|
-
"{ id, name, columns: [{ id, name,
|
|
2037
|
+
"{ id, name, columns: [{ id, name, data_type, required, relation_type, target_table_id, is_identification, text_format }] } | null",
|
|
2035
2038
|
loading: "boolean",
|
|
2036
2039
|
error: "DatastoreError | null",
|
|
2037
2040
|
refetch: "() => Promise<void>",
|
|
@@ -3245,7 +3248,7 @@ const PRIMITIVES = [
|
|
|
3245
3248
|
{
|
|
3246
3249
|
name: "TextInput",
|
|
3247
3250
|
description:
|
|
3248
|
-
"Single-line or multi-line text field. Controlled — pass `value` + `onChangeText`. Set `multiline` for a textarea-style field (`numberOfLines` controls visible rows). See https://reactnative.dev/docs/textinput.",
|
|
3251
|
+
"Single-line or multi-line text field. Controlled — pass `value` + `onChangeText`. Set `multiline` for a textarea-style field (`numberOfLines` controls visible rows). Pick the on-screen keyboard from what the field holds — the default alphabetic keyboard is wrong for most structured values: `keyboardType=\"email-address\"` for an email, `\"phone-pad\"` for a phone number, `\"url\"` for a link, `\"number-pad\"` for whole digits (a card or code), `\"decimal-pad\"` for an amount. For an email, link, username or code also pass `autoCapitalize=\"none\"` and `autoCorrect={false}`, so the keyboard never rewrites the value; `autoComplete` (\"email\", \"tel\", \"name\", \"one-time-code\") lets the device offer a saved value, and `secureTextEntry` masks a password. The same props reach the web: react-native-web turns `keyboardType` into the input's `type` / `inputMode`. A column's declared format (`text_format` on a `useDatastoreSchema` column: \"EMAIL\" | \"PHONE\" | \"URL\") names the keyboard for a field bound to it. See https://reactnative.dev/docs/textinput.",
|
|
3249
3252
|
rnComponent: "TextInput",
|
|
3250
3253
|
docsUrl: "https://reactnative.dev/docs/textinput",
|
|
3251
3254
|
},
|
|
@@ -3604,7 +3607,7 @@ const PRIMITIVES = [
|
|
|
3604
3607
|
{
|
|
3605
3608
|
name: "TextField",
|
|
3606
3609
|
description:
|
|
3607
|
-
'Themed text-entry field. `<TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />` — every `TextInput` prop works, and its background, text colour, border, corner radius and height come from the app\'s input look (Design > Components > Inputs), so a widget\'s fields match the built-in forms. `multiline` grows the field to a ceiling and then scrolls inside it. Override one field with `background`, `textColor`, `borderColor`, `radius` or `height`; `style` is merged last. `label` draws a label above the field and names it for a screen reader, `helper` a hint below, `error` a message in the helper\'s place with a danger border (`true` marks it without a message), `disabled` makes it read-only and dimmed; `containerStyle` styles the box around a labelled field, or around the field and its icons. An icon goes INSIDE the field, never beside it: `leadingIcon` / `trailingIcon` take a lucide name (`<TextField trailingIcon="map-pin" … />`), and `onTrailingPress` with a `trailingAccessibilityLabel` makes the trailing one a button (a clear control, "use my location"). Never wrap a field in a bordered row of your own to put an icon or a button beside it: the field draws its own border, so that shows a box inside a box. Use it by default instead of styling a bare `TextInput`; a field it cannot draw (a one-time code split into digit boxes, an amount with a stepper inside its border) is your own build that keeps a label, the input colours of the theme and an error message.',
|
|
3610
|
+
'Themed text-entry field. `<TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />` — every `TextInput` prop works (set `keyboardType` from what the field holds: `"email-address"`, `"phone-pad"`, `"url"`, `"number-pad"`, `"decimal-pad"`), and its background, text colour, border, corner radius and height come from the app\'s input look (Design > Components > Inputs), so a widget\'s fields match the built-in forms. `multiline` grows the field to a ceiling and then scrolls inside it. Override one field with `background`, `textColor`, `borderColor`, `radius` or `height`; `style` is merged last. `label` draws a label above the field and names it for a screen reader, `helper` a hint below, `error` a message in the helper\'s place with a danger border (`true` marks it without a message), `disabled` makes it read-only and dimmed; `containerStyle` styles the box around a labelled field, or around the field and its icons. An icon goes INSIDE the field, never beside it: `leadingIcon` / `trailingIcon` take a lucide name (`<TextField trailingIcon="map-pin" … />`), and `onTrailingPress` with a `trailingAccessibilityLabel` makes the trailing one a button (a clear control, "use my location"). Never wrap a field in a bordered row of your own to put an icon or a button beside it: the field draws its own border, so that shows a box inside a box. Use it by default instead of styling a bare `TextInput`; a field it cannot draw (a one-time code split into digit boxes, an amount with a stepper inside its border) is your own build that keeps a label, the input colours of the theme and an error message.',
|
|
3608
3611
|
rnComponent: null,
|
|
3609
3612
|
docsUrl: null,
|
|
3610
3613
|
},
|
|
@@ -6463,7 +6466,10 @@ const CONTRACT = deepFreeze({
|
|
|
6463
6466
|
// `useUsers().invite` takes `send_invitation` (false stores the invite
|
|
6464
6467
|
// unsent), invite rows carry `sent_at`, `not_sent` is a status, and
|
|
6465
6468
|
// `useInvites().resend` performs the first send.
|
|
6466
|
-
|
|
6469
|
+
// 1.217.0: additive (sc-4747) — `TextInput` documents `keyboardType`,
|
|
6470
|
+
// `autoCapitalize`, `autoCorrect`, `autoComplete`, `secureTextEntry`;
|
|
6471
|
+
// `useDatastoreSchema` columns document `text_format` (snake_case wire).
|
|
6472
|
+
version: "1.217.0",
|
|
6467
6473
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6468
6474
|
hooks: HOOKS,
|
|
6469
6475
|
primitives: PRIMITIVES,
|
package/dist/hooks.js
CHANGED
|
@@ -3165,9 +3165,9 @@ export function useDatastoreRecord(table, recordId, options) {
|
|
|
3165
3165
|
|
|
3166
3166
|
/**
|
|
3167
3167
|
* Stateful table-schema resolver hook. Returns { schema, loading, error,
|
|
3168
|
-
* refetch } where `schema` is `{ id, name, columns: [{ id, name,
|
|
3169
|
-
* required,
|
|
3170
|
-
* loaded).
|
|
3168
|
+
* refetch } where `schema` is `{ id, name, columns: [{ id, name, data_type,
|
|
3169
|
+
* required, relation_type, target_table_id, is_identification, text_format }] }`
|
|
3170
|
+
* (`null` until loaded) — the server's snake_case, verbatim.
|
|
3171
3171
|
*
|
|
3172
3172
|
* The host's datastore client exposes `schema(table)` which resolves to the
|
|
3173
3173
|
* table's structural metadata — columns and their types, NOT row data. Use it
|
package/dist/index.d.ts
CHANGED
|
@@ -1667,6 +1667,19 @@ export interface DatastoreSchemaColumn {
|
|
|
1667
1667
|
copy_via_column_id?: string | null;
|
|
1668
1668
|
/** Set on a copy the server keeps: the related table's column it copies. */
|
|
1669
1669
|
copy_of_column_id?: string | null;
|
|
1670
|
+
/**
|
|
1671
|
+
* STRING / TEXT columns only — the value's declared format. EMAIL, PHONE and
|
|
1672
|
+
* URL name the keyboard a field for this column should raise
|
|
1673
|
+
* (`keyboardType` "email-address" / "phone-pad" / "url").
|
|
1674
|
+
*/
|
|
1675
|
+
text_format?:
|
|
1676
|
+
| "EMAIL"
|
|
1677
|
+
| "PHONE"
|
|
1678
|
+
| "URL"
|
|
1679
|
+
| "JSON_ARRAY"
|
|
1680
|
+
| "JSON_OBJECT"
|
|
1681
|
+
| "JSON_VALUE"
|
|
1682
|
+
| null;
|
|
1670
1683
|
}
|
|
1671
1684
|
|
|
1672
1685
|
export interface DatastoreSchema {
|
package/dist/property-schema.js
CHANGED
|
@@ -82,6 +82,9 @@ const VALID_TYPES = new Set([
|
|
|
82
82
|
"object", "array",
|
|
83
83
|
]);
|
|
84
84
|
|
|
85
|
+
/** The propertySchema field types, for documentation generated from this list. */
|
|
86
|
+
export const PROPERTY_TYPES = Object.freeze([...VALID_TYPES]);
|
|
87
|
+
|
|
85
88
|
function isPlainObject(v) {
|
|
86
89
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
87
90
|
}
|
package/dist/web-modal.js
CHANGED
|
@@ -59,6 +59,32 @@ function useCloseOnOutsideScroll(container, enabled, onCloseRef) {
|
|
|
59
59
|
}, [container, enabled, onCloseRef]);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// A dialog owns the screen, as on native: the page beneath must not
|
|
63
|
+
// scroll, even once the dialog's own body runs out of scroll.
|
|
64
|
+
let pageScrollLocks = 0;
|
|
65
|
+
let unlockedRootStyle = null;
|
|
66
|
+
|
|
67
|
+
function usePageScrollLock(enabled) {
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
if (!enabled) return undefined;
|
|
70
|
+
const root = document.documentElement;
|
|
71
|
+
if (pageScrollLocks++ === 0) {
|
|
72
|
+
unlockedRootStyle = {
|
|
73
|
+
overflow: root.style.overflow,
|
|
74
|
+
scrollbarGutter: root.style.scrollbarGutter,
|
|
75
|
+
};
|
|
76
|
+
// Keep a visible scrollbar's space, so the page does not shift sideways.
|
|
77
|
+
if (window.innerWidth > root.clientWidth) root.style.scrollbarGutter = "stable";
|
|
78
|
+
root.style.overflow = "hidden";
|
|
79
|
+
}
|
|
80
|
+
return () => {
|
|
81
|
+
if (--pageScrollLocks > 0) return;
|
|
82
|
+
root.style.overflow = unlockedRootStyle.overflow;
|
|
83
|
+
root.style.scrollbarGutter = unlockedRootStyle.scrollbarGutter;
|
|
84
|
+
};
|
|
85
|
+
}, [enabled]);
|
|
86
|
+
}
|
|
87
|
+
|
|
62
88
|
function tabStops(dialog) {
|
|
63
89
|
return Array.from(dialog.querySelectorAll(FOCUSABLE)).filter(
|
|
64
90
|
(node) => node.getAttribute("aria-hidden") !== "true",
|
|
@@ -147,6 +173,8 @@ function ModalLayer({
|
|
|
147
173
|
}, [visible]);
|
|
148
174
|
const container = useBodyContainer(visible);
|
|
149
175
|
useCloseOnOutsideScroll(container, closesOnOutsideScroll, onCloseRef);
|
|
176
|
+
// An anchored panel closes on a scroll beneath instead.
|
|
177
|
+
usePageScrollLock(Boolean(container) && !closesOnOutsideScroll);
|
|
150
178
|
|
|
151
179
|
React.useEffect(() => {
|
|
152
180
|
const dialog = dialogRef.current;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.267.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
|
@@ -2025,13 +2025,16 @@ const HOOKS = [
|
|
|
2025
2025
|
"widget can resolve a stored columnId to the column's name / dataType / " +
|
|
2026
2026
|
"relation target at runtime (e.g. a form that renders inputs by column " +
|
|
2027
2027
|
"type). Returns { schema, loading, error, refetch } where schema is " +
|
|
2028
|
-
"{ id, name, columns: [{ id, name,
|
|
2029
|
-
"
|
|
2028
|
+
"{ id, name, columns: [{ id, name, data_type, required, relation_type, " +
|
|
2029
|
+
"target_table_id, is_identification, text_format }] } (null until " +
|
|
2030
|
+
"loaded; snake_case as the server sends it). `text_format` is " +
|
|
2031
|
+
"\"EMAIL\" | \"PHONE\" | \"URL\" (or a JSON format, or null) and names the " +
|
|
2032
|
+
"keyboard a field for that column should raise. Reads only the " +
|
|
2030
2033
|
"structure, never row data; gated by the same datastore.read ACL as " +
|
|
2031
2034
|
"record reads, so a public-grant table resolves for anonymous visitors.",
|
|
2032
2035
|
returnShape: {
|
|
2033
2036
|
schema:
|
|
2034
|
-
"{ id, name, columns: [{ id, name,
|
|
2037
|
+
"{ id, name, columns: [{ id, name, data_type, required, relation_type, target_table_id, is_identification, text_format }] } | null",
|
|
2035
2038
|
loading: "boolean",
|
|
2036
2039
|
error: "DatastoreError | null",
|
|
2037
2040
|
refetch: "() => Promise<void>",
|
|
@@ -3245,7 +3248,7 @@ const PRIMITIVES = [
|
|
|
3245
3248
|
{
|
|
3246
3249
|
name: "TextInput",
|
|
3247
3250
|
description:
|
|
3248
|
-
"Single-line or multi-line text field. Controlled — pass `value` + `onChangeText`. Set `multiline` for a textarea-style field (`numberOfLines` controls visible rows). See https://reactnative.dev/docs/textinput.",
|
|
3251
|
+
"Single-line or multi-line text field. Controlled — pass `value` + `onChangeText`. Set `multiline` for a textarea-style field (`numberOfLines` controls visible rows). Pick the on-screen keyboard from what the field holds — the default alphabetic keyboard is wrong for most structured values: `keyboardType=\"email-address\"` for an email, `\"phone-pad\"` for a phone number, `\"url\"` for a link, `\"number-pad\"` for whole digits (a card or code), `\"decimal-pad\"` for an amount. For an email, link, username or code also pass `autoCapitalize=\"none\"` and `autoCorrect={false}`, so the keyboard never rewrites the value; `autoComplete` (\"email\", \"tel\", \"name\", \"one-time-code\") lets the device offer a saved value, and `secureTextEntry` masks a password. The same props reach the web: react-native-web turns `keyboardType` into the input's `type` / `inputMode`. A column's declared format (`text_format` on a `useDatastoreSchema` column: \"EMAIL\" | \"PHONE\" | \"URL\") names the keyboard for a field bound to it. See https://reactnative.dev/docs/textinput.",
|
|
3249
3252
|
rnComponent: "TextInput",
|
|
3250
3253
|
docsUrl: "https://reactnative.dev/docs/textinput",
|
|
3251
3254
|
},
|
|
@@ -3604,7 +3607,7 @@ const PRIMITIVES = [
|
|
|
3604
3607
|
{
|
|
3605
3608
|
name: "TextField",
|
|
3606
3609
|
description:
|
|
3607
|
-
'Themed text-entry field. `<TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />` — every `TextInput` prop works, and its background, text colour, border, corner radius and height come from the app\'s input look (Design > Components > Inputs), so a widget\'s fields match the built-in forms. `multiline` grows the field to a ceiling and then scrolls inside it. Override one field with `background`, `textColor`, `borderColor`, `radius` or `height`; `style` is merged last. `label` draws a label above the field and names it for a screen reader, `helper` a hint below, `error` a message in the helper\'s place with a danger border (`true` marks it without a message), `disabled` makes it read-only and dimmed; `containerStyle` styles the box around a labelled field, or around the field and its icons. An icon goes INSIDE the field, never beside it: `leadingIcon` / `trailingIcon` take a lucide name (`<TextField trailingIcon="map-pin" … />`), and `onTrailingPress` with a `trailingAccessibilityLabel` makes the trailing one a button (a clear control, "use my location"). Never wrap a field in a bordered row of your own to put an icon or a button beside it: the field draws its own border, so that shows a box inside a box. Use it by default instead of styling a bare `TextInput`; a field it cannot draw (a one-time code split into digit boxes, an amount with a stepper inside its border) is your own build that keeps a label, the input colours of the theme and an error message.',
|
|
3610
|
+
'Themed text-entry field. `<TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />` — every `TextInput` prop works (set `keyboardType` from what the field holds: `"email-address"`, `"phone-pad"`, `"url"`, `"number-pad"`, `"decimal-pad"`), and its background, text colour, border, corner radius and height come from the app\'s input look (Design > Components > Inputs), so a widget\'s fields match the built-in forms. `multiline` grows the field to a ceiling and then scrolls inside it. Override one field with `background`, `textColor`, `borderColor`, `radius` or `height`; `style` is merged last. `label` draws a label above the field and names it for a screen reader, `helper` a hint below, `error` a message in the helper\'s place with a danger border (`true` marks it without a message), `disabled` makes it read-only and dimmed; `containerStyle` styles the box around a labelled field, or around the field and its icons. An icon goes INSIDE the field, never beside it: `leadingIcon` / `trailingIcon` take a lucide name (`<TextField trailingIcon="map-pin" … />`), and `onTrailingPress` with a `trailingAccessibilityLabel` makes the trailing one a button (a clear control, "use my location"). Never wrap a field in a bordered row of your own to put an icon or a button beside it: the field draws its own border, so that shows a box inside a box. Use it by default instead of styling a bare `TextInput`; a field it cannot draw (a one-time code split into digit boxes, an amount with a stepper inside its border) is your own build that keeps a label, the input colours of the theme and an error message.',
|
|
3608
3611
|
rnComponent: null,
|
|
3609
3612
|
docsUrl: null,
|
|
3610
3613
|
},
|
|
@@ -6463,7 +6466,10 @@ const CONTRACT = deepFreeze({
|
|
|
6463
6466
|
// `useUsers().invite` takes `send_invitation` (false stores the invite
|
|
6464
6467
|
// unsent), invite rows carry `sent_at`, `not_sent` is a status, and
|
|
6465
6468
|
// `useInvites().resend` performs the first send.
|
|
6466
|
-
|
|
6469
|
+
// 1.217.0: additive (sc-4747) — `TextInput` documents `keyboardType`,
|
|
6470
|
+
// `autoCapitalize`, `autoCorrect`, `autoComplete`, `secureTextEntry`;
|
|
6471
|
+
// `useDatastoreSchema` columns document `text_format` (snake_case wire).
|
|
6472
|
+
version: "1.217.0",
|
|
6467
6473
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
6468
6474
|
hooks: HOOKS,
|
|
6469
6475
|
primitives: PRIMITIVES,
|