@kerfjs/ui 5.0.0-beta.109 → 5.0.0-beta.110
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/ai/component-catalog.json +5 -3
- package/ai/components/text-field.md +4 -2
- package/ai/public-api-signatures-v1.md +6 -4
- package/ai/skill.md +4 -0
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/{chunk-YVZCKXR3.js → chunk-36HVQ72F.js} +3 -1
- package/dist/chunk-36HVQ72F.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/styles/button.css +10 -2
- package/dist/styles/components/actions/button/button.css +10 -2
- package/dist/text-field.d.ts +5 -3
- package/dist/text-field.js +1 -1
- package/docs/accessibility.md +16 -0
- package/llms.txt +7 -3
- package/package.json +1 -1
- package/dist/chunk-YVZCKXR3.js.map +0 -1
|
@@ -6716,7 +6716,7 @@
|
|
|
6716
6716
|
"category": "Controls",
|
|
6717
6717
|
"kind": "component",
|
|
6718
6718
|
"source": "kerf",
|
|
6719
|
-
"description": "A labeled single-line input or multi-line textarea with hint and error text, an optional leading icon, clear button, character count, and native numeric constraints, at the 44px control height or compact.",
|
|
6719
|
+
"description": "A labeled single-line input (including native date and number types) or multi-line textarea with hint and error text, an optional leading icon, clear button, character count, and native numeric constraints, at the 44px control height or compact.",
|
|
6720
6720
|
"uses": [
|
|
6721
6721
|
"lucide-icon"
|
|
6722
6722
|
],
|
|
@@ -6729,7 +6729,8 @@
|
|
|
6729
6729
|
"A form or dialog collects a line of free text: label names it (or ariaLabel when nearby content already does), hint explains it, and error reports a validation problem.",
|
|
6730
6730
|
"People write a longer note or message: multiline with rows, or rows plus maxRows to grow with the content; maxLength with showCount shows the remaining budget.",
|
|
6731
6731
|
"A filter or search field needs a leading icon and a clear button: icon and clearable.",
|
|
6732
|
-
"An exact number needs native range or decimal-step validation: type=number with min, max, and step (including step=any)."
|
|
6732
|
+
"An exact number needs native range or decimal-step validation: type=number with min, max, and step (including step=any).",
|
|
6733
|
+
"A form needs a calendar date with the browser's native date picker: type=date."
|
|
6733
6734
|
],
|
|
6734
6735
|
"avoidWhen": [
|
|
6735
6736
|
"Do not use when only existing values are allowed; use Select.",
|
|
@@ -6775,7 +6776,8 @@
|
|
|
6775
6776
|
"disabled",
|
|
6776
6777
|
"read-only",
|
|
6777
6778
|
"required",
|
|
6778
|
-
"
|
|
6779
|
+
"native autofocus (input or textarea)",
|
|
6780
|
+
"email, password, search, url, tel, number, or date type",
|
|
6779
6781
|
"number type with native min, max, and step constraints"
|
|
6780
6782
|
],
|
|
6781
6783
|
"accessibility": [
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
`@kerfjs/ui:text-field` · Kerf UI · component · Controls
|
|
6
6
|
|
|
7
|
-
A labeled single-line input or multi-line textarea with hint and error text, an optional leading icon, clear button, character count, and native numeric constraints, at the 44px control height or compact.
|
|
7
|
+
A labeled single-line input (including native date and number types) or multi-line textarea with hint and error text, an optional leading icon, clear button, character count, and native numeric constraints, at the 44px control height or compact.
|
|
8
8
|
|
|
9
9
|
## When to use
|
|
10
10
|
|
|
@@ -14,6 +14,7 @@ Collect free text (names, addresses, notes, comments) with Kerf's field chrome,
|
|
|
14
14
|
- People write a longer note or message: multiline with rows, or rows plus maxRows to grow with the content; maxLength with showCount shows the remaining budget.
|
|
15
15
|
- A filter or search field needs a leading icon and a clear button: icon and clearable.
|
|
16
16
|
- An exact number needs native range or decimal-step validation: type=number with min, max, and step (including step=any).
|
|
17
|
+
- A form needs a calendar date with the browser's native date picker: type=date.
|
|
17
18
|
|
|
18
19
|
**Not when:**
|
|
19
20
|
|
|
@@ -53,7 +54,8 @@ Exact prop names and types: [`@kerfjs/ui/text-field`](../public-api-signatures-v
|
|
|
53
54
|
- disabled
|
|
54
55
|
- read-only
|
|
55
56
|
- required
|
|
56
|
-
-
|
|
57
|
+
- native autofocus (input or textarea)
|
|
58
|
+
- email, password, search, url, tel, number, or date type
|
|
57
59
|
- number type with native min, max, and step constraints
|
|
58
60
|
|
|
59
61
|
## Composition
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Public API signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.110` and `kerfjs@5.0.0-beta.110`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
|
|
4
4
|
|
|
5
5
|
## `@kerfjs/ui/css-values`
|
|
6
6
|
|
|
@@ -6125,7 +6125,7 @@ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
|
6125
6125
|
/** `default` is the 44px control height; `compact` is 33px with smaller text. */
|
|
6126
6126
|
type TextFieldSize = 'default' | 'compact';
|
|
6127
6127
|
/** The kinds of single-line value a TextField takes. */
|
|
6128
|
-
type TextFieldType = 'text' | 'email' | 'password' | 'search' | 'url' | 'tel' | 'number';
|
|
6128
|
+
type TextFieldType = 'text' | 'email' | 'password' | 'search' | 'url' | 'tel' | 'number' | 'date';
|
|
6129
6129
|
/** Application `data-*` metadata; the TextField's own markers stay protected. */
|
|
6130
6130
|
type TextFieldRootAttributes = Readonly<Record<`data-${string}`, string | undefined>>;
|
|
6131
6131
|
/**
|
|
@@ -6169,7 +6169,7 @@ interface TextFieldOptions {
|
|
|
6169
6169
|
* `wireTextFields(root)`, which updates it as people type.
|
|
6170
6170
|
*/
|
|
6171
6171
|
showCount?: boolean;
|
|
6172
|
-
/** Single-line value kind (default `text`). */
|
|
6172
|
+
/** Single-line value kind (default `text`); `date` uses the native picker. */
|
|
6173
6173
|
type?: TextFieldType;
|
|
6174
6174
|
/** Native lower bound for `type="number"` (number or numeric string). */
|
|
6175
6175
|
min?: number | string;
|
|
@@ -6189,6 +6189,8 @@ interface TextFieldOptions {
|
|
|
6189
6189
|
disabled?: boolean;
|
|
6190
6190
|
readOnly?: boolean;
|
|
6191
6191
|
required?: boolean;
|
|
6192
|
+
/** Request native focus on insertion or when the containing dialog opens. */
|
|
6193
|
+
autofocus?: boolean;
|
|
6192
6194
|
autocomplete?: string;
|
|
6193
6195
|
/** The input's id, for an external label or form. */
|
|
6194
6196
|
id?: string;
|
|
@@ -6205,7 +6207,7 @@ interface TextFieldOptions {
|
|
|
6205
6207
|
* The whole field is one `<label>`, so clicking anywhere on it focuses the
|
|
6206
6208
|
* input; the input is named by `label` (or `ariaLabel`) directly.
|
|
6207
6209
|
*/
|
|
6208
|
-
declare function TextField({ label, ariaLabel, value, name, placeholder, hint, error, size, icon, clearable, clearLabel, maxLength, showCount, type, min, max, step, multiline, rows, maxRows, disabled, readOnly, required, autocomplete, id, className, rootAttributes, slot, }: TextFieldProps): kerfjs.SafeHtml;
|
|
6210
|
+
declare function TextField({ label, ariaLabel, value, name, placeholder, hint, error, size, icon, clearable, clearLabel, maxLength, showCount, type, min, max, step, multiline, rows, maxRows, disabled, readOnly, required, autofocus, autocomplete, id, className, rootAttributes, slot, }: TextFieldProps): kerfjs.SafeHtml;
|
|
6209
6211
|
|
|
6210
6212
|
export { TextField, type TextFieldName, type TextFieldOptions, type TextFieldProps, type TextFieldRootAttributes, type TextFieldSize, type TextFieldType };
|
|
6211
6213
|
```
|
package/ai/skill.md
CHANGED
|
@@ -471,6 +471,10 @@ For a read-only field preview that replaces a Web Awesome input, use `FieldLabel
|
|
|
471
471
|
For exact numeric entry, use `TextField` with `type="number"` and pass native
|
|
472
472
|
`min`, `max`, and `step` as numbers or strings (`step="any"` permits arbitrary
|
|
473
473
|
decimals). Those attributes reach the inner input only for numeric fields.
|
|
474
|
+
For a calendar date use `type="date"` with an ISO date value. Set
|
|
475
|
+
`autofocus` when the native input or textarea should receive initial focus on
|
|
476
|
+
insertion or when its containing dialog opens; move focus explicitly when an
|
|
477
|
+
application interaction needs different timing.
|
|
474
478
|
|
|
475
479
|
Common mistakes:
|
|
476
480
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Web Awesome JSX signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from the emitted `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from the emitted `@kerfjs/ui@5.0.0-beta.110` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { KerfCustomElement } from 'kerfjs/jsx-runtime';
|
|
@@ -33,6 +33,7 @@ function TextField({
|
|
|
33
33
|
disabled = false,
|
|
34
34
|
readOnly = false,
|
|
35
35
|
required = false,
|
|
36
|
+
autofocus = false,
|
|
36
37
|
autocomplete,
|
|
37
38
|
id,
|
|
38
39
|
className = "",
|
|
@@ -50,6 +51,7 @@ function TextField({
|
|
|
50
51
|
disabled,
|
|
51
52
|
readonly: readOnly,
|
|
52
53
|
required,
|
|
54
|
+
autofocus,
|
|
53
55
|
autocomplete,
|
|
54
56
|
maxlength: maxLength,
|
|
55
57
|
// The label element also wraps the clear button and the count, so the
|
|
@@ -118,4 +120,4 @@ function TextField({
|
|
|
118
120
|
|
|
119
121
|
export { TextField };
|
|
120
122
|
|
|
121
|
-
//# sourceMappingURL=chunk-
|
|
123
|
+
//# sourceMappingURL=chunk-36HVQ72F.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/forms/text-field/text-field.tsx"],"names":[],"mappings":"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","file":"chunk-36HVQ72F.js","sourcesContent":["import { CircleX } from 'lucide';\n\nimport type { KerfUiContent } from '../../../shared/content/semantic-content.js';\nimport { filterDataAttributes } from '../../../shared/dom/extension-attributes.js';\nimport { LucideIcon } from '../../media/lucide-icon/lucide-icon.js';\n\n/** `default` is the 44px control height; `compact` is 33px with smaller text. */\nexport type TextFieldSize = 'default' | 'compact';\n\n/** The kinds of single-line value a TextField takes. */\nexport type TextFieldType =\n 'text' | 'email' | 'password' | 'search' | 'url' | 'tel' | 'number' | 'date';\n\nconst TEXT_FIELD_PROTECTED = new Set([\n 'data-component',\n 'data-size',\n 'data-invalid',\n 'data-multiline',\n]);\n\n/** Application `data-*` metadata; the TextField's own markers stay protected. */\nexport type TextFieldRootAttributes = Readonly<\n Record<`data-${string}`, string | undefined>\n>;\n\n/**\n * The field's accessible name: a visible `label`, or `ariaLabel` when the\n * surrounding content already names it.\n */\nexport type TextFieldName =\n { label: string; ariaLabel?: never } | { label?: never; ariaLabel: string };\n\nexport type TextFieldProps = TextFieldName & TextFieldOptions;\n\nexport interface TextFieldOptions {\n /** The controlled value. */\n value?: string;\n name?: string;\n placeholder?: string;\n /** Supporting text below the field, also its accessible description. */\n hint?: string;\n /**\n * A validation message: shown below the field in the danger color, added\n * to its description, and marking it `aria-invalid`.\n */\n error?: string;\n size?: TextFieldSize;\n /** A leading decorative icon, such as a `LucideIcon` (single-line only). */\n icon?: KerfUiContent;\n /**\n * A clear button while the field has a value (single-line only). Needs\n * `wireTextFields(root)`, which clears the field and announces `input`.\n */\n clearable?: boolean;\n /** The clear button's accessible name (default `Clear`). */\n clearLabel?: string;\n /** The most characters the field accepts (native `maxlength`). */\n maxLength?: number;\n /**\n * Show a live `count / maxLength` below the field. Needs `maxLength` and\n * `wireTextFields(root)`, which updates it as people type.\n */\n showCount?: boolean;\n /** Single-line value kind (default `text`); `date` uses the native picker. */\n type?: TextFieldType;\n /** Native lower bound for `type=\"number\"` (number or numeric string). */\n min?: number | string;\n /** Native upper bound for `type=\"number\"` (number or numeric string). */\n max?: number | string;\n /** Native number increment (number, numeric string, or `\"any\"`). */\n step?: number | string;\n /** A multi-line field: a `<textarea>`. */\n multiline?: boolean;\n /** A multi-line field's height in lines (default 3). */\n rows?: number;\n /**\n * Grow a multi-line field with its content from `rows` up to this many\n * lines, then scroll.\n */\n maxRows?: number;\n disabled?: boolean;\n readOnly?: boolean;\n required?: boolean;\n /** Request native focus on insertion or when the containing dialog opens. */\n autofocus?: boolean;\n autocomplete?: string;\n /** The input's id, for an external label or form. */\n id?: string;\n className?: string;\n /** Safe `data-*` metadata (a `data-key`). */\n rootAttributes?: TextFieldRootAttributes;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/**\n * A text input with its label above, an optional leading icon and clear\n * button, and supporting or error text below, at the 44px control height or\n * compact. `multiline` makes it a textarea that can grow with its content.\n * The whole field is one `<label>`, so clicking anywhere on it focuses the\n * input; the input is named by `label` (or `ariaLabel`) directly.\n */\nexport function TextField({\n label,\n ariaLabel,\n value,\n name,\n placeholder,\n hint,\n error,\n size = 'default',\n icon,\n clearable = false,\n clearLabel = 'Clear',\n maxLength,\n showCount = false,\n type = 'text',\n min,\n max,\n step,\n multiline = false,\n rows = 3,\n maxRows,\n disabled = false,\n readOnly = false,\n required = false,\n autofocus = false,\n autocomplete,\n id,\n className = '',\n rootAttributes = {},\n slot,\n}: TextFieldProps) {\n const description = [hint, error].filter(Boolean).join(' ') || undefined;\n const shared = {\n id,\n name,\n // A clearable field always has a placeholder, so CSS can hide its clear\n // button while the field is empty (`:placeholder-shown`); a blank one\n // shows nothing.\n placeholder: clearable && !multiline ? (placeholder ?? ' ') : placeholder,\n disabled,\n readonly: readOnly,\n required,\n autofocus,\n autocomplete,\n maxlength: maxLength,\n // The label element also wraps the clear button and the count, so the\n // input takes its name explicitly rather than from the label's text.\n 'aria-label': label ?? ariaLabel,\n 'aria-description': description,\n 'aria-invalid': error ? 'true' : undefined,\n } as const;\n const hintText =\n hint === undefined ? null : (\n <span class=\"kui-text-field__hint\" aria-hidden=\"true\">\n {hint}\n </span>\n );\n const count =\n showCount && maxLength !== undefined\n ? `${String((value ?? '').length)} / ${String(maxLength)}`\n : undefined;\n const style = [\n multiline ? `--_kui-text-field-rows:${String(rows)}` : '',\n multiline && maxRows !== undefined\n ? `--_kui-text-field-max-rows:${String(Math.max(rows, maxRows))}`\n : '',\n ]\n .filter(Boolean)\n .join(';');\n return (\n <label\n {...filterDataAttributes(rootAttributes, TEXT_FIELD_PROTECTED)}\n class={`kui-text-field ${className}`.trim()}\n data-component=\"text-field\"\n data-size={size}\n data-invalid={error ? '' : undefined}\n data-multiline={\n multiline ? (maxRows === undefined ? '' : 'grow') : undefined\n }\n style={style || undefined}\n slot={slot}\n >\n {label === undefined ? null : (\n <span class=\"kui-text-field__label\" aria-hidden=\"true\">\n {label}\n {required ? (\n <span class=\"kui-text-field__required\" aria-hidden=\"true\">\n *\n </span>\n ) : null}\n </span>\n )}\n <span class=\"kui-text-field__box\">\n {icon === undefined || multiline ? null : (\n <span class=\"kui-text-field__icon\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n {multiline ? (\n <textarea {...shared} class=\"kui-text-field__input\" rows={rows}>\n {value ?? ''}\n </textarea>\n ) : (\n <input\n {...shared}\n class=\"kui-text-field__input\"\n type={type}\n value={value}\n min={type === 'number' ? min : undefined}\n max={type === 'number' ? max : undefined}\n step={type === 'number' ? step : undefined}\n />\n )}\n {clearable && !multiline ? (\n <button\n type=\"button\"\n class=\"kui-text-field__clear\"\n aria-label={clearLabel}\n data-text-field-clear=\"\"\n disabled={disabled || readOnly}\n >\n <LucideIcon icon={CircleX} name=\"circle-x\" />\n </button>\n ) : null}\n </span>\n {count === undefined ? (\n hintText\n ) : (\n <span class=\"kui-text-field__footer\">\n {hintText}\n <span class=\"kui-text-field__count\" data-text-field-count=\"\">\n {count}\n </span>\n </span>\n )}\n {error === undefined ? null : (\n <span class=\"kui-text-field__error\" aria-hidden=\"true\">\n {error}\n </span>\n )}\n </label>\n );\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ export { wireScrollDividers } from './chunk-OZ6X76G5.js';
|
|
|
17
17
|
export { wireCheckboxes } from './chunk-CNM6ZWP2.js';
|
|
18
18
|
export { TabBarItem } from './chunk-ZCDRLN3B.js';
|
|
19
19
|
export { TabBar } from './chunk-7AK5I7U7.js';
|
|
20
|
-
export { TextField } from './chunk-
|
|
20
|
+
export { TextField } from './chunk-36HVQ72F.js';
|
|
21
21
|
export { Spacer } from './chunk-YMUIQRWK.js';
|
|
22
22
|
export { SplitButton } from './chunk-TI2GV36S.js';
|
|
23
23
|
export { StateBanner } from './chunk-2FW7D4IG.js';
|
package/dist/styles/button.css
CHANGED
|
@@ -44,7 +44,12 @@
|
|
|
44
44
|
--_kui-button-fill: var(--kui-color-brand-fill-loud);
|
|
45
45
|
--_kui-button-text: var(--kui-color-brand-on-loud);
|
|
46
46
|
--_kui-button-quiet: var(--kui-color-brand-fill-quiet);
|
|
47
|
-
|
|
47
|
+
/* A quiet hover fill can be stronger than the page (including app-owned
|
|
48
|
+
palettes), so darken the brand label enough for text on that fill. */
|
|
49
|
+
--_kui-button-on-quiet: light-dark(
|
|
50
|
+
oklch(from var(--kui-color-brand-on-quiet) min(l, 0.38) c h),
|
|
51
|
+
var(--kui-color-brand-on-quiet)
|
|
52
|
+
);
|
|
48
53
|
--_kui-button-outline: var(--kui-color-brand-border-loud);
|
|
49
54
|
}
|
|
50
55
|
.kui-button[data-tone="success"] {
|
|
@@ -108,7 +113,10 @@
|
|
|
108
113
|
--_kui-button-fill: var(--kui-color-brand-fill-loud);
|
|
109
114
|
--_kui-button-text: var(--kui-color-brand-on-loud);
|
|
110
115
|
--_kui-button-quiet: var(--kui-color-brand-fill-quiet);
|
|
111
|
-
--_kui-button-on-quiet:
|
|
116
|
+
--_kui-button-on-quiet: light-dark(
|
|
117
|
+
oklch(from var(--kui-color-brand-on-quiet) min(l, 0.38) c h),
|
|
118
|
+
var(--kui-color-brand-on-quiet)
|
|
119
|
+
);
|
|
112
120
|
--_kui-button-outline: var(--kui-color-brand-border-loud);
|
|
113
121
|
}
|
|
114
122
|
/* A pressed toggle draws on top of its tone and emphasis. Secondary and plain
|
|
@@ -44,7 +44,12 @@
|
|
|
44
44
|
--_kui-button-fill: var(--kui-color-brand-fill-loud);
|
|
45
45
|
--_kui-button-text: var(--kui-color-brand-on-loud);
|
|
46
46
|
--_kui-button-quiet: var(--kui-color-brand-fill-quiet);
|
|
47
|
-
|
|
47
|
+
/* A quiet hover fill can be stronger than the page (including app-owned
|
|
48
|
+
palettes), so darken the brand label enough for text on that fill. */
|
|
49
|
+
--_kui-button-on-quiet: light-dark(
|
|
50
|
+
oklch(from var(--kui-color-brand-on-quiet) min(l, 0.38) c h),
|
|
51
|
+
var(--kui-color-brand-on-quiet)
|
|
52
|
+
);
|
|
48
53
|
--_kui-button-outline: var(--kui-color-brand-border-loud);
|
|
49
54
|
}
|
|
50
55
|
.kui-button[data-tone="success"] {
|
|
@@ -108,7 +113,10 @@
|
|
|
108
113
|
--_kui-button-fill: var(--kui-color-brand-fill-loud);
|
|
109
114
|
--_kui-button-text: var(--kui-color-brand-on-loud);
|
|
110
115
|
--_kui-button-quiet: var(--kui-color-brand-fill-quiet);
|
|
111
|
-
--_kui-button-on-quiet:
|
|
116
|
+
--_kui-button-on-quiet: light-dark(
|
|
117
|
+
oklch(from var(--kui-color-brand-on-quiet) min(l, 0.38) c h),
|
|
118
|
+
var(--kui-color-brand-on-quiet)
|
|
119
|
+
);
|
|
112
120
|
--_kui-button-outline: var(--kui-color-brand-border-loud);
|
|
113
121
|
}
|
|
114
122
|
/* A pressed toggle draws on top of its tone and emphasis. Secondary and plain
|
package/dist/text-field.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
|
4
4
|
/** `default` is the 44px control height; `compact` is 33px with smaller text. */
|
|
5
5
|
type TextFieldSize = 'default' | 'compact';
|
|
6
6
|
/** The kinds of single-line value a TextField takes. */
|
|
7
|
-
type TextFieldType = 'text' | 'email' | 'password' | 'search' | 'url' | 'tel' | 'number';
|
|
7
|
+
type TextFieldType = 'text' | 'email' | 'password' | 'search' | 'url' | 'tel' | 'number' | 'date';
|
|
8
8
|
/** Application `data-*` metadata; the TextField's own markers stay protected. */
|
|
9
9
|
type TextFieldRootAttributes = Readonly<Record<`data-${string}`, string | undefined>>;
|
|
10
10
|
/**
|
|
@@ -48,7 +48,7 @@ interface TextFieldOptions {
|
|
|
48
48
|
* `wireTextFields(root)`, which updates it as people type.
|
|
49
49
|
*/
|
|
50
50
|
showCount?: boolean;
|
|
51
|
-
/** Single-line value kind (default `text`). */
|
|
51
|
+
/** Single-line value kind (default `text`); `date` uses the native picker. */
|
|
52
52
|
type?: TextFieldType;
|
|
53
53
|
/** Native lower bound for `type="number"` (number or numeric string). */
|
|
54
54
|
min?: number | string;
|
|
@@ -68,6 +68,8 @@ interface TextFieldOptions {
|
|
|
68
68
|
disabled?: boolean;
|
|
69
69
|
readOnly?: boolean;
|
|
70
70
|
required?: boolean;
|
|
71
|
+
/** Request native focus on insertion or when the containing dialog opens. */
|
|
72
|
+
autofocus?: boolean;
|
|
71
73
|
autocomplete?: string;
|
|
72
74
|
/** The input's id, for an external label or form. */
|
|
73
75
|
id?: string;
|
|
@@ -84,6 +86,6 @@ interface TextFieldOptions {
|
|
|
84
86
|
* The whole field is one `<label>`, so clicking anywhere on it focuses the
|
|
85
87
|
* input; the input is named by `label` (or `ariaLabel`) directly.
|
|
86
88
|
*/
|
|
87
|
-
declare function TextField({ label, ariaLabel, value, name, placeholder, hint, error, size, icon, clearable, clearLabel, maxLength, showCount, type, min, max, step, multiline, rows, maxRows, disabled, readOnly, required, autocomplete, id, className, rootAttributes, slot, }: TextFieldProps): kerfjs.SafeHtml;
|
|
89
|
+
declare function TextField({ label, ariaLabel, value, name, placeholder, hint, error, size, icon, clearable, clearLabel, maxLength, showCount, type, min, max, step, multiline, rows, maxRows, disabled, readOnly, required, autofocus, autocomplete, id, className, rootAttributes, slot, }: TextFieldProps): kerfjs.SafeHtml;
|
|
88
90
|
|
|
89
91
|
export { TextField, type TextFieldName, type TextFieldOptions, type TextFieldProps, type TextFieldRootAttributes, type TextFieldSize, type TextFieldType };
|
package/dist/text-field.js
CHANGED
package/docs/accessibility.md
CHANGED
|
@@ -546,12 +546,22 @@ its label; the input itself reports `required`. The focus ring is drawn on the
|
|
|
546
546
|
box around the input (`:focus-within`), 3px with a 1px offset like every Kerf
|
|
547
547
|
control.
|
|
548
548
|
|
|
549
|
+
`autofocus` forwards to the native input or textarea. It requests initial focus
|
|
550
|
+
when a field is inserted or its containing native dialog opens. Applications
|
|
551
|
+
still direct focus themselves when it must move from an already focused control.
|
|
552
|
+
|
|
549
553
|
For `type="number"`, `min`, `max`, and `step` pass through to the native input.
|
|
550
554
|
Each accepts a number or string; `step="any"` allows arbitrary decimals.
|
|
551
555
|
They are omitted for other input types and `multiline` textareas. Native
|
|
552
556
|
constraint validation (`rangeUnderflow`, `rangeOverflow`, and `stepMismatch`)
|
|
553
557
|
then applies without custom field wiring.
|
|
554
558
|
|
|
559
|
+
For `type="date"`, TextField renders a native date input. The browser owns its
|
|
560
|
+
picker, localized display, keyboard interaction, and `input`/`change` events;
|
|
561
|
+
the `value` remains an ISO date string (`YYYY-MM-DD`). The same `name`, label,
|
|
562
|
+
required, disabled, read-only, and focus behavior as other single-line fields
|
|
563
|
+
applies. Numeric `min`, `max`, and `step` props do not pass to date inputs.
|
|
564
|
+
|
|
555
565
|
`clearable` adds a native `<button>` named by `clearLabel` (default "Clear")
|
|
556
566
|
after the input. It is hidden while the field is empty, is a normal Tab stop,
|
|
557
567
|
and with `wireTextFields(root)` empties the field, dispatches bubbling `input`
|
|
@@ -563,6 +573,12 @@ touch screens so focusing a field never zooms the page in iOS.
|
|
|
563
573
|
|
|
564
574
|
## Button and SplitButton focus
|
|
565
575
|
|
|
576
|
+
A brand `Button` with `secondary` or `plain` emphasis clamps its
|
|
577
|
+
`brand-on-quiet` label to a darker light-theme value. Its quiet hover fill can
|
|
578
|
+
be stronger than the page background, so the unclamped token may be too light
|
|
579
|
+
for the button label. The same label color applies to pressed and focused
|
|
580
|
+
states; the focus ring remains visible around the control.
|
|
581
|
+
|
|
566
582
|
`Button` and each `SplitButton` half draw the 3px focus ring 1px outside the
|
|
567
583
|
box, against the surrounding surface, because a loud fill can share the ring's
|
|
568
584
|
hue (the brand fill matches the focus color in both schemes). An ancestor that
|
package/llms.txt
CHANGED
|
@@ -743,11 +743,12 @@ headingLevel`, `SearchField clearIcon`, `ContentItem frame="framed"` for a
|
|
|
743
743
|
an app div with `max-height`, `overflow`, and a border.
|
|
744
744
|
- **Use `Button` for every standalone action, icon-only included.**
|
|
745
745
|
`Button` (`@kerfjs/ui/button`) is a native button in a `tone` (`neutral`,
|
|
746
|
-
`brand`, `danger`) and an `emphasis` (`primary`, `secondary`, `plain`), at
|
|
746
|
+
`brand`, `success`, `danger`) and an `emphasis` (`primary`, `secondary`, `plain`), at
|
|
747
747
|
the 44px control height or `size="compact"`, with an optional `icon`,
|
|
748
748
|
`loading`, `href`, form `type`, `shape` (`rounded` or `pill`), and `pressed`
|
|
749
749
|
for a toggle. With an `icon` and `ariaLabel` but no label it is an icon-only
|
|
750
|
-
square (a circle as a pill).
|
|
750
|
+
square (a circle as a pill). Brand secondary/plain labels darken the brand
|
|
751
|
+
token in light mode to stay legible on a quiet hover fill. Inside a `ToolbarControlGroup`
|
|
751
752
|
keep a native `button`; the group owns its geometry.
|
|
752
753
|
- **Paint a strip's surface with `List surface`.** `surface="base"` or
|
|
753
754
|
`"raised"` fills a List and its insets with a solid surface (a banner strip
|
|
@@ -759,7 +760,10 @@ headingLevel`, `SearchField clearIcon`, `ContentItem frame="framed"` for a
|
|
|
759
760
|
`showCount`, at the 44px height or `size="compact"`. Call
|
|
760
761
|
`wireTextFields(root)` (`@kerfjs/ui/wire-text-fields`) once for clearing and
|
|
761
762
|
counting. For `type="number"`, pass native `min`, `max`, and `step` as numbers
|
|
762
|
-
or strings (`step="any"` accepts arbitrary decimals).
|
|
763
|
+
or strings (`step="any"` accepts arbitrary decimals). For `type="date"`,
|
|
764
|
+
pass an ISO date string and use the browser's native picker. `autofocus`
|
|
765
|
+
forwards to the native input or textarea for insertion and dialog open.
|
|
766
|
+
Never render `wa-input`
|
|
763
767
|
or `wa-textarea` for a form field.
|
|
764
768
|
- **Use `Checkbox`, `CheckboxGroup`, `RadioGroup`, and `Switch` for native
|
|
765
769
|
toggles.** `@kerfjs/ui/checkbox` (with `indeterminate` and
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/forms/text-field/text-field.tsx"],"names":[],"mappings":"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","file":"chunk-YVZCKXR3.js","sourcesContent":["import { CircleX } from 'lucide';\n\nimport type { KerfUiContent } from '../../../shared/content/semantic-content.js';\nimport { filterDataAttributes } from '../../../shared/dom/extension-attributes.js';\nimport { LucideIcon } from '../../media/lucide-icon/lucide-icon.js';\n\n/** `default` is the 44px control height; `compact` is 33px with smaller text. */\nexport type TextFieldSize = 'default' | 'compact';\n\n/** The kinds of single-line value a TextField takes. */\nexport type TextFieldType =\n 'text' | 'email' | 'password' | 'search' | 'url' | 'tel' | 'number';\n\nconst TEXT_FIELD_PROTECTED = new Set([\n 'data-component',\n 'data-size',\n 'data-invalid',\n 'data-multiline',\n]);\n\n/** Application `data-*` metadata; the TextField's own markers stay protected. */\nexport type TextFieldRootAttributes = Readonly<\n Record<`data-${string}`, string | undefined>\n>;\n\n/**\n * The field's accessible name: a visible `label`, or `ariaLabel` when the\n * surrounding content already names it.\n */\nexport type TextFieldName =\n { label: string; ariaLabel?: never } | { label?: never; ariaLabel: string };\n\nexport type TextFieldProps = TextFieldName & TextFieldOptions;\n\nexport interface TextFieldOptions {\n /** The controlled value. */\n value?: string;\n name?: string;\n placeholder?: string;\n /** Supporting text below the field, also its accessible description. */\n hint?: string;\n /**\n * A validation message: shown below the field in the danger color, added\n * to its description, and marking it `aria-invalid`.\n */\n error?: string;\n size?: TextFieldSize;\n /** A leading decorative icon, such as a `LucideIcon` (single-line only). */\n icon?: KerfUiContent;\n /**\n * A clear button while the field has a value (single-line only). Needs\n * `wireTextFields(root)`, which clears the field and announces `input`.\n */\n clearable?: boolean;\n /** The clear button's accessible name (default `Clear`). */\n clearLabel?: string;\n /** The most characters the field accepts (native `maxlength`). */\n maxLength?: number;\n /**\n * Show a live `count / maxLength` below the field. Needs `maxLength` and\n * `wireTextFields(root)`, which updates it as people type.\n */\n showCount?: boolean;\n /** Single-line value kind (default `text`). */\n type?: TextFieldType;\n /** Native lower bound for `type=\"number\"` (number or numeric string). */\n min?: number | string;\n /** Native upper bound for `type=\"number\"` (number or numeric string). */\n max?: number | string;\n /** Native number increment (number, numeric string, or `\"any\"`). */\n step?: number | string;\n /** A multi-line field: a `<textarea>`. */\n multiline?: boolean;\n /** A multi-line field's height in lines (default 3). */\n rows?: number;\n /**\n * Grow a multi-line field with its content from `rows` up to this many\n * lines, then scroll.\n */\n maxRows?: number;\n disabled?: boolean;\n readOnly?: boolean;\n required?: boolean;\n autocomplete?: string;\n /** The input's id, for an external label or form. */\n id?: string;\n className?: string;\n /** Safe `data-*` metadata (a `data-key`). */\n rootAttributes?: TextFieldRootAttributes;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/**\n * A text input with its label above, an optional leading icon and clear\n * button, and supporting or error text below, at the 44px control height or\n * compact. `multiline` makes it a textarea that can grow with its content.\n * The whole field is one `<label>`, so clicking anywhere on it focuses the\n * input; the input is named by `label` (or `ariaLabel`) directly.\n */\nexport function TextField({\n label,\n ariaLabel,\n value,\n name,\n placeholder,\n hint,\n error,\n size = 'default',\n icon,\n clearable = false,\n clearLabel = 'Clear',\n maxLength,\n showCount = false,\n type = 'text',\n min,\n max,\n step,\n multiline = false,\n rows = 3,\n maxRows,\n disabled = false,\n readOnly = false,\n required = false,\n autocomplete,\n id,\n className = '',\n rootAttributes = {},\n slot,\n}: TextFieldProps) {\n const description = [hint, error].filter(Boolean).join(' ') || undefined;\n const shared = {\n id,\n name,\n // A clearable field always has a placeholder, so CSS can hide its clear\n // button while the field is empty (`:placeholder-shown`); a blank one\n // shows nothing.\n placeholder: clearable && !multiline ? (placeholder ?? ' ') : placeholder,\n disabled,\n readonly: readOnly,\n required,\n autocomplete,\n maxlength: maxLength,\n // The label element also wraps the clear button and the count, so the\n // input takes its name explicitly rather than from the label's text.\n 'aria-label': label ?? ariaLabel,\n 'aria-description': description,\n 'aria-invalid': error ? 'true' : undefined,\n } as const;\n const hintText =\n hint === undefined ? null : (\n <span class=\"kui-text-field__hint\" aria-hidden=\"true\">\n {hint}\n </span>\n );\n const count =\n showCount && maxLength !== undefined\n ? `${String((value ?? '').length)} / ${String(maxLength)}`\n : undefined;\n const style = [\n multiline ? `--_kui-text-field-rows:${String(rows)}` : '',\n multiline && maxRows !== undefined\n ? `--_kui-text-field-max-rows:${String(Math.max(rows, maxRows))}`\n : '',\n ]\n .filter(Boolean)\n .join(';');\n return (\n <label\n {...filterDataAttributes(rootAttributes, TEXT_FIELD_PROTECTED)}\n class={`kui-text-field ${className}`.trim()}\n data-component=\"text-field\"\n data-size={size}\n data-invalid={error ? '' : undefined}\n data-multiline={\n multiline ? (maxRows === undefined ? '' : 'grow') : undefined\n }\n style={style || undefined}\n slot={slot}\n >\n {label === undefined ? null : (\n <span class=\"kui-text-field__label\" aria-hidden=\"true\">\n {label}\n {required ? (\n <span class=\"kui-text-field__required\" aria-hidden=\"true\">\n *\n </span>\n ) : null}\n </span>\n )}\n <span class=\"kui-text-field__box\">\n {icon === undefined || multiline ? null : (\n <span class=\"kui-text-field__icon\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n {multiline ? (\n <textarea {...shared} class=\"kui-text-field__input\" rows={rows}>\n {value ?? ''}\n </textarea>\n ) : (\n <input\n {...shared}\n class=\"kui-text-field__input\"\n type={type}\n value={value}\n min={type === 'number' ? min : undefined}\n max={type === 'number' ? max : undefined}\n step={type === 'number' ? step : undefined}\n />\n )}\n {clearable && !multiline ? (\n <button\n type=\"button\"\n class=\"kui-text-field__clear\"\n aria-label={clearLabel}\n data-text-field-clear=\"\"\n disabled={disabled || readOnly}\n >\n <LucideIcon icon={CircleX} name=\"circle-x\" />\n </button>\n ) : null}\n </span>\n {count === undefined ? (\n hintText\n ) : (\n <span class=\"kui-text-field__footer\">\n {hintText}\n <span class=\"kui-text-field__count\" data-text-field-count=\"\">\n {count}\n </span>\n </span>\n )}\n {error === undefined ? null : (\n <span class=\"kui-text-field__error\" aria-hidden=\"true\">\n {error}\n </span>\n )}\n </label>\n );\n}\n"]}
|