bank20baht-ui 0.0.2 → 0.0.7
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 +49 -0
- package/dist/components/baht-form.d.ts +13 -0
- package/dist/components/baht-workflow.d.ts +4 -3
- package/dist/components/primitives/baht-repeater.d.ts +33 -0
- package/dist/components/primitives/index.d.ts +2 -3
- package/dist/components/primitives/tag-map.d.ts +7 -0
- package/dist/core/i18n.d.ts +42 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/types.d.ts +19 -1
- package/dist/core/validators.d.ts +2 -0
- package/dist/core/views.d.ts +12 -0
- package/dist/custom-elements.json +507 -24
- package/dist/engine.js +261 -168
- package/dist/index.js +772 -659
- package/dist/react.d.ts +2 -0
- package/dist/tokens.css +38 -0
- package/dist/vscode.html-custom-data.json +22 -2
- package/dist/web-types.json +60 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,6 +35,55 @@ npm i bank20baht-ui
|
|
|
35
35
|
`<baht-form>` is a **dumb controlled component**: props in → UI → `baht-change` out. No buttons, no
|
|
36
36
|
cross-field logic, no fetch. Your app owns the state (mutate the JSON, feed it back).
|
|
37
37
|
|
|
38
|
+
### Views — show a subset of one form
|
|
39
|
+
|
|
40
|
+
Define named display projections on the schema (e.g. per user role); select one with the
|
|
41
|
+
`view` attribute:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
el.form = {
|
|
45
|
+
...form,
|
|
46
|
+
views: [{ name: 'summary', fields: ['age', 'firstName'] }], // view order = display order
|
|
47
|
+
};
|
|
48
|
+
el.view = 'summary'; // or <baht-form view="summary">; unset = full form
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Views are **client-side display only**: the view picks which fields render (and validate —
|
|
52
|
+
the user can only fix what they see), but `getData()` still returns the full form and the
|
|
53
|
+
server (`bank20baht-validator`) always validates the complete schema. In a workflow, set
|
|
54
|
+
`view: 'summary'` on a step's form to pin the view that step renders.
|
|
55
|
+
|
|
56
|
+
### Repeater — line items / repeating rows
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
{ order: 5, type: 'repeater', key: 'items', label: 'Line items', minRows: 1,
|
|
60
|
+
fields: [
|
|
61
|
+
{ order: 1, type: 'text', key: 'name', width: '1/2', required: true },
|
|
62
|
+
{ order: 2, type: 'number', key: 'qty', width: '1/4', min: 1 },
|
|
63
|
+
] }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Value is an array of row objects (`getData().items → [{ name, qty }, …]`). Rows add/remove in
|
|
67
|
+
the UI (`minRows`/`maxRows` bound the count), each row validates its sub-fields — errors come
|
|
68
|
+
back as `items[0].name`, on both the client and `bank20baht-validator`. One level deep only.
|
|
69
|
+
|
|
70
|
+
### i18n — Thai (or any) built-in strings
|
|
71
|
+
|
|
72
|
+
All built-in strings — validation messages, Next/Submit/Back, the approval step —
|
|
73
|
+
come from one catalog:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import { setLocale } from 'bank20baht-ui';
|
|
77
|
+
|
|
78
|
+
setLocale('th'); // built-ins: 'en' (default), 'th'
|
|
79
|
+
setLocale('th', { required: 'ห้ามว่าง' }); // locale + your own overrides
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Call it once before mounting. Per-field `message` and label attributes
|
|
83
|
+
(`next-label`, `approval.approveLabel`, …) still win over the catalog. The server can
|
|
84
|
+
localize too: `bank20baht-validator` re-exports `setLocale`, so validation issues come
|
|
85
|
+
back in the same language the client shows.
|
|
86
|
+
|
|
38
87
|
## Quick start — wizard with rules (Level 2)
|
|
39
88
|
|
|
40
89
|
```js
|
|
@@ -16,9 +16,22 @@ export declare class BahtForm extends BahtElement {
|
|
|
16
16
|
options: BahtFormOptions;
|
|
17
17
|
/** L2 (workflow) derived per-field UI state, keyed by field key. Optional. */
|
|
18
18
|
fieldStates: Record<string, DerivedFieldState>;
|
|
19
|
+
/**
|
|
20
|
+
* Name of a view from `form.views`. Display-only: when set, only that view's
|
|
21
|
+
* fields are rendered and validated (the user can only fix what they see),
|
|
22
|
+
* but getData() still returns the FULL form — the server always validates
|
|
23
|
+
* the complete schema, views never change the submit payload.
|
|
24
|
+
*/
|
|
25
|
+
view?: string;
|
|
19
26
|
/** Edits the user made since the host last set `value` (controlled overlay). */
|
|
20
27
|
private edits;
|
|
21
28
|
private errors;
|
|
29
|
+
/**
|
|
30
|
+
* One control element per field key, reused across renders. Recreating them
|
|
31
|
+
* every render (the naive approach) replaces the DOM node on each keystroke,
|
|
32
|
+
* which drops focus and makes continuous typing impossible.
|
|
33
|
+
*/
|
|
34
|
+
private controls;
|
|
22
35
|
protected willUpdate(changed: Map<PropertyKey, unknown>): void;
|
|
23
36
|
private get sortedFields();
|
|
24
37
|
private stateFor;
|
|
@@ -16,9 +16,10 @@ export declare class BahtWorkflow extends BahtElement {
|
|
|
16
16
|
value: NestedData;
|
|
17
17
|
registry: Registry;
|
|
18
18
|
validateGate: boolean;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/** Label attributes beat the i18n catalog; unset = t('next')/t('submit')/t('back'). */
|
|
20
|
+
nextLabel?: string;
|
|
21
|
+
submitLabel?: string;
|
|
22
|
+
backLabel?: string;
|
|
22
23
|
showBack: boolean;
|
|
23
24
|
private data;
|
|
24
25
|
private derived;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TemplateResult } from 'lit';
|
|
2
|
+
import { BahtFieldBase } from '../base.js';
|
|
3
|
+
import { ValidationError } from '../../core/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* <baht-repeater> — array-of-rows control (spec 01 §10). `value` is an array
|
|
6
|
+
* of row objects; every row renders the same `field.fields` sub-descriptors
|
|
7
|
+
* (one level deep — nested repeaters are skipped). Controlled like every
|
|
8
|
+
* primitive: edits emit the WHOLE array via `baht-input`; the host re-feeds.
|
|
9
|
+
*/
|
|
10
|
+
export declare class BahtRepeater extends BahtFieldBase {
|
|
11
|
+
/** Row-scoped errors from the host form, keyed `key[i].subKey`. */
|
|
12
|
+
rowErrors: ValidationError[];
|
|
13
|
+
/**
|
|
14
|
+
* One control per `${rowIndex}:${subKey}`, reused across renders so typing
|
|
15
|
+
* doesn't drop focus (same trick as <baht-form>, NOTES round 27). Cleared
|
|
16
|
+
* when the descriptor changes or row indexes shift (remove).
|
|
17
|
+
*/
|
|
18
|
+
private controls;
|
|
19
|
+
protected willUpdate(changed: Map<PropertyKey, unknown>): void;
|
|
20
|
+
private get rows();
|
|
21
|
+
private get subFields();
|
|
22
|
+
private patchRow;
|
|
23
|
+
private addRow;
|
|
24
|
+
private removeRow;
|
|
25
|
+
private errorFor;
|
|
26
|
+
private renderSub;
|
|
27
|
+
protected renderControl(): TemplateResult;
|
|
28
|
+
}
|
|
29
|
+
declare global {
|
|
30
|
+
interface HTMLElementTagNameMap {
|
|
31
|
+
'baht-repeater': BahtRepeater;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { FieldType } from '../../core/types.js';
|
|
2
1
|
export { BahtButton, type ButtonVariant, type ButtonSize } from './baht-button.js';
|
|
3
2
|
export { renderIcon, isIconName, type IconName } from './icons.js';
|
|
4
3
|
export { BahtBadge, type BadgeTone } from './baht-badge.js';
|
|
@@ -42,5 +41,5 @@ export { BahtEmptyState } from './baht-empty-state.js';
|
|
|
42
41
|
export { BahtErrorState } from './baht-error-state.js';
|
|
43
42
|
export { BahtKeyvalueEditor } from './baht-keyvalue-editor.js';
|
|
44
43
|
export { BahtReferencePicker, type ReferenceOption } from './baht-reference-picker.js';
|
|
45
|
-
|
|
46
|
-
export
|
|
44
|
+
export { BahtRepeater } from './baht-repeater.js';
|
|
45
|
+
export { TAG_FOR_TYPE } from './tag-map.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FieldType } from '../../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* field descriptor `type` -> primitive tag (used by <baht-form>, <baht-repeater>
|
|
4
|
+
* and the builder). Lives in its own module so baht-repeater can read it
|
|
5
|
+
* without importing the primitives barrel (which would be circular).
|
|
6
|
+
*/
|
|
7
|
+
export declare const TAG_FOR_TYPE: Record<FieldType, string>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface MessageCatalog {
|
|
2
|
+
required: string;
|
|
3
|
+
minLength: string;
|
|
4
|
+
maxLength: string;
|
|
5
|
+
min: string;
|
|
6
|
+
max: string;
|
|
7
|
+
pattern: string;
|
|
8
|
+
email: string;
|
|
9
|
+
addRow: string;
|
|
10
|
+
removeRow: string;
|
|
11
|
+
minRows: string;
|
|
12
|
+
maxRows: string;
|
|
13
|
+
next: string;
|
|
14
|
+
submit: string;
|
|
15
|
+
back: string;
|
|
16
|
+
step: string;
|
|
17
|
+
approve: string;
|
|
18
|
+
reject: string;
|
|
19
|
+
decision: string;
|
|
20
|
+
comment: string;
|
|
21
|
+
commentRequiredSuffix: string;
|
|
22
|
+
decisionRequired: string;
|
|
23
|
+
commentRequired: string;
|
|
24
|
+
nothingToReview: string;
|
|
25
|
+
yes: string;
|
|
26
|
+
no: string;
|
|
27
|
+
emptyValue: string;
|
|
28
|
+
}
|
|
29
|
+
export type MessageKey = keyof MessageCatalog;
|
|
30
|
+
export type Locale = 'en' | 'th';
|
|
31
|
+
/**
|
|
32
|
+
* Switch every built-in string to a locale, optionally patching single keys:
|
|
33
|
+
*
|
|
34
|
+
* setLocale('th') // all Thai
|
|
35
|
+
* setLocale('th', { required: 'ห้ามว่าง' }) // Thai + one custom message
|
|
36
|
+
*
|
|
37
|
+
* Global and synchronous — set it before mounting (or before validating on
|
|
38
|
+
* the server). Components read the catalog at render time.
|
|
39
|
+
*/
|
|
40
|
+
export declare function setLocale(locale: Locale, overrides?: Partial<MessageCatalog>): void;
|
|
41
|
+
/** Resolve one catalog string, interpolating `{param}` placeholders. */
|
|
42
|
+
export declare function t(key: MessageKey, params?: Record<string, unknown>): string;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ export * from './types.js';
|
|
|
2
2
|
export { defaultValue } from './defaults.js';
|
|
3
3
|
export { approvalFields } from './approval.js';
|
|
4
4
|
export { validateField } from './validators.js';
|
|
5
|
+
export { resolveViewFields } from './views.js';
|
|
6
|
+
export { setLocale, t } from './i18n.js';
|
|
7
|
+
export type { Locale, MessageCatalog, MessageKey } from './i18n.js';
|
|
5
8
|
export { evaluate } from './engine/evaluate.js';
|
|
6
9
|
export { evalCondition, readRef } from './engine/conditions.js';
|
|
7
10
|
export { evalValueExpr } from './engine/expressions.js';
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Control types supported by v1 (spec 01 §2). */
|
|
2
|
-
export type FieldType = 'text' | 'textarea' | 'number' | 'email' | 'dropdown' | 'multiselect' | 'checkbox' | 'radio' | 'toggle' | 'date' | 'file' | 'label';
|
|
2
|
+
export type FieldType = 'text' | 'textarea' | 'number' | 'email' | 'dropdown' | 'multiselect' | 'checkbox' | 'radio' | 'toggle' | 'date' | 'file' | 'label' | 'repeater';
|
|
3
3
|
export type FieldWidth = '1/4' | '1/2' | '3/4' | 'full';
|
|
4
4
|
export interface FieldOption {
|
|
5
5
|
key: string;
|
|
@@ -37,14 +37,30 @@ export interface FieldDescriptor {
|
|
|
37
37
|
maxSize?: number;
|
|
38
38
|
text?: string;
|
|
39
39
|
variant?: string;
|
|
40
|
+
fields?: FieldDescriptor[];
|
|
41
|
+
minRows?: number;
|
|
42
|
+
maxRows?: number;
|
|
43
|
+
addLabel?: string;
|
|
40
44
|
validators?: ValidatorSpec[];
|
|
41
45
|
formatter?: string;
|
|
42
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* A named, client-side-only projection of a form: which fields to show, in
|
|
49
|
+
* what order (e.g. per user role). Display sugar only — the submit payload is
|
|
50
|
+
* always the full form and the server always validates the complete schema.
|
|
51
|
+
*/
|
|
52
|
+
export interface FormViewSpec {
|
|
53
|
+
name: string;
|
|
54
|
+
/** field keys included, in display order; keys absent from field_data are ignored */
|
|
55
|
+
fields: string[];
|
|
56
|
+
}
|
|
43
57
|
/** A Form definition — consumed standalone by <baht-form> (spec 00 §4.1). */
|
|
44
58
|
export interface FormDefinition {
|
|
45
59
|
formId: string;
|
|
46
60
|
formName: string;
|
|
47
61
|
field_data: FieldDescriptor[];
|
|
62
|
+
/** Optional named display subsets; select one via <baht-form view="...">. */
|
|
63
|
+
views?: FormViewSpec[];
|
|
48
64
|
}
|
|
49
65
|
export type ApprovalDecision = 'approved' | 'rejected';
|
|
50
66
|
/** Comment box policy on an approval step. */
|
|
@@ -73,6 +89,8 @@ export interface WorkflowForm extends FormDefinition {
|
|
|
73
89
|
*/
|
|
74
90
|
kind?: 'form' | 'approval';
|
|
75
91
|
approval?: ApprovalConfig;
|
|
92
|
+
/** Name of one of this form's `views` to render for this step (display only). */
|
|
93
|
+
view?: string;
|
|
76
94
|
}
|
|
77
95
|
export type ConditionOp = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'notIn' | 'contains' | 'notContains' | 'startsWith' | 'endsWith' | 'isEmpty' | 'isNotEmpty';
|
|
78
96
|
/** Leaf: compare `formName.fieldKey` against a constant or another field. */
|
|
@@ -2,5 +2,7 @@ import { FieldDescriptor, Registry, ValidationError } from './types.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Validate one field's value against its declarative validators (spec 01 §4).
|
|
4
4
|
* Empty + not required = valid (other validators only run on non-empty values).
|
|
5
|
+
* Repeaters (spec 01 §10) additionally check row bounds and recurse into each
|
|
6
|
+
* row's sub-fields, prefixing keys as `key[i].subKey`.
|
|
5
7
|
*/
|
|
6
8
|
export declare function validateField(field: FieldDescriptor, value: unknown, registry?: Registry): ValidationError[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FieldDescriptor, FormDefinition } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the fields a consumer of `form` should DISPLAY. Views are
|
|
4
|
+
* client-side projections only — data and server validation always cover the
|
|
5
|
+
* full form.
|
|
6
|
+
*
|
|
7
|
+
* - No `view` → every field, sorted by `order` (the full form).
|
|
8
|
+
* - A known view → only its fields, in the order the view lists them.
|
|
9
|
+
* - An unknown view → `null`; the caller decides the fallback (<baht-form>
|
|
10
|
+
* warns and shows the full form).
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveViewFields(form: FormDefinition, view?: string): FieldDescriptor[] | null;
|