@italia/globals 0.1.0-alpha.1 → 1.0.0-alpha.4
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/custom-elements.json +1655 -168
- package/dist/src/base-component/base-component.d.ts +28 -13
- package/dist/src/base-component/base-component.d.ts.map +1 -1
- package/dist/src/base-component/base-component.js +27 -17
- package/dist/src/base-component/base-component.js.map +1 -1
- package/dist/src/controllers/aria-keyboard-accordion-controller.d.ts +15 -0
- package/dist/src/controllers/aria-keyboard-accordion-controller.d.ts.map +1 -0
- package/dist/src/controllers/aria-keyboard-accordion-controller.js +52 -0
- package/dist/src/controllers/aria-keyboard-accordion-controller.js.map +1 -0
- package/dist/src/controllers/aria-keyboard-list-controller.d.ts +18 -0
- package/dist/src/controllers/aria-keyboard-list-controller.d.ts.map +1 -0
- package/dist/src/controllers/aria-keyboard-list-controller.js +58 -0
- package/dist/src/controllers/aria-keyboard-list-controller.js.map +1 -0
- package/dist/src/controllers/collapse-controller.d.ts +12 -0
- package/dist/src/controllers/collapse-controller.d.ts.map +1 -0
- package/dist/src/controllers/collapse-controller.js +95 -0
- package/dist/src/controllers/collapse-controller.js.map +1 -0
- package/dist/src/controllers/roving-tabindex-controller.d.ts +85 -0
- package/dist/src/controllers/roving-tabindex-controller.d.ts.map +1 -0
- package/dist/src/controllers/roving-tabindex-controller.js +200 -0
- package/dist/src/controllers/roving-tabindex-controller.js.map +1 -0
- package/dist/src/form/form-control.d.ts +61 -0
- package/dist/src/form/form-control.d.ts.map +1 -0
- package/dist/src/form/form-control.js +259 -0
- package/dist/src/form/form-control.js.map +1 -0
- package/dist/src/form/form-controller.d.ts +68 -0
- package/dist/src/form/form-controller.d.ts.map +1 -0
- package/dist/src/form/form-controller.js +398 -0
- package/dist/src/form/form-controller.js.map +1 -0
- package/dist/src/form/locales/it.d.ts +4 -0
- package/dist/src/form/locales/it.d.ts.map +1 -0
- package/dist/src/form/locales/it.js +12 -0
- package/dist/src/form/locales/it.js.map +1 -0
- package/dist/src/index.d.ts +20 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +18 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/mixins/validity.d.ts +19 -19
- package/dist/src/mixins/validity.d.ts.map +1 -1
- package/dist/src/mixins/validity.js +6 -68
- package/dist/src/mixins/validity.js.map +1 -1
- package/dist/src/stories/formControlReusableStories.d.ts +19 -0
- package/dist/src/stories/formControlReusableStories.d.ts.map +1 -0
- package/dist/src/stories/formControlReusableStories.js +64 -0
- package/dist/src/stories/formControlReusableStories.js.map +1 -0
- package/dist/src/stories/reusableUsageGuidelinesStories.d.ts +13 -0
- package/dist/src/stories/reusableUsageGuidelinesStories.d.ts.map +1 -0
- package/dist/src/stories/reusableUsageGuidelinesStories.js +39 -0
- package/dist/src/stories/reusableUsageGuidelinesStories.js.map +1 -0
- package/dist/src/window-manager.d.ts +20 -0
- package/dist/src/window-manager.d.ts.map +1 -0
- package/dist/src/window-manager.js +47 -0
- package/dist/src/window-manager.js.map +1 -0
- package/package.json +10 -8
- package/dist/src/mixins/form.d.ts +0 -363
- package/dist/src/mixins/form.d.ts.map +0 -1
- package/dist/src/mixins/form.js +0 -36
- package/dist/src/mixins/form.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roving-tabindex-controller.d.ts","sourceRoot":"","sources":["../../../src/controllers/roving-tabindex-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,KAAK,CAAC;AAEjE,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,WAAW;IACzD;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;IAEpB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,GAAG,YAAY,GAAG,UAAU,KAAK,IAAI,CAAC;IAE/E;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;IAE/C;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,wBAAwB,CAAC,CAAC,SAAS,WAAW,CAAE,YAAW,kBAAkB;IACxF,OAAO,CAAC,IAAI,CAAyB;IAErC,OAAO,CAAC,MAAM,CAKZ;gBAEU,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;IAazE,aAAa,IAAI,IAAI;IAKrB,gBAAgB,IAAI,IAAI;IAIxB;;;OAGG;IACH,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IA8B5C;;;;;OAKG;IACH,aAAa,CAAC,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO;IAyE5D;;OAEG;IACH,OAAO,CAAC,YAAY;IAmBpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAkB1B;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAUxB;;OAEG;IACH,UAAU,IAAI,IAAI;IASlB;;OAEG;IACH,SAAS,IAAI,IAAI;CAQlB"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roving Tabindex Controller
|
|
3
|
+
*
|
|
4
|
+
* Implements the ARIA roving tabindex pattern for keyboard navigation.
|
|
5
|
+
* See: https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```ts
|
|
9
|
+
* private rovingTabindex = new RovingTabindexController(this, {
|
|
10
|
+
* getItems: () => Array.from(this.querySelectorAll('my-item')),
|
|
11
|
+
* onSelect: (item) => this.selectItem(item),
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export class RovingTabindexController {
|
|
16
|
+
constructor(host, config) {
|
|
17
|
+
this.host = host;
|
|
18
|
+
this.config = {
|
|
19
|
+
wrap: true,
|
|
20
|
+
direction: 'both',
|
|
21
|
+
selectOnFocus: false,
|
|
22
|
+
skipItem: (item) => item.hasAttribute('disabled') || item.disabled === true,
|
|
23
|
+
...config,
|
|
24
|
+
};
|
|
25
|
+
host.addController(this);
|
|
26
|
+
}
|
|
27
|
+
// eslint-disable-next-line class-methods-use-this
|
|
28
|
+
hostConnected() {
|
|
29
|
+
// Controller is ready when host connects
|
|
30
|
+
}
|
|
31
|
+
// eslint-disable-next-line class-methods-use-this
|
|
32
|
+
hostDisconnected() {
|
|
33
|
+
// Cleanup if needed
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Update tabindex values for all items
|
|
37
|
+
* @param activeIndex - Index of the item that should be tabbable (default: 0 or first non-disabled)
|
|
38
|
+
*/
|
|
39
|
+
updateTabindices(activeIndex) {
|
|
40
|
+
const items = this.config.getItems();
|
|
41
|
+
if (!items || items.length === 0) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Find the active index
|
|
45
|
+
let targetIndex = activeIndex ?? 0;
|
|
46
|
+
// If no active index specified, use first non-disabled item
|
|
47
|
+
if (activeIndex === undefined) {
|
|
48
|
+
targetIndex = items.findIndex((item) => !this.config.skipItem(item));
|
|
49
|
+
if (targetIndex === -1) {
|
|
50
|
+
targetIndex = 0; // Fallback to first item if all disabled
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Set tabindex for all items
|
|
54
|
+
items.forEach((item, index) => {
|
|
55
|
+
const itemElement = item;
|
|
56
|
+
if (this.config.skipItem(itemElement)) {
|
|
57
|
+
// eslint-disable-next-line no-param-reassign
|
|
58
|
+
itemElement.tabIndex = -1;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// eslint-disable-next-line no-param-reassign
|
|
62
|
+
itemElement.tabIndex = index === targetIndex ? 0 : -1;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Handle keyboard navigation
|
|
68
|
+
* @param currentItem - The currently focused item
|
|
69
|
+
* @param event - The keyboard event
|
|
70
|
+
* @returns true if the event was handled, false otherwise
|
|
71
|
+
*/
|
|
72
|
+
handleKeydown(currentItem, event) {
|
|
73
|
+
const { direction } = this.config;
|
|
74
|
+
const { key } = event;
|
|
75
|
+
// Determine if this key should be handled based on direction
|
|
76
|
+
const isVertical = key === 'ArrowUp' || key === 'ArrowDown';
|
|
77
|
+
const isHorizontal = key === 'ArrowLeft' || key === 'ArrowRight';
|
|
78
|
+
const isHome = key === 'Home';
|
|
79
|
+
const isEnd = key === 'End';
|
|
80
|
+
const shouldHandle = isHome ||
|
|
81
|
+
isEnd ||
|
|
82
|
+
(direction === 'both' && (isVertical || isHorizontal)) ||
|
|
83
|
+
(direction === 'vertical' && isVertical) ||
|
|
84
|
+
(direction === 'horizontal' && isHorizontal);
|
|
85
|
+
if (!shouldHandle) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
// Prevent default behavior (page scrolling)
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
const items = this.config.getItems();
|
|
91
|
+
const currentIndex = items.indexOf(currentItem);
|
|
92
|
+
if (currentIndex === -1) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
let nextIndex = currentIndex;
|
|
96
|
+
// Handle Home/End keys
|
|
97
|
+
if (isHome) {
|
|
98
|
+
nextIndex = 0;
|
|
99
|
+
}
|
|
100
|
+
else if (isEnd) {
|
|
101
|
+
nextIndex = items.length - 1;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Handle arrow keys
|
|
105
|
+
const isNext = key === 'ArrowDown' || key === 'ArrowRight';
|
|
106
|
+
const isPrev = key === 'ArrowUp' || key === 'ArrowLeft';
|
|
107
|
+
if (isNext) {
|
|
108
|
+
nextIndex = this.getNextIndex(items, currentIndex, 1);
|
|
109
|
+
}
|
|
110
|
+
else if (isPrev) {
|
|
111
|
+
nextIndex = this.getNextIndex(items, currentIndex, -1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Skip disabled items
|
|
115
|
+
nextIndex = this.findNextValidIndex(items, nextIndex, nextIndex > currentIndex ? 1 : -1);
|
|
116
|
+
if (nextIndex !== -1 && nextIndex !== currentIndex) {
|
|
117
|
+
const nextItem = items[nextIndex];
|
|
118
|
+
// Update tabindices
|
|
119
|
+
this.updateTabindices(nextIndex);
|
|
120
|
+
// Focus the next item
|
|
121
|
+
nextItem.focus();
|
|
122
|
+
// Optionally select/activate the item
|
|
123
|
+
if (this.config.selectOnFocus && this.config.onSelect) {
|
|
124
|
+
this.config.onSelect(nextItem, event);
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get the next index based on direction
|
|
132
|
+
*/
|
|
133
|
+
getNextIndex(items, currentIndex, direction) {
|
|
134
|
+
const { wrap } = this.config;
|
|
135
|
+
let nextIndex = currentIndex + direction;
|
|
136
|
+
if (wrap) {
|
|
137
|
+
// Wrap around
|
|
138
|
+
if (nextIndex < 0) {
|
|
139
|
+
nextIndex = items.length - 1;
|
|
140
|
+
}
|
|
141
|
+
else if (nextIndex >= items.length) {
|
|
142
|
+
nextIndex = 0;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// Clamp to bounds
|
|
147
|
+
nextIndex = Math.max(0, Math.min(items.length - 1, nextIndex));
|
|
148
|
+
}
|
|
149
|
+
return nextIndex;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Find the next valid (non-disabled) index
|
|
153
|
+
*/
|
|
154
|
+
findNextValidIndex(items, startIndex, direction) {
|
|
155
|
+
const maxAttempts = items.length;
|
|
156
|
+
let attempts = 0;
|
|
157
|
+
let index = startIndex;
|
|
158
|
+
while (attempts < maxAttempts) {
|
|
159
|
+
if (!this.config.skipItem(items[index])) {
|
|
160
|
+
return index;
|
|
161
|
+
}
|
|
162
|
+
index = this.getNextIndex(items, index, direction);
|
|
163
|
+
attempts += 1;
|
|
164
|
+
}
|
|
165
|
+
// All items are disabled
|
|
166
|
+
return -1;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Set focus to a specific item
|
|
170
|
+
*/
|
|
171
|
+
focusItem(item) {
|
|
172
|
+
const items = this.config.getItems();
|
|
173
|
+
const index = items.indexOf(item);
|
|
174
|
+
if (index !== -1) {
|
|
175
|
+
this.updateTabindices(index);
|
|
176
|
+
item.focus();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Set focus to the first non-disabled item
|
|
181
|
+
*/
|
|
182
|
+
focusFirst() {
|
|
183
|
+
const items = this.config.getItems();
|
|
184
|
+
const firstValidIndex = this.findNextValidIndex(items, 0, 1);
|
|
185
|
+
if (firstValidIndex !== -1) {
|
|
186
|
+
this.focusItem(items[firstValidIndex]);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Set focus to the last non-disabled item
|
|
191
|
+
*/
|
|
192
|
+
focusLast() {
|
|
193
|
+
const items = this.config.getItems();
|
|
194
|
+
const lastValidIndex = this.findNextValidIndex(items, items.length - 1, -1);
|
|
195
|
+
if (lastValidIndex !== -1) {
|
|
196
|
+
this.focusItem(items[lastValidIndex]);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=roving-tabindex-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roving-tabindex-controller.js","sourceRoot":"","sources":["../../../src/controllers/roving-tabindex-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import { ReactiveController, ReactiveControllerHost } from 'lit';\n\nexport interface RovingTabindexConfig<T extends HTMLElement> {\n /**\n * Selector or function to get the items to manage\n */\n getItems: () => T[];\n\n /**\n * Callback when an item is selected/activated\n */\n onSelect?: (item: T, event: KeyboardEvent | PointerEvent | MouseEvent) => void;\n\n /**\n * Whether items wrap around (last -> first and vice versa)\n * @default true\n */\n wrap?: boolean;\n\n /**\n * Direction of navigation\n * @default 'both'\n */\n direction?: 'horizontal' | 'vertical' | 'both';\n\n /**\n * Whether to select items on focus (vs just focusing)\n * @default false\n */\n selectOnFocus?: boolean;\n\n /**\n * Custom logic to determine if an item should be skipped\n * @default (item) => item.hasAttribute('disabled')\n */\n skipItem?: (item: T) => boolean;\n}\n\n/**\n * Roving Tabindex Controller\n *\n * Implements the ARIA roving tabindex pattern for keyboard navigation.\n * See: https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex\n *\n * Usage:\n * ```ts\n * private rovingTabindex = new RovingTabindexController(this, {\n * getItems: () => Array.from(this.querySelectorAll('my-item')),\n * onSelect: (item) => this.selectItem(item),\n * });\n * ```\n */\nexport class RovingTabindexController<T extends HTMLElement> implements ReactiveController {\n private host: ReactiveControllerHost;\n\n private config: RovingTabindexConfig<T> & {\n wrap: boolean;\n direction: 'horizontal' | 'vertical' | 'both';\n selectOnFocus: boolean;\n skipItem: (item: T) => boolean;\n };\n\n constructor(host: ReactiveControllerHost, config: RovingTabindexConfig<T>) {\n this.host = host;\n this.config = {\n wrap: true,\n direction: 'both',\n selectOnFocus: false,\n skipItem: (item) => item.hasAttribute('disabled') || (item as any).disabled === true,\n ...config,\n };\n host.addController(this);\n }\n\n // eslint-disable-next-line class-methods-use-this\n hostConnected(): void {\n // Controller is ready when host connects\n }\n\n // eslint-disable-next-line class-methods-use-this\n hostDisconnected(): void {\n // Cleanup if needed\n }\n\n /**\n * Update tabindex values for all items\n * @param activeIndex - Index of the item that should be tabbable (default: 0 or first non-disabled)\n */\n updateTabindices(activeIndex?: number): void {\n const items = this.config.getItems();\n if (!items || items.length === 0) {\n return;\n }\n\n // Find the active index\n let targetIndex = activeIndex ?? 0;\n\n // If no active index specified, use first non-disabled item\n if (activeIndex === undefined) {\n targetIndex = items.findIndex((item) => !this.config.skipItem(item));\n if (targetIndex === -1) {\n targetIndex = 0; // Fallback to first item if all disabled\n }\n }\n\n // Set tabindex for all items\n items.forEach((item, index) => {\n const itemElement = item;\n if (this.config.skipItem(itemElement)) {\n // eslint-disable-next-line no-param-reassign\n itemElement.tabIndex = -1;\n } else {\n // eslint-disable-next-line no-param-reassign\n itemElement.tabIndex = index === targetIndex ? 0 : -1;\n }\n });\n }\n\n /**\n * Handle keyboard navigation\n * @param currentItem - The currently focused item\n * @param event - The keyboard event\n * @returns true if the event was handled, false otherwise\n */\n handleKeydown(currentItem: T, event: KeyboardEvent): boolean {\n const { direction } = this.config;\n const { key } = event;\n\n // Determine if this key should be handled based on direction\n const isVertical = key === 'ArrowUp' || key === 'ArrowDown';\n const isHorizontal = key === 'ArrowLeft' || key === 'ArrowRight';\n const isHome = key === 'Home';\n const isEnd = key === 'End';\n\n const shouldHandle =\n isHome ||\n isEnd ||\n (direction === 'both' && (isVertical || isHorizontal)) ||\n (direction === 'vertical' && isVertical) ||\n (direction === 'horizontal' && isHorizontal);\n\n if (!shouldHandle) {\n return false;\n }\n\n // Prevent default behavior (page scrolling)\n event.preventDefault();\n\n const items = this.config.getItems();\n const currentIndex = items.indexOf(currentItem);\n\n if (currentIndex === -1) {\n return false;\n }\n\n let nextIndex = currentIndex;\n\n // Handle Home/End keys\n if (isHome) {\n nextIndex = 0;\n } else if (isEnd) {\n nextIndex = items.length - 1;\n } else {\n // Handle arrow keys\n const isNext = key === 'ArrowDown' || key === 'ArrowRight';\n const isPrev = key === 'ArrowUp' || key === 'ArrowLeft';\n\n if (isNext) {\n nextIndex = this.getNextIndex(items, currentIndex, 1);\n } else if (isPrev) {\n nextIndex = this.getNextIndex(items, currentIndex, -1);\n }\n }\n\n // Skip disabled items\n nextIndex = this.findNextValidIndex(items, nextIndex, nextIndex > currentIndex ? 1 : -1);\n\n if (nextIndex !== -1 && nextIndex !== currentIndex) {\n const nextItem = items[nextIndex];\n\n // Update tabindices\n this.updateTabindices(nextIndex);\n\n // Focus the next item\n nextItem.focus();\n\n // Optionally select/activate the item\n if (this.config.selectOnFocus && this.config.onSelect) {\n this.config.onSelect(nextItem, event);\n }\n\n return true;\n }\n\n return false;\n }\n\n /**\n * Get the next index based on direction\n */\n private getNextIndex(items: T[], currentIndex: number, direction: 1 | -1): number {\n const { wrap } = this.config;\n let nextIndex = currentIndex + direction;\n\n if (wrap) {\n // Wrap around\n if (nextIndex < 0) {\n nextIndex = items.length - 1;\n } else if (nextIndex >= items.length) {\n nextIndex = 0;\n }\n } else {\n // Clamp to bounds\n nextIndex = Math.max(0, Math.min(items.length - 1, nextIndex));\n }\n\n return nextIndex;\n }\n\n /**\n * Find the next valid (non-disabled) index\n */\n private findNextValidIndex(items: T[], startIndex: number, direction: 1 | -1): number {\n const maxAttempts = items.length;\n let attempts = 0;\n let index = startIndex;\n\n while (attempts < maxAttempts) {\n if (!this.config.skipItem(items[index])) {\n return index;\n }\n\n index = this.getNextIndex(items, index, direction);\n attempts += 1;\n }\n\n // All items are disabled\n return -1;\n }\n\n /**\n * Set focus to a specific item\n */\n focusItem(item: T): void {\n const items = this.config.getItems();\n const index = items.indexOf(item);\n\n if (index !== -1) {\n this.updateTabindices(index);\n item.focus();\n }\n }\n\n /**\n * Set focus to the first non-disabled item\n */\n focusFirst(): void {\n const items = this.config.getItems();\n const firstValidIndex = this.findNextValidIndex(items, 0, 1);\n\n if (firstValidIndex !== -1) {\n this.focusItem(items[firstValidIndex]);\n }\n }\n\n /**\n * Set focus to the last non-disabled item\n */\n focusLast(): void {\n const items = this.config.getItems();\n const lastValidIndex = this.findNextValidIndex(items, items.length - 1, -1);\n\n if (lastValidIndex !== -1) {\n this.focusItem(items[lastValidIndex]);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { BaseLocalizedComponent } from '../base-component/base-component.js';
|
|
2
|
+
import { FormControlController } from './form-controller.js';
|
|
3
|
+
export declare class FormControl extends BaseLocalizedComponent {
|
|
4
|
+
protected readonly formControlController: FormControlController;
|
|
5
|
+
_touched: boolean;
|
|
6
|
+
inputElement: HTMLInputElement;
|
|
7
|
+
/** The name of the input, submitted as a name/value pair with form data. */
|
|
8
|
+
name: string;
|
|
9
|
+
/** The current value of the input, submitted as a name/value pair with form data. */
|
|
10
|
+
value: string;
|
|
11
|
+
/** If the input is disabled. */
|
|
12
|
+
disabled: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
|
|
15
|
+
* to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
|
|
16
|
+
* the same document or shadow root for this to work.
|
|
17
|
+
*/
|
|
18
|
+
form: string;
|
|
19
|
+
/** If you implement your custom validation and you won't to trigger default validation */
|
|
20
|
+
customValidation: boolean;
|
|
21
|
+
/** If your input is invalid from your custom validition, set this attribute with message validation */
|
|
22
|
+
validationText: string;
|
|
23
|
+
/** Pattern the `value` must match to be valid */
|
|
24
|
+
pattern?: string;
|
|
25
|
+
/** The input's minimum value. Only applies to date and number input types. */
|
|
26
|
+
min: number | string | Date | undefined;
|
|
27
|
+
/** The input's maximum value. Only applies to date and number input types. */
|
|
28
|
+
max: number | string | Date | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Specifies the granularity that the value must adhere to, or the special value `any` which means no stepping is
|
|
31
|
+
* implied, allowing any numeric value. Only applies to date and number input types.
|
|
32
|
+
*/
|
|
33
|
+
step: number | 'any';
|
|
34
|
+
/** The input's minimum length. */
|
|
35
|
+
minlength: number;
|
|
36
|
+
/** The input's maximum length. */
|
|
37
|
+
maxlength: number;
|
|
38
|
+
/** If the input is required. */
|
|
39
|
+
required: boolean;
|
|
40
|
+
protected isInGroup: boolean;
|
|
41
|
+
/** Gets the validity state object */
|
|
42
|
+
get validity(): ValidityState;
|
|
43
|
+
validationMessage: string;
|
|
44
|
+
/** Gets the associated form, if one exists. */
|
|
45
|
+
getForm(): HTMLFormElement | null;
|
|
46
|
+
checkValidity(): boolean;
|
|
47
|
+
/** Checks for validity and shows the browser's validation message if the control is invalid. */
|
|
48
|
+
reportValidity(): boolean;
|
|
49
|
+
/** Sets a custom validation message. Pass an empty string to restore validity. */
|
|
50
|
+
setCustomValidity(message: string): void;
|
|
51
|
+
protected _handleReady(): void;
|
|
52
|
+
protected _handleInput(e: Event): void;
|
|
53
|
+
protected _handleBlur(e: Event): void;
|
|
54
|
+
protected _handleFocus(e: Event): void;
|
|
55
|
+
protected _handleClick(e: Event): void;
|
|
56
|
+
protected handleValidationMessages(): void;
|
|
57
|
+
protected _handleInvalid(event: Event): void;
|
|
58
|
+
protected _handleChange(e: Event): void;
|
|
59
|
+
updated(changedProperties: Map<string | number | symbol, unknown>): void;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=form-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-control.d.ts","sourceRoot":"","sources":["../../../src/form/form-control.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAK7D,qBAAa,WAAY,SAAQ,sBAAsB;IACrD,SAAS,CAAC,QAAQ,CAAC,qBAAqB,wBAErC;IASH,QAAQ,UAAS;IAGjB,YAAY,EAAG,gBAAgB,CAAC;IAEhC,4EAA4E;IAE5E,IAAI,SAAM;IAEV,qFAAqF;IAErF,KAAK,SAAM;IAEX,gCAAgC;IAEhC,QAAQ,UAAS;IAEjB;;;;OAIG;IACwC,IAAI,SAAM;IAErD,0FAA0F;IAE1F,gBAAgB,UAAS;IAEzB,uGAAuG;IAEvG,cAAc,EAAE,MAAM,CAAM;IAE5B,iDAAiD;IAEjD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,8EAA8E;IAE9E,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAExC,8EAA8E;IAE9E,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAExC;;;OAGG;IAEH,IAAI,EAAE,MAAM,GAAG,KAAK,CAAS;IAE7B,kCAAkC;IAElC,SAAS,SAAM;IAEf,kCAAkC;IAElC,SAAS,SAAM;IAEf,gCAAgC;IAEhC,QAAQ,UAAS;IAIjB,SAAS,CAAC,SAAS,UAAS;IAE5B,qCAAqC;IACrC,IAAW,QAAQ,IAAI,aAAa,CAEnC;IAGM,iBAAiB,SAAM;IAE9B,+CAA+C;IACxC,OAAO,IAAI,eAAe,GAAG,IAAI;IAKjC,aAAa,IAAI,OAAO;IAK/B,gGAAgG;IACzF,cAAc;IAOrB,kFAAkF;IAC3E,iBAAiB,CAAC,OAAO,EAAE,MAAM;IAOxC,SAAS,CAAC,YAAY;IAOtB,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK;IAY/B,SAAS,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK;IAO9B,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK;IAK/B,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK;IAO/B,SAAS,CAAC,wBAAwB;IAmClC,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK;IAKrC,SAAS,CAAC,aAAa,CAAC,CAAC,EAAE,KAAK;IAoCvB,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC;CAS3E"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import { property, query, state } from 'lit/decorators.js';
|
|
3
|
+
import { registerTranslation } from '@italia/i18n';
|
|
4
|
+
import { BaseLocalizedComponent } from '../base-component/base-component.js';
|
|
5
|
+
import { FormControlController } from './form-controller.js';
|
|
6
|
+
import it from './locales/it.js';
|
|
7
|
+
registerTranslation(it);
|
|
8
|
+
export class FormControl extends BaseLocalizedComponent {
|
|
9
|
+
constructor() {
|
|
10
|
+
super(...arguments);
|
|
11
|
+
this.formControlController = new FormControlController(this, {
|
|
12
|
+
assumeInteractionOn: ['it-input', 'it-blur', 'it-change'],
|
|
13
|
+
});
|
|
14
|
+
// TODO: verificare se serve davvero con il fatto che usiamo form-controller
|
|
15
|
+
// static formAssociated = true;
|
|
16
|
+
// @property()
|
|
17
|
+
// internals = this.attachInternals();
|
|
18
|
+
this._touched = false;
|
|
19
|
+
/** The name of the input, submitted as a name/value pair with form data. */
|
|
20
|
+
this.name = '';
|
|
21
|
+
/** The current value of the input, submitted as a name/value pair with form data. */
|
|
22
|
+
this.value = '';
|
|
23
|
+
/** If the input is disabled. */
|
|
24
|
+
this.disabled = false;
|
|
25
|
+
/**
|
|
26
|
+
* By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
|
|
27
|
+
* to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
|
|
28
|
+
* the same document or shadow root for this to work.
|
|
29
|
+
*/
|
|
30
|
+
this.form = '';
|
|
31
|
+
/** If you implement your custom validation and you won't to trigger default validation */
|
|
32
|
+
this.customValidation = false;
|
|
33
|
+
/** If your input is invalid from your custom validition, set this attribute with message validation */
|
|
34
|
+
this.validationText = '';
|
|
35
|
+
/**
|
|
36
|
+
* Specifies the granularity that the value must adhere to, or the special value `any` which means no stepping is
|
|
37
|
+
* implied, allowing any numeric value. Only applies to date and number input types.
|
|
38
|
+
*/
|
|
39
|
+
this.step = 'any';
|
|
40
|
+
/** The input's minimum length. */
|
|
41
|
+
this.minlength = -1;
|
|
42
|
+
/** The input's maximum length. */
|
|
43
|
+
this.maxlength = -1;
|
|
44
|
+
/** If the input is required. */
|
|
45
|
+
this.required = false;
|
|
46
|
+
/* For grouped input, like checkbox-group */
|
|
47
|
+
this.isInGroup = false;
|
|
48
|
+
this.validationMessage = '';
|
|
49
|
+
}
|
|
50
|
+
/** Gets the validity state object */
|
|
51
|
+
get validity() {
|
|
52
|
+
return this.inputElement?.validity;
|
|
53
|
+
}
|
|
54
|
+
/** Gets the associated form, if one exists. */
|
|
55
|
+
getForm() {
|
|
56
|
+
return this.formControlController.getForm();
|
|
57
|
+
}
|
|
58
|
+
// Form validation methods
|
|
59
|
+
checkValidity() {
|
|
60
|
+
const inputValid = this.inputElement?.checkValidity() ?? true; // this.inputElement.checkValidity() è la validazione del browser
|
|
61
|
+
return inputValid;
|
|
62
|
+
}
|
|
63
|
+
/** Checks for validity and shows the browser's validation message if the control is invalid. */
|
|
64
|
+
reportValidity() {
|
|
65
|
+
// const ret = this.inputElement.checkValidity();
|
|
66
|
+
const ret = this.checkValidity(); // chiama la checkValidity, che se è stata overridata chiama quella
|
|
67
|
+
this.handleValidationMessages();
|
|
68
|
+
return ret; // this.inputElement.reportValidity();
|
|
69
|
+
}
|
|
70
|
+
/** Sets a custom validation message. Pass an empty string to restore validity. */
|
|
71
|
+
setCustomValidity(message) {
|
|
72
|
+
this.inputElement.setCustomValidity(message);
|
|
73
|
+
this.validationMessage = this.inputElement.validationMessage;
|
|
74
|
+
this.formControlController.updateValidity();
|
|
75
|
+
}
|
|
76
|
+
// Handlers
|
|
77
|
+
_handleReady() {
|
|
78
|
+
requestAnimationFrame(() => {
|
|
79
|
+
this.dispatchEvent(new CustomEvent('it-input-ready', { bubbles: true, detail: { el: this.inputElement } }));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
83
|
+
_handleInput(e) {
|
|
84
|
+
this.handleValidationMessages();
|
|
85
|
+
this.dispatchEvent(new CustomEvent('it-input', {
|
|
86
|
+
detail: { value: this.inputElement.value, el: this.inputElement },
|
|
87
|
+
bubbles: true,
|
|
88
|
+
composed: true,
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
92
|
+
_handleBlur(e) {
|
|
93
|
+
this._touched = true;
|
|
94
|
+
this.handleValidationMessages();
|
|
95
|
+
this.dispatchEvent(new FocusEvent('it-blur', { bubbles: true, composed: true }));
|
|
96
|
+
}
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
98
|
+
_handleFocus(e) {
|
|
99
|
+
this.dispatchEvent(new FocusEvent('it-focus', { bubbles: true, composed: true }));
|
|
100
|
+
}
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
102
|
+
_handleClick(e) {
|
|
103
|
+
this.dispatchEvent(new MouseEvent('it-click', { bubbles: true, composed: true }));
|
|
104
|
+
}
|
|
105
|
+
/*
|
|
106
|
+
Override default browser validation messages
|
|
107
|
+
*/
|
|
108
|
+
handleValidationMessages() {
|
|
109
|
+
if (!this.customValidation) {
|
|
110
|
+
const _v = this.inputElement.validity;
|
|
111
|
+
const isRequiredHandledByGroup = this.isInGroup === true;
|
|
112
|
+
if (_v.valueMissing && !isRequiredHandledByGroup) {
|
|
113
|
+
this.setCustomValidity(this.$t('validityRequired'));
|
|
114
|
+
}
|
|
115
|
+
else if (_v.patternMismatch) {
|
|
116
|
+
this.setCustomValidity(this.$t('validityPattern'));
|
|
117
|
+
}
|
|
118
|
+
else if (_v.tooShort) {
|
|
119
|
+
this.setCustomValidity(this.$t('validityMinlength').replace('{minlength}', this.minlength.toString()));
|
|
120
|
+
}
|
|
121
|
+
else if (_v.tooLong) {
|
|
122
|
+
this.setCustomValidity(this.$t('validityMaxlength').replace('{maxlength}', this.maxlength.toString()));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
/* nothing. Usa il messaggio di errore della validazione
|
|
126
|
+
di default del browser per altri errori di validità come:
|
|
127
|
+
- typeMismatch
|
|
128
|
+
- rangeUnderflow
|
|
129
|
+
- rangeOverflow
|
|
130
|
+
- stepMismatch
|
|
131
|
+
- badInput */
|
|
132
|
+
const otherConstraintErrors = _v.typeMismatch || _v.rangeUnderflow || _v.rangeOverflow || _v.stepMismatch || _v.badInput;
|
|
133
|
+
if (!otherConstraintErrors) {
|
|
134
|
+
this.setCustomValidity('');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this.validationMessage = this.inputElement.validationMessage;
|
|
139
|
+
}
|
|
140
|
+
_handleInvalid(event) {
|
|
141
|
+
this.formControlController.setValidity(false);
|
|
142
|
+
this.formControlController.emitInvalidEvent(event);
|
|
143
|
+
}
|
|
144
|
+
_handleChange(e) {
|
|
145
|
+
const target = e.target;
|
|
146
|
+
let value;
|
|
147
|
+
if (target instanceof HTMLInputElement) {
|
|
148
|
+
switch (target.type) {
|
|
149
|
+
case 'checkbox':
|
|
150
|
+
case 'radio':
|
|
151
|
+
value = target.checked;
|
|
152
|
+
break;
|
|
153
|
+
case 'file':
|
|
154
|
+
value = target.files; // FileList
|
|
155
|
+
break;
|
|
156
|
+
default:
|
|
157
|
+
value = target.value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (target instanceof HTMLSelectElement) {
|
|
161
|
+
if (target.multiple) {
|
|
162
|
+
value = Array.from(target.selectedOptions).map((o) => o.value);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
value = target.value;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// textarea o altri input con value
|
|
170
|
+
value = target.value;
|
|
171
|
+
}
|
|
172
|
+
this.dispatchEvent(new CustomEvent('it-change', {
|
|
173
|
+
detail: { value, el: target },
|
|
174
|
+
bubbles: true,
|
|
175
|
+
composed: true,
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
updated(changedProperties) {
|
|
179
|
+
super.updated?.(changedProperties);
|
|
180
|
+
if (this.customValidation) {
|
|
181
|
+
this.setCustomValidity(this.validationText ?? '');
|
|
182
|
+
}
|
|
183
|
+
else if (this.formControlController.userInteracted()) {
|
|
184
|
+
this.formControlController.updateValidity();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
__decorate([
|
|
189
|
+
state(),
|
|
190
|
+
__metadata("design:type", Object)
|
|
191
|
+
], FormControl.prototype, "_touched", void 0);
|
|
192
|
+
__decorate([
|
|
193
|
+
query('.it-form__control'),
|
|
194
|
+
__metadata("design:type", HTMLInputElement)
|
|
195
|
+
], FormControl.prototype, "inputElement", void 0);
|
|
196
|
+
__decorate([
|
|
197
|
+
property({ type: String, reflect: true }) // from FormControl
|
|
198
|
+
,
|
|
199
|
+
__metadata("design:type", Object)
|
|
200
|
+
], FormControl.prototype, "name", void 0);
|
|
201
|
+
__decorate([
|
|
202
|
+
property({ reflect: true }),
|
|
203
|
+
__metadata("design:type", Object)
|
|
204
|
+
], FormControl.prototype, "value", void 0);
|
|
205
|
+
__decorate([
|
|
206
|
+
property({ type: Boolean, reflect: true }) // from FormControl
|
|
207
|
+
,
|
|
208
|
+
__metadata("design:type", Object)
|
|
209
|
+
], FormControl.prototype, "disabled", void 0);
|
|
210
|
+
__decorate([
|
|
211
|
+
property({ reflect: true, type: String }),
|
|
212
|
+
__metadata("design:type", Object)
|
|
213
|
+
], FormControl.prototype, "form", void 0);
|
|
214
|
+
__decorate([
|
|
215
|
+
property({ type: Boolean, reflect: true, attribute: 'custom-validation' }),
|
|
216
|
+
__metadata("design:type", Object)
|
|
217
|
+
], FormControl.prototype, "customValidation", void 0);
|
|
218
|
+
__decorate([
|
|
219
|
+
property({ attribute: 'validity-message', reflect: true }),
|
|
220
|
+
__metadata("design:type", String)
|
|
221
|
+
], FormControl.prototype, "validationText", void 0);
|
|
222
|
+
__decorate([
|
|
223
|
+
property(),
|
|
224
|
+
__metadata("design:type", String)
|
|
225
|
+
], FormControl.prototype, "pattern", void 0);
|
|
226
|
+
__decorate([
|
|
227
|
+
property(),
|
|
228
|
+
__metadata("design:type", Object)
|
|
229
|
+
], FormControl.prototype, "min", void 0);
|
|
230
|
+
__decorate([
|
|
231
|
+
property(),
|
|
232
|
+
__metadata("design:type", Object)
|
|
233
|
+
], FormControl.prototype, "max", void 0);
|
|
234
|
+
__decorate([
|
|
235
|
+
property(),
|
|
236
|
+
__metadata("design:type", Object)
|
|
237
|
+
], FormControl.prototype, "step", void 0);
|
|
238
|
+
__decorate([
|
|
239
|
+
property({ type: Number }),
|
|
240
|
+
__metadata("design:type", Object)
|
|
241
|
+
], FormControl.prototype, "minlength", void 0);
|
|
242
|
+
__decorate([
|
|
243
|
+
property({ type: Number }),
|
|
244
|
+
__metadata("design:type", Object)
|
|
245
|
+
], FormControl.prototype, "maxlength", void 0);
|
|
246
|
+
__decorate([
|
|
247
|
+
property({ type: Boolean, reflect: true }) // from FormControl
|
|
248
|
+
,
|
|
249
|
+
__metadata("design:type", Object)
|
|
250
|
+
], FormControl.prototype, "required", void 0);
|
|
251
|
+
__decorate([
|
|
252
|
+
property({ type: Boolean }),
|
|
253
|
+
__metadata("design:type", Object)
|
|
254
|
+
], FormControl.prototype, "isInGroup", void 0);
|
|
255
|
+
__decorate([
|
|
256
|
+
state(),
|
|
257
|
+
__metadata("design:type", Object)
|
|
258
|
+
], FormControl.prototype, "validationMessage", void 0);
|
|
259
|
+
//# sourceMappingURL=form-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-control.js","sourceRoot":"","sources":["../../../src/form/form-control.ts"],"names":[],"mappings":"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","sourcesContent":["import { property, query, state } from 'lit/decorators.js';\nimport { registerTranslation } from '@italia/i18n';\nimport { BaseLocalizedComponent } from '../base-component/base-component.js';\nimport { FormControlController } from './form-controller.js';\nimport it from './locales/it.js';\n\nregisterTranslation(it);\n\nexport class FormControl extends BaseLocalizedComponent {\n protected readonly formControlController = new FormControlController(this, {\n assumeInteractionOn: ['it-input', 'it-blur', 'it-change'],\n });\n\n // TODO: verificare se serve davvero con il fatto che usiamo form-controller\n // static formAssociated = true;\n\n // @property()\n // internals = this.attachInternals();\n\n @state()\n _touched = false;\n\n @query('.it-form__control')\n inputElement!: HTMLInputElement; // from FormControl\n\n /** The name of the input, submitted as a name/value pair with form data. */\n @property({ type: String, reflect: true }) // from FormControl\n name = '';\n\n /** The current value of the input, submitted as a name/value pair with form data. */\n @property({ reflect: true })\n value = '';\n\n /** If the input is disabled. */\n @property({ type: Boolean, reflect: true }) // from FormControl\n disabled = false;\n\n /**\n * By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you\n * to place the form control outside of a form and associate it with the form that has this `id`. The form must be in\n * the same document or shadow root for this to work.\n */\n @property({ reflect: true, type: String }) form = '';\n\n /** If you implement your custom validation and you won't to trigger default validation */\n @property({ type: Boolean, reflect: true, attribute: 'custom-validation' })\n customValidation = false;\n\n /** If your input is invalid from your custom validition, set this attribute with message validation */\n @property({ attribute: 'validity-message', reflect: true })\n validationText: string = '';\n\n /** Pattern the `value` must match to be valid */\n @property()\n pattern?: string;\n\n /** The input's minimum value. Only applies to date and number input types. */\n @property()\n min: number | string | Date | undefined;\n\n /** The input's maximum value. Only applies to date and number input types. */\n @property()\n max: number | string | Date | undefined;\n\n /**\n * Specifies the granularity that the value must adhere to, or the special value `any` which means no stepping is\n * implied, allowing any numeric value. Only applies to date and number input types.\n */\n @property()\n step: number | 'any' = 'any';\n\n /** The input's minimum length. */\n @property({ type: Number })\n minlength = -1;\n\n /** The input's maximum length. */\n @property({ type: Number })\n maxlength = -1;\n\n /** If the input is required. */\n @property({ type: Boolean, reflect: true }) // from FormControl\n required = false;\n\n /* For grouped input, like checkbox-group */\n @property({ type: Boolean })\n protected isInGroup = false;\n\n /** Gets the validity state object */\n public get validity(): ValidityState {\n return this.inputElement?.validity;\n }\n\n @state()\n public validationMessage = '';\n\n /** Gets the associated form, if one exists. */\n public getForm(): HTMLFormElement | null {\n return this.formControlController.getForm();\n }\n\n // Form validation methods\n public checkValidity(): boolean {\n const inputValid = this.inputElement?.checkValidity() ?? true; // this.inputElement.checkValidity() è la validazione del browser\n return inputValid;\n }\n\n /** Checks for validity and shows the browser's validation message if the control is invalid. */\n public reportValidity() {\n // const ret = this.inputElement.checkValidity();\n const ret = this.checkValidity(); // chiama la checkValidity, che se è stata overridata chiama quella\n this.handleValidationMessages();\n return ret; // this.inputElement.reportValidity();\n }\n\n /** Sets a custom validation message. Pass an empty string to restore validity. */\n public setCustomValidity(message: string) {\n this.inputElement.setCustomValidity(message);\n this.validationMessage = this.inputElement.validationMessage;\n this.formControlController.updateValidity();\n }\n\n // Handlers\n protected _handleReady() {\n requestAnimationFrame(() => {\n this.dispatchEvent(new CustomEvent('it-input-ready', { bubbles: true, detail: { el: this.inputElement } }));\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected _handleInput(e: Event) {\n this.handleValidationMessages();\n this.dispatchEvent(\n new CustomEvent('it-input', {\n detail: { value: this.inputElement.value, el: this.inputElement },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected _handleBlur(e: Event) {\n this._touched = true;\n this.handleValidationMessages();\n this.dispatchEvent(new FocusEvent('it-blur', { bubbles: true, composed: true }));\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected _handleFocus(e: Event) {\n this.dispatchEvent(new FocusEvent('it-focus', { bubbles: true, composed: true }));\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected _handleClick(e: Event) {\n this.dispatchEvent(new MouseEvent('it-click', { bubbles: true, composed: true }));\n }\n\n /*\n Override default browser validation messages\n */\n protected handleValidationMessages() {\n if (!this.customValidation) {\n const _v = this.inputElement.validity;\n\n const isRequiredHandledByGroup = this.isInGroup === true;\n\n if (_v.valueMissing && !isRequiredHandledByGroup) {\n this.setCustomValidity(this.$t('validityRequired'));\n } else if (_v.patternMismatch) {\n this.setCustomValidity(this.$t('validityPattern'));\n } else if (_v.tooShort) {\n this.setCustomValidity(this.$t('validityMinlength').replace('{minlength}', this.minlength.toString()));\n } else if (_v.tooLong) {\n this.setCustomValidity(this.$t('validityMaxlength').replace('{maxlength}', this.maxlength.toString()));\n } else {\n /* nothing. Usa il messaggio di errore della validazione\n di default del browser per altri errori di validità come:\n - typeMismatch\n - rangeUnderflow\n - rangeOverflow\n - stepMismatch\n - badInput */\n\n const otherConstraintErrors =\n _v.typeMismatch || _v.rangeUnderflow || _v.rangeOverflow || _v.stepMismatch || _v.badInput;\n\n if (!otherConstraintErrors) {\n this.setCustomValidity('');\n }\n }\n }\n\n this.validationMessage = this.inputElement.validationMessage;\n }\n\n protected _handleInvalid(event: Event) {\n this.formControlController.setValidity(false);\n this.formControlController.emitInvalidEvent(event);\n }\n\n protected _handleChange(e: Event) {\n const target = e.target as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;\n let value: unknown;\n\n if (target instanceof HTMLInputElement) {\n switch (target.type) {\n case 'checkbox':\n case 'radio':\n value = target.checked;\n break;\n case 'file':\n value = target.files; // FileList\n break;\n default:\n value = target.value;\n }\n } else if (target instanceof HTMLSelectElement) {\n if (target.multiple) {\n value = Array.from(target.selectedOptions).map((o) => o.value);\n } else {\n value = target.value;\n }\n } else {\n // textarea o altri input con value\n value = (target as any).value;\n }\n\n this.dispatchEvent(\n new CustomEvent('it-change', {\n detail: { value, el: target },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n override updated(changedProperties: Map<string | number | symbol, unknown>) {\n super.updated?.(changedProperties);\n\n if (this.customValidation) {\n this.setCustomValidity(this.validationText ?? '');\n } else if (this.formControlController.userInteracted()) {\n this.formControlController.updateValidity();\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
import { FormControl } from './form-control.js';
|
|
3
|
+
export declare const formCollections: WeakMap<HTMLFormElement, Set<FormControl>>;
|
|
4
|
+
export interface FormControlControllerOptions {
|
|
5
|
+
/** A function that returns the form containing the form control. */
|
|
6
|
+
form: (input: FormControl) => HTMLFormElement | null;
|
|
7
|
+
/** A function that returns the form control's name, which will be submitted with the form data. */
|
|
8
|
+
name: (input: FormControl) => string;
|
|
9
|
+
/** A function that returns the form control's current value. */
|
|
10
|
+
value: (input: FormControl) => unknown | unknown[];
|
|
11
|
+
/** A function that returns the form control's current disabled state. If disabled, the value won't be submitted. */
|
|
12
|
+
disabled: (input: FormControl) => boolean;
|
|
13
|
+
reportValidity: (input: FormControl) => boolean;
|
|
14
|
+
checkValidity: (input: FormControl) => boolean;
|
|
15
|
+
/** A function that sets the form control's value */
|
|
16
|
+
setValue: (input: FormControl, value: unknown) => void;
|
|
17
|
+
/**
|
|
18
|
+
* An array of event names to listen to. When all events in the list are emitted, the control will receive validity
|
|
19
|
+
* states such as user-valid and user-invalid.user interacted validity states. */
|
|
20
|
+
assumeInteractionOn: string[];
|
|
21
|
+
}
|
|
22
|
+
/** A reactive controller to allow form controls to participate in form submission, validation, etc. */
|
|
23
|
+
export declare class FormControlController implements ReactiveController {
|
|
24
|
+
host: FormControl & ReactiveControllerHost;
|
|
25
|
+
form?: HTMLFormElement | null;
|
|
26
|
+
options: FormControlControllerOptions;
|
|
27
|
+
submittedOnce: boolean;
|
|
28
|
+
constructor(host: ReactiveControllerHost & FormControl, options?: Partial<FormControlControllerOptions>);
|
|
29
|
+
hostConnected(): void;
|
|
30
|
+
hostDisconnected(): void;
|
|
31
|
+
hostUpdated(): void;
|
|
32
|
+
private attachForm;
|
|
33
|
+
private detachForm;
|
|
34
|
+
private handleFormData;
|
|
35
|
+
private handleFormSubmit;
|
|
36
|
+
private handleFormReset;
|
|
37
|
+
private handleInteraction;
|
|
38
|
+
private checkFormValidity;
|
|
39
|
+
private reportFormValidity;
|
|
40
|
+
private setUserInteracted;
|
|
41
|
+
private doAction;
|
|
42
|
+
/** Returns the associated `<form>` element, if one exists. */
|
|
43
|
+
getForm(): HTMLFormElement | null;
|
|
44
|
+
/** Resets the form, restoring all the control to their default value */
|
|
45
|
+
reset(submitter?: HTMLInputElement | any): void;
|
|
46
|
+
/** Submits the form, triggering validation and form data injection. */
|
|
47
|
+
submit(submitter?: HTMLInputElement | any): void;
|
|
48
|
+
/**
|
|
49
|
+
* Synchronously sets the form control's validity. Call this when you know the future validity but need to update
|
|
50
|
+
* the host element immediately, i.e. before Lit updates the component in the next update.
|
|
51
|
+
*/
|
|
52
|
+
setValidity(isValid: boolean): void;
|
|
53
|
+
userInteracted(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Updates the form control's validity based on the current value of `host.validity.valid`. Call this when anything
|
|
56
|
+
* that affects constraint validation changes so the component receives the correct validity states.
|
|
57
|
+
*/
|
|
58
|
+
updateValidity(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Dispatches a non-bubbling, cancelable custom event of type `it-invalid`.
|
|
61
|
+
* If the `it-invalid` event will be cancelled then the original `invalid`
|
|
62
|
+
* event (which may have been passed as argument) will also be cancelled.
|
|
63
|
+
* If no original `invalid` event has been passed then the `it-invalid`
|
|
64
|
+
* event will be cancelled before being dispatched.
|
|
65
|
+
*/
|
|
66
|
+
emitInvalidEvent(originalInvalidEvent?: Event): void;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=form-controller.d.ts.map
|