@happyvertical/smrt-ui 0.57.19 → 0.57.21

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.
Files changed (39) hide show
  1. package/AGENTS.md +11 -1
  2. package/README.md +91 -0
  3. package/dist/components/display/DocumentViewer.svelte +443 -0
  4. package/dist/components/display/DocumentViewer.svelte.d.ts +24 -0
  5. package/dist/components/display/DocumentViewer.svelte.d.ts.map +1 -0
  6. package/dist/components/display/document-viewer-logic.d.ts +35 -0
  7. package/dist/components/display/document-viewer-logic.d.ts.map +1 -0
  8. package/dist/components/display/document-viewer-logic.js +56 -0
  9. package/dist/components/display/index.d.ts +5 -1
  10. package/dist/components/display/index.d.ts.map +1 -1
  11. package/dist/components/display/index.js +4 -0
  12. package/dist/components/display/pdf-opener.d.ts +44 -0
  13. package/dist/components/display/pdf-opener.d.ts.map +1 -0
  14. package/dist/components/display/pdf-opener.js +121 -0
  15. package/dist/components/display/pdf-types.d.ts +19 -0
  16. package/dist/components/display/pdf-types.d.ts.map +1 -0
  17. package/dist/components/display/pdf-types.js +7 -0
  18. package/dist/components/display/types.d.ts +19 -0
  19. package/dist/components/display/types.d.ts.map +1 -1
  20. package/dist/components/forms/NumericKeypad.svelte +511 -0
  21. package/dist/components/forms/NumericKeypad.svelte.d.ts +51 -0
  22. package/dist/components/forms/NumericKeypad.svelte.d.ts.map +1 -0
  23. package/dist/components/forms/index.d.ts +3 -1
  24. package/dist/components/forms/index.d.ts.map +1 -1
  25. package/dist/components/forms/index.js +2 -0
  26. package/dist/components/forms/numeric-keypad-logic.d.ts +68 -0
  27. package/dist/components/forms/numeric-keypad-logic.d.ts.map +1 -0
  28. package/dist/components/forms/numeric-keypad-logic.js +157 -0
  29. package/dist/components/forms/types.d.ts +17 -0
  30. package/dist/components/forms/types.d.ts.map +1 -1
  31. package/dist/i18n/strings.ui.d.ts +23 -0
  32. package/dist/i18n/strings.ui.d.ts.map +1 -1
  33. package/dist/i18n/strings.ui.js +25 -0
  34. package/dist/svelte/playground/TouchToolsPreview.svelte +47 -0
  35. package/dist/svelte/playground/TouchToolsPreview.svelte.d.ts +4 -0
  36. package/dist/svelte/playground/TouchToolsPreview.svelte.d.ts.map +1 -0
  37. package/dist/svelte/playground.d.ts.map +1 -1
  38. package/dist/svelte/playground.js +10 -0
  39. package/package.json +13 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numeric-keypad-logic.d.ts","sourceRoot":"","sources":["../../../src/components/forms/numeric-keypad-logic.ts"],"names":[],"mappings":"AAUA,+BAA+B;AAC/B,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB,gEAAgE;AAChE,MAAM,WAAW,mBAAmB;IAClC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uEAAuE;IACvE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gEAAgE;IAChE,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,4DAA4D;AAC5D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3E,kCAAkC;AAClC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAIrC,yEAAyE;AACzE,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,SAAI,GACX,MAAM,CAMR;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAM7D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EACvC,KAAK,EAAE,UAAU,GAChB,MAAM,CAeR;AASD,gFAAgF;AAChF,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,UAAU,GAChB,MAAM,CAsBR;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAItD;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACrC,aAAa,CAQf;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIzE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAWf;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/B,MAAM,GAAG,IAAI,CASf"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Pure entry logic for `NumericKeypad` (smrt#3463): key press to next text,
3
+ * range validation, and typed text <-> integer minor units. Svelte-free and
4
+ * DOM-free so it unit-tests without rendering.
5
+ *
6
+ * Money never goes through a float: text converts to and from integer minor
7
+ * units with string arithmetic, so `0.1 + 0.2` style drift cannot appear.
8
+ */
9
+ import { ISO_4217_MINOR_UNITS } from '../display/currency-metadata.js';
10
+ /** Default longest typed text. */
11
+ export const DEFAULT_MAX_LENGTH = 12;
12
+ const TEXT_PATTERN = /^\d*(\.\d*)?$/;
13
+ /** Minor-unit exponent of an ISO 4217 currency (USD 2, JPY 0, KWD 3). */
14
+ export function minorUnitExponent(currency, fallback = 2) {
15
+ if (!currency)
16
+ return fallback;
17
+ const exponent = ISO_4217_MINOR_UNITS.get(currency.toUpperCase());
18
+ if (exponent === undefined)
19
+ return fallback;
20
+ // Funds and metals have no minor unit: whole units only.
21
+ return exponent === null ? 0 : exponent;
22
+ }
23
+ /** Map a hardware `KeyboardEvent.key` to a keypad key; `,` types a point. */
24
+ export function keyFromKeyboard(key) {
25
+ if (/^[0-9]$/.test(key))
26
+ return { kind: 'digit', digit: key };
27
+ if (key === '.' || key === ',')
28
+ return { kind: 'decimal' };
29
+ if (key === 'Backspace')
30
+ return { kind: 'backspace' };
31
+ if (key === 'Delete')
32
+ return { kind: 'clear' };
33
+ return null;
34
+ }
35
+ /**
36
+ * Coerce an outside value (a bound string, a number, junk) to well-formed
37
+ * typed text under `rules`; anything unrepresentable becomes `''`.
38
+ */
39
+ export function normalizeText(raw, rules) {
40
+ if (raw === null || raw === undefined)
41
+ return '';
42
+ let text = typeof raw === 'number' ? numberToPlainText(raw) : raw.trim();
43
+ if (text === '')
44
+ return '';
45
+ text = text.replace(',', '.');
46
+ if (!TEXT_PATTERN.test(text))
47
+ return '';
48
+ let [whole = '', fraction] = text.split('.');
49
+ whole = whole.replace(/^0+(?=\d)/, '');
50
+ if (whole === '' && fraction !== undefined)
51
+ whole = '0';
52
+ if (fraction === undefined || rules.maxDecimals <= 0) {
53
+ text = whole;
54
+ }
55
+ else {
56
+ text = `${whole}.${fraction.slice(0, rules.maxDecimals)}`;
57
+ }
58
+ return text.length > rules.maxLength ? '' : text;
59
+ }
60
+ function numberToPlainText(value) {
61
+ if (!Number.isFinite(value) || value < 0)
62
+ return '';
63
+ // Exponent forms ("1e21") are not typeable text.
64
+ const text = String(value);
65
+ return /e/i.test(text) ? '' : text;
66
+ }
67
+ /** The text after pressing `key`; the same string when the press is refused. */
68
+ export function applyKey(text, key, rules) {
69
+ switch (key.kind) {
70
+ case 'clear':
71
+ return '';
72
+ case 'backspace':
73
+ return text.slice(0, -1);
74
+ case 'decimal': {
75
+ if (rules.maxDecimals <= 0 || text.includes('.'))
76
+ return text;
77
+ const next = text === '' ? '0.' : `${text}.`;
78
+ return next.length > rules.maxLength ? text : next;
79
+ }
80
+ case 'digit': {
81
+ if (!/^[0-9]$/.test(key.digit))
82
+ return text;
83
+ const point = text.indexOf('.');
84
+ if (point >= 0 && text.length - point - 1 >= rules.maxDecimals) {
85
+ return text;
86
+ }
87
+ // A lone leading zero is replaced, so "05" never exists.
88
+ const next = text === '0' ? key.digit : `${text}${key.digit}`;
89
+ return next.length > rules.maxLength ? text : next;
90
+ }
91
+ }
92
+ }
93
+ /** Number value of typed text, or `null` when empty. Display and range use only. */
94
+ export function parseEntry(text) {
95
+ if (text === '' || text === '.')
96
+ return null;
97
+ const value = Number(text);
98
+ return Number.isFinite(value) ? value : null;
99
+ }
100
+ /** Check typed text against optional `min` / `max` (major units). */
101
+ export function validateEntry(text, limits) {
102
+ if (text === '')
103
+ return 'empty';
104
+ if (!TEXT_PATTERN.test(text))
105
+ return 'invalid';
106
+ const value = parseEntry(text);
107
+ if (value === null)
108
+ return 'invalid';
109
+ if (limits.min !== undefined && value < limits.min)
110
+ return 'below';
111
+ if (limits.max !== undefined && value > limits.max)
112
+ return 'above';
113
+ return 'ok';
114
+ }
115
+ /** True when `text` is above `max`: the entry the "clamp" mode refuses. */
116
+ export function exceedsMax(text, max) {
117
+ if (max === undefined)
118
+ return false;
119
+ const value = parseEntry(text);
120
+ return value !== null && value > max;
121
+ }
122
+ /**
123
+ * Typed text to integer minor units (`"12.5"` with exponent 2 is `1250`).
124
+ * `null` for empty or malformed text, more decimals than `exponent`, or a
125
+ * result beyond `Number.MAX_SAFE_INTEGER`.
126
+ */
127
+ export function textToMinorUnits(text, exponent) {
128
+ if (!Number.isInteger(exponent) || exponent < 0)
129
+ return null;
130
+ if (text === '' || text === '.' || !TEXT_PATTERN.test(text))
131
+ return null;
132
+ const [whole = '', fraction = ''] = text.split('.');
133
+ if (fraction.length > exponent)
134
+ return null;
135
+ const digits = `${whole}${fraction.padEnd(exponent, '0')}`.replace(/^0+(?=\d)/, '');
136
+ const minor = Number(digits === '' ? '0' : digits);
137
+ return Number.isSafeInteger(minor) ? minor : null;
138
+ }
139
+ /**
140
+ * Integer minor units to typed text (`1250`, exponent 2 -> `"12.5"`). With
141
+ * `trim: false` the fraction is padded to `exponent` (`"12.50"`). `null` for a
142
+ * non-integer, negative, or unsafe amount.
143
+ */
144
+ export function minorUnitsToText(minor, exponent, options = {}) {
145
+ if (!Number.isSafeInteger(minor) || minor < 0)
146
+ return null;
147
+ if (!Number.isInteger(exponent) || exponent < 0)
148
+ return null;
149
+ if (exponent === 0)
150
+ return String(minor);
151
+ const padded = String(minor).padStart(exponent + 1, '0');
152
+ const whole = padded.slice(0, -exponent);
153
+ let fraction = padded.slice(-exponent);
154
+ if (options.trim !== false)
155
+ fraction = fraction.replace(/0+$/, '');
156
+ return fraction === '' ? whole : `${whole}.${fraction}`;
157
+ }
@@ -52,6 +52,23 @@ export interface SignaturePadLabels {
52
52
  useSignature: string;
53
53
  committed: string;
54
54
  }
55
+ /** Text overrides for `NumericKeypad`; defaults come from the `ui` i18n catalog. */
56
+ export interface NumericKeypadLabels {
57
+ /** Accessible name of the keypad group. */
58
+ region: string;
59
+ /** Accessible name of the display line. */
60
+ display: string;
61
+ decimal: string;
62
+ backspace: string;
63
+ clear: string;
64
+ submit: string;
65
+ /** Template with `{min}`. */
66
+ belowMin: string;
67
+ /** Template with `{max}`. */
68
+ aboveMax: string;
69
+ /** The amount cannot be represented as safe integer minor units. */
70
+ invalidAmount: string;
71
+ }
55
72
  /** A searchable relation target for the RelationInput component */
56
73
  export interface RelationOption {
57
74
  /** Record id; this is the value the form posts. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/forms/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,IAAI,EAAE,IAAI,CAAC;IACX,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAC1B,KAAK,GACL,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,mBAAmB,GACnB,WAAW,GACX,aAAa,GACb,OAAO,GACP,UAAU,CAAC;AAEf,oFAAoF;AACpF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEjE,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/forms/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,IAAI,EAAE,IAAI,CAAC;IACX,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAC1B,KAAK,GACL,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,mBAAmB,GACnB,WAAW,GACX,aAAa,GACb,OAAO,GACP,UAAU,CAAC;AAEf,oFAAoF;AACpF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEjE,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,oFAAoF;AACpF,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -107,6 +107,29 @@ export declare const M: {
107
107
  readonly 'ui.signature_pad.clear': "ui.signature_pad.clear";
108
108
  readonly 'ui.signature_pad.use_signature': "ui.signature_pad.use_signature";
109
109
  readonly 'ui.signature_pad.committed': "ui.signature_pad.committed";
110
+ readonly 'ui.numeric_keypad.region': "ui.numeric_keypad.region";
111
+ readonly 'ui.numeric_keypad.display': "ui.numeric_keypad.display";
112
+ readonly 'ui.numeric_keypad.decimal': "ui.numeric_keypad.decimal";
113
+ readonly 'ui.numeric_keypad.backspace': "ui.numeric_keypad.backspace";
114
+ readonly 'ui.numeric_keypad.clear': "ui.numeric_keypad.clear";
115
+ readonly 'ui.numeric_keypad.submit': "ui.numeric_keypad.submit";
116
+ readonly 'ui.numeric_keypad.below_min': "ui.numeric_keypad.below_min";
117
+ readonly 'ui.numeric_keypad.above_max': "ui.numeric_keypad.above_max";
118
+ readonly 'ui.numeric_keypad.invalid_amount': "ui.numeric_keypad.invalid_amount";
119
+ readonly 'ui.document_viewer.region': "ui.document_viewer.region";
120
+ readonly 'ui.document_viewer.pages': "ui.document_viewer.pages";
121
+ readonly 'ui.document_viewer.zoom': "ui.document_viewer.zoom";
122
+ readonly 'ui.document_viewer.previous_page': "ui.document_viewer.previous_page";
123
+ readonly 'ui.document_viewer.next_page': "ui.document_viewer.next_page";
124
+ readonly 'ui.document_viewer.page_of': "ui.document_viewer.page_of";
125
+ readonly 'ui.document_viewer.page_label': "ui.document_viewer.page_label";
126
+ readonly 'ui.document_viewer.zoom_out': "ui.document_viewer.zoom_out";
127
+ readonly 'ui.document_viewer.zoom_in': "ui.document_viewer.zoom_in";
128
+ readonly 'ui.document_viewer.fit': "ui.document_viewer.fit";
129
+ readonly 'ui.document_viewer.loading': "ui.document_viewer.loading";
130
+ readonly 'ui.document_viewer.failed': "ui.document_viewer.failed";
131
+ readonly 'ui.document_viewer.retry': "ui.document_viewer.retry";
132
+ readonly 'ui.document_viewer.unsupported': "ui.document_viewer.unsupported";
110
133
  readonly 'ui.staged_control_review.region': "ui.staged_control_review.region";
111
134
  readonly 'ui.staged_control_review.heading': "ui.staged_control_review.heading";
112
135
  readonly 'ui.staged_control_review.description': "ui.staged_control_review.description";
@@ -1 +1 @@
1
- {"version":3,"file":"strings.ui.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ui.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6KZ,CAAC"}
1
+ {"version":3,"file":"strings.ui.d.ts","sourceRoot":"","sources":["../../src/i18n/strings.ui.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2MZ,CAAC"}
@@ -126,6 +126,31 @@ export const M = defineMessages({
126
126
  'ui.signature_pad.clear': 'Clear',
127
127
  'ui.signature_pad.use_signature': 'Use signature',
128
128
  'ui.signature_pad.committed': 'Signature attached.',
129
+ // forms/NumericKeypad.svelte
130
+ 'ui.numeric_keypad.region': 'Number keypad',
131
+ 'ui.numeric_keypad.display': 'Entered amount',
132
+ 'ui.numeric_keypad.decimal': 'Decimal point',
133
+ 'ui.numeric_keypad.backspace': 'Delete last character',
134
+ 'ui.numeric_keypad.clear': 'Clear',
135
+ 'ui.numeric_keypad.submit': 'Confirm',
136
+ 'ui.numeric_keypad.below_min': 'Must be at least {min}.',
137
+ 'ui.numeric_keypad.above_max': 'Must be at most {max}.',
138
+ 'ui.numeric_keypad.invalid_amount': 'Enter an amount within the supported range.',
139
+ // display/DocumentViewer.svelte
140
+ 'ui.document_viewer.region': 'Document',
141
+ 'ui.document_viewer.pages': 'Pages',
142
+ 'ui.document_viewer.zoom': 'Zoom',
143
+ 'ui.document_viewer.previous_page': 'Previous page',
144
+ 'ui.document_viewer.next_page': 'Next page',
145
+ 'ui.document_viewer.page_of': 'Page {page} of {pages}',
146
+ 'ui.document_viewer.page_label': '{label}, page {page}',
147
+ 'ui.document_viewer.zoom_out': 'Zoom out',
148
+ 'ui.document_viewer.zoom_in': 'Zoom in',
149
+ 'ui.document_viewer.fit': 'Fit',
150
+ 'ui.document_viewer.loading': 'Loading the document…',
151
+ 'ui.document_viewer.failed': 'This document could not be opened. Check the network and try again.',
152
+ 'ui.document_viewer.retry': 'Try again',
153
+ 'ui.document_viewer.unsupported': 'This kind of file cannot be shown here. Use a PDF or a picture.',
129
154
  // forms/StagedControlReview.svelte
130
155
  'ui.staged_control_review.region': 'Review proposed changes',
131
156
  'ui.staged_control_review.heading': 'Proposed changes',
@@ -0,0 +1,47 @@
1
+ <script lang="ts">
2
+ import DocumentViewer from '../../components/display/DocumentViewer.svelte';
3
+ import NumericKeypad from '../../components/forms/NumericKeypad.svelte';
4
+ import type { NumericKeypadDetail } from '../../components/forms/numeric-keypad-logic.js';
5
+
6
+ let quantity = $state('');
7
+ let amount = $state('');
8
+ let lastMoney = $state<NumericKeypadDetail | null>(null);
9
+
10
+ // A drawn sheet served from memory, so the preview needs no network or pdf.js.
11
+ const sheet = `<svg xmlns="http://www.w3.org/2000/svg" width="800" height="1000" viewBox="0 0 800 1000"><rect width="800" height="1000" fill="#fff"/><rect x="40" y="40" width="720" height="920" fill="none" stroke="#111" stroke-width="4"/><text x="80" y="130" font-family="sans-serif" font-size="48" fill="#111">Sample shop print</text><path d="M80 220h640M80 300h440M80 380h560M80 460h300" stroke="#555" stroke-width="6"/></svg>`;
12
+ const previewFetch: typeof fetch = async () =>
13
+ new Response(sheet, { headers: { 'content-type': 'image/svg+xml' } });
14
+ </script>
15
+
16
+ <div class="workbench">
17
+ <header>
18
+ <p class="eyebrow">Touch density</p>
19
+ <h4>Keypad and document viewer</h4>
20
+ <p>Every key and control is at least 4rem. Money is carried as integer minor units; the viewer reads through an injected fetch and never points an element at the API.</p>
21
+ </header>
22
+ <section>
23
+ <h5>Quantity</h5>
24
+ <NumericKeypad bind:value={quantity} min={1} max={500} unit="pcs" />
25
+ </section>
26
+ <section>
27
+ <h5>Money (USD)</h5>
28
+ <NumericKeypad bind:value={amount} mode="money" currency="USD" prefix="$" onChange={(detail) => (lastMoney = detail)} onSubmit={(detail) => (lastMoney = detail)} />
29
+ <p class="posted" role="status">{lastMoney?.minorUnits == null ? 'Type an amount.' : `${lastMoney.minorUnits} minor units`}</p>
30
+ </section>
31
+ <section>
32
+ <h5>Document</h5>
33
+ <DocumentViewer src="/preview/sheet.svg" mimeType="image/svg+xml" label="Sample shop print" fetch={previewFetch} />
34
+ </section>
35
+ </div>
36
+
37
+ <style>
38
+ @layer smrt.reset, smrt.structure, smrt.theme;
39
+ @layer smrt.theme {
40
+ .workbench { display: grid; gap: var(--smrt-spacing-5); color: var(--smrt-color-on-surface); }
41
+ header, section { display: grid; gap: var(--smrt-spacing-3); min-width: 0; }
42
+ h4, h5, p { margin: 0; } h4 { font: var(--smrt-typography-headline-small-font); }
43
+ h5 { font: var(--smrt-typography-title-medium-font); }
44
+ header p:not(.eyebrow), .posted { color: var(--smrt-color-on-surface-variant); }
45
+ .eyebrow { color: var(--smrt-color-primary); font: var(--smrt-typography-label-small-font); letter-spacing: .1em; text-transform: uppercase; }
46
+ }
47
+ </style>
@@ -0,0 +1,4 @@
1
+ declare const TouchToolsPreview: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type TouchToolsPreview = ReturnType<typeof TouchToolsPreview>;
3
+ export default TouchToolsPreview;
4
+ //# sourceMappingURL=TouchToolsPreview.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TouchToolsPreview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/playground/TouchToolsPreview.svelte.ts"],"names":[],"mappings":"AA+CA,QAAA,MAAM,iBAAiB,2DAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../src/svelte/playground.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaA,wBAmHE"}
1
+ {"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../src/svelte/playground.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA,wBA6HE"}
@@ -5,6 +5,7 @@ const loadFeedback = () => import('./playground/FeedbackPreview.svelte');
5
5
  const loadCollections = () => import('./playground/CollectionsPreview.svelte');
6
6
  const loadCalendar = () => import('./playground/CalendarPreview.svelte');
7
7
  const loadCapture = () => import('./playground/CapturePreview.svelte');
8
+ const loadTouchTools = () => import('./playground/TouchToolsPreview.svelte');
8
9
  const loadPermissionPicker = () => import('./playground/PermissionPickerPreview.svelte');
9
10
  const loadStyleless = () => import('./playground/StylelessPreview.svelte');
10
11
  export default {
@@ -110,5 +111,14 @@ export default {
110
111
  tags: ['styling', 'styleless', 'layers', 'restyling'],
111
112
  modes: { mock: { label: 'Interactive' } },
112
113
  },
114
+ {
115
+ id: 'touch-tools',
116
+ title: 'Touch Keypad & Document Viewer',
117
+ description: 'Glove-sized number/money keypad with integer minor units, and a pinch-zoom document viewer that reads files through an injected fetch.',
118
+ loadComponent: loadTouchTools,
119
+ order: 11,
120
+ tags: ['forms', 'touch', 'keypad', 'money', 'pdf', 'viewer'],
121
+ modes: { mock: { label: 'Interactive' } },
122
+ },
113
123
  ],
114
124
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-ui",
3
- "version": "0.57.19",
3
+ "version": "0.57.21",
4
4
  "smrtJsdoc": "strict",
5
5
  "description": "Domain-agnostic Svelte 5 UI runtime for SMRT: primitives, i18n client, theme system, and module UI registry",
6
6
  "type": "module",
@@ -24,6 +24,12 @@
24
24
  "import": "./dist/components/ui/index.js",
25
25
  "default": "./dist/components/ui/index.js"
26
26
  },
27
+ "./display": {
28
+ "types": "./dist/components/display/index.d.ts",
29
+ "svelte": "./dist/components/display/index.js",
30
+ "import": "./dist/components/display/index.js",
31
+ "default": "./dist/components/display/index.js"
32
+ },
27
33
  "./feedback": {
28
34
  "types": "./dist/components/feedback/index.d.ts",
29
35
  "svelte": "./dist/components/feedback/index.js",
@@ -164,17 +170,21 @@
164
170
  "access": "public"
165
171
  },
166
172
  "dependencies": {
167
- "@happyvertical/smrt-types": "0.57.19",
173
+ "@happyvertical/smrt-types": "0.57.21",
168
174
  "esm-env": "^1.2.2"
169
175
  },
170
176
  "peerDependencies": {
171
177
  "@happyvertical/speech": ">=0.102.5",
178
+ "pdfjs-dist": ">=5.0.0",
172
179
  "svelte": "^5.57.0"
173
180
  },
174
181
  "peerDependenciesMeta": {
175
182
  "@happyvertical/speech": {
176
183
  "optional": true
177
184
  },
185
+ "pdfjs-dist": {
186
+ "optional": true
187
+ },
178
188
  "svelte": {
179
189
  "optional": true
180
190
  }
@@ -188,6 +198,7 @@
188
198
  "@testing-library/user-event": "^14.6.7",
189
199
  "@types/node": "24.13.6",
190
200
  "axe-core": "^4.13.0",
201
+ "pdfjs-dist": "^6.2.108",
191
202
  "svelte": "^5.57.1",
192
203
  "svelte-check": "^4.7.6",
193
204
  "typescript": "6.0.3",