@winkintel/bootstrap-svelte 1.0.4 → 1.0.6
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 +1 -1
- package/dist/Alert/alert.svelte +4 -2
- package/dist/Badge/badge.svelte +6 -4
- package/dist/Badge/badge.svelte.d.ts +2 -2
- package/dist/Badge/types.d.ts +2 -1
- package/dist/Badge/types.js +3 -11
- package/dist/Button/button.svelte +29 -9
- package/dist/ButtonGroup/button-group.svelte +3 -2
- package/dist/ButtonGroup/button-toolbar.svelte +3 -2
- package/dist/ButtonGroup/types.contract.d.ts +1 -0
- package/dist/ButtonGroup/types.contract.js +15 -0
- package/dist/ButtonGroup/types.d.ts +2 -2
- package/dist/Carousel/carousel-indicator-button.svelte +8 -1
- package/dist/Carousel/carousel-item.svelte +29 -9
- package/dist/Carousel/carousel.svelte +12 -5
- package/dist/Carousel/carousel.svelte.js +200 -42
- package/dist/Col/col.svelte +30 -22
- package/dist/Collapse/collapse.svelte +3 -3
- package/dist/Collapse/transition.js +78 -15
- package/dist/Collapse/types.d.ts +1 -0
- package/dist/Dropdown/dropdown-item.svelte +7 -0
- package/dist/Dropdown/dropdown.svelte.js +66 -19
- package/dist/Form/form-check-input.svelte +6 -6
- package/dist/Form/form-color-input.svelte +8 -3
- package/dist/Form/form-date-input.svelte +4 -4
- package/dist/Form/form-datetime-local-input.svelte +4 -4
- package/dist/Form/form-email-input.svelte +4 -4
- package/dist/Form/form-file-input.svelte +46 -6
- package/dist/Form/form-file-input.svelte.d.ts +3 -2
- package/dist/Form/form-hidden-input.svelte +1 -1
- package/dist/Form/form-hidden-input.svelte.d.ts +1 -1
- package/dist/Form/form-image-input.svelte +1 -1
- package/dist/Form/form-image-input.svelte.d.ts +2 -1
- package/dist/Form/form-month-input.svelte +4 -4
- package/dist/Form/form-number-input.svelte +4 -4
- package/dist/Form/form-password-input.svelte +4 -4
- package/dist/Form/form-radio-input.svelte +53 -3
- package/dist/Form/form-radio-input.svelte.d.ts +3 -2
- package/dist/Form/form-range-input.svelte +1 -1
- package/dist/Form/form-range-input.svelte.d.ts +2 -1
- package/dist/Form/form-search-input.svelte +4 -4
- package/dist/Form/form-select.svelte +7 -7
- package/dist/Form/form-switch-input.svelte +4 -4
- package/dist/Form/form-telephone-input.svelte +4 -4
- package/dist/Form/form-text-area.svelte +4 -4
- package/dist/Form/form-text-input.svelte +4 -4
- package/dist/Form/form-time-input.svelte +4 -4
- package/dist/Form/form-url-input.svelte +4 -4
- package/dist/Form/form-week-Input.svelte +4 -4
- package/dist/Form/types.d.ts +26 -20
- package/dist/ListGroup/list-group-item-action.svelte +14 -7
- package/dist/Modal/modal-title.svelte +7 -7
- package/dist/Modal/modal.svelte +86 -26
- package/dist/Modal/modal.svelte.js +15 -0
- package/dist/Nav/nav-link.svelte +12 -8
- package/dist/Navbar/navbar-collapse.svelte +18 -7
- package/dist/Navbar/navbar-collapse.svelte.d.ts +1 -1
- package/dist/Navbar/navbar-nav.svelte +1 -1
- package/dist/Navbar/navbar-toggler.svelte +9 -7
- package/dist/Navbar/navbar-toggler.svelte.d.ts +1 -1
- package/dist/Navbar/types.d.ts +3 -2
- package/dist/Offcanvas/offcanvas.svelte +38 -7
- package/dist/Pagination/pagination-item.svelte +27 -20
- package/dist/Pagination/pagination-link.svelte +20 -5
- package/dist/Pagination/pagination.svelte.js +28 -8
- package/dist/Placeholder/placeholder-item.svelte +7 -2
- package/dist/Placeholder/placeholder-item.svelte.d.ts +1 -1
- package/dist/Placeholder/types.d.ts +1 -0
- package/dist/Portal/portal.svelte +6 -1
- package/dist/Progress/progress-bar.svelte +22 -9
- package/dist/Row/row.svelte +9 -10
- package/dist/Row/types.d.ts +8 -7
- package/dist/Scrollspy/scrollspy-attachment.svelte.js +47 -28
- package/dist/Tooltip/tooltip.svelte +57 -10
- package/dist/common/css.js +96 -24
- package/dist/common/navbar-offcanvas.svelte.d.ts +2 -0
- package/dist/common/navbar-offcanvas.svelte.js +5 -4
- package/dist/common/overlay-stack.d.ts +13 -0
- package/dist/common/overlay-stack.js +49 -0
- package/dist/common/scrollbar.js +24 -8
- package/package.json +3 -1
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
const hasValidTransitionDuration = (transitionDuration) => transitionDuration !== undefined && Number.isFinite(transitionDuration) && transitionDuration >= 0;
|
|
2
|
+
const resolveTransitionDuration = (node, transitionDuration) => hasValidTransitionDuration(transitionDuration) ? transitionDuration : getTransitionDuration(node);
|
|
3
|
+
const transitionTimingStates = new WeakMap();
|
|
4
|
+
const applyTransitionTiming = (node, duration) => {
|
|
5
|
+
const owner = {};
|
|
6
|
+
let state = transitionTimingStates.get(node);
|
|
7
|
+
if (!state) {
|
|
8
|
+
state = {
|
|
9
|
+
duration: {
|
|
10
|
+
value: node.style.getPropertyValue('transition-duration'),
|
|
11
|
+
priority: node.style.getPropertyPriority('transition-duration')
|
|
12
|
+
},
|
|
13
|
+
delay: {
|
|
14
|
+
value: node.style.getPropertyValue('transition-delay'),
|
|
15
|
+
priority: node.style.getPropertyPriority('transition-delay')
|
|
16
|
+
},
|
|
17
|
+
owner
|
|
18
|
+
};
|
|
19
|
+
transitionTimingStates.set(node, state);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
state.owner = owner;
|
|
23
|
+
}
|
|
24
|
+
node.style.setProperty('transition-duration', `${duration}ms`);
|
|
25
|
+
node.style.setProperty('transition-delay', '0ms');
|
|
26
|
+
return () => {
|
|
27
|
+
if (transitionTimingStates.get(node)?.owner !== owner)
|
|
28
|
+
return;
|
|
29
|
+
transitionTimingStates.delete(node);
|
|
30
|
+
if (state.duration.value) {
|
|
31
|
+
node.style.setProperty('transition-duration', state.duration.value, state.duration.priority);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
node.style.removeProperty('transition-duration');
|
|
35
|
+
}
|
|
36
|
+
if (state.delay.value) {
|
|
37
|
+
node.style.setProperty('transition-delay', state.delay.value, state.delay.priority);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
node.style.removeProperty('transition-delay');
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
};
|
|
1
44
|
export function collapseIn(node, params) {
|
|
2
45
|
const isHorizontal = params.isHorizontal;
|
|
3
46
|
// Choose dimension to animate: width for horizontal, height for vertical
|
|
@@ -6,7 +49,16 @@ export function collapseIn(node, params) {
|
|
|
6
49
|
node.classList.add('collapsing');
|
|
7
50
|
node.classList.remove('collapse', 'show');
|
|
8
51
|
node.style[stylePropName] = '0';
|
|
9
|
-
const duration =
|
|
52
|
+
const duration = resolveTransitionDuration(node, params.transitionDuration);
|
|
53
|
+
const restoreTransitionTiming = hasValidTransitionDuration(params.transitionDuration) ? applyTransitionTiming(node, duration) : () => undefined;
|
|
54
|
+
// Svelte skips transition ticks when duration is zero, so complete the
|
|
55
|
+
// Bootstrap class state ourselves for explicit no-animation transitions.
|
|
56
|
+
if (duration === 0) {
|
|
57
|
+
node.classList.remove('collapsing');
|
|
58
|
+
node.classList.add('collapse', 'show');
|
|
59
|
+
node.style[stylePropName] = '';
|
|
60
|
+
restoreTransitionTiming();
|
|
61
|
+
}
|
|
10
62
|
return {
|
|
11
63
|
duration,
|
|
12
64
|
css: (_progress, _remaining) => 'overflow: hidden;',
|
|
@@ -26,6 +78,7 @@ export function collapseIn(node, params) {
|
|
|
26
78
|
node.classList.remove('collapsing');
|
|
27
79
|
node.classList.add('collapse', 'show');
|
|
28
80
|
node.style[stylePropName] = '';
|
|
81
|
+
restoreTransitionTiming();
|
|
29
82
|
}
|
|
30
83
|
}
|
|
31
84
|
};
|
|
@@ -38,7 +91,15 @@ export function collapseOut(node, params) {
|
|
|
38
91
|
// Bootstrap collapse semantics: start with "collapsing" state, remove "collapse" and "show"
|
|
39
92
|
node.classList.add('collapsing');
|
|
40
93
|
node.classList.remove('collapse', 'show');
|
|
41
|
-
const duration =
|
|
94
|
+
const duration = resolveTransitionDuration(node, params.transitionDuration);
|
|
95
|
+
const restoreTransitionTiming = hasValidTransitionDuration(params.transitionDuration) ? applyTransitionTiming(node, duration) : () => undefined;
|
|
96
|
+
// See collapseIn: a zero-duration transition does not receive a final tick.
|
|
97
|
+
if (duration === 0) {
|
|
98
|
+
node.classList.remove('collapsing');
|
|
99
|
+
node.classList.add('collapse');
|
|
100
|
+
node.style[dimension] = '';
|
|
101
|
+
restoreTransitionTiming();
|
|
102
|
+
}
|
|
42
103
|
return {
|
|
43
104
|
duration,
|
|
44
105
|
css: (_progress, _remaining) => 'overflow: hidden;',
|
|
@@ -52,6 +113,7 @@ export function collapseOut(node, params) {
|
|
|
52
113
|
// Completion: remove "collapsing", add "collapse" (but not "show")
|
|
53
114
|
node.classList.remove('collapsing');
|
|
54
115
|
node.classList.add('collapse');
|
|
116
|
+
restoreTransitionTiming();
|
|
55
117
|
}
|
|
56
118
|
}
|
|
57
119
|
};
|
|
@@ -59,17 +121,18 @@ export function collapseOut(node, params) {
|
|
|
59
121
|
export function getTransitionDuration(element) {
|
|
60
122
|
if (!element)
|
|
61
123
|
return 0;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
124
|
+
const { transitionDuration, transitionDelay } = window.getComputedStyle(element);
|
|
125
|
+
const parseTime = (value, allowNegative) => {
|
|
126
|
+
const firstValue = value.split(',')[0]?.trim() ?? '';
|
|
127
|
+
const match = /^([+-]?(?:\d+(?:\.\d*)?|\.\d+))(ms|s)$/i.exec(firstValue);
|
|
128
|
+
if (!match)
|
|
129
|
+
return 0;
|
|
130
|
+
const amount = Number(match[1]);
|
|
131
|
+
if (!Number.isFinite(amount) || (!allowNegative && amount < 0))
|
|
132
|
+
return 0;
|
|
133
|
+
return match[2]?.toLowerCase() === 'ms' ? amount : amount * 1000;
|
|
134
|
+
};
|
|
135
|
+
const duration = parseTime(transitionDuration, false);
|
|
136
|
+
const delay = parseTime(transitionDelay, true);
|
|
137
|
+
return Math.max(0, duration + delay);
|
|
75
138
|
}
|
package/dist/Collapse/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { DivElement } from '../common/types.js';
|
|
|
2
2
|
export type CollapseTransitionStylePropName = 'width' | 'height';
|
|
3
3
|
export type CollapseTransitionParams = {
|
|
4
4
|
isHorizontal?: boolean;
|
|
5
|
+
transitionDuration?: number;
|
|
5
6
|
};
|
|
6
7
|
export type CollapseRootProps = DivElement & {
|
|
7
8
|
isHorizontal?: boolean;
|
|
@@ -22,6 +22,7 @@ A clickable item within a dropdown menu.
|
|
|
22
22
|
<script lang="ts">/* eslint-disable @typescript-eslint/no-explicit-any -- restOfProps spread targets a union element type that is not spreadable without as any */
|
|
23
23
|
import { uniqueClsx } from '../common/css.js';
|
|
24
24
|
import { noop } from '../common/noop.js';
|
|
25
|
+
import { onDestroy, onMount } from 'svelte';
|
|
25
26
|
import { DropdownItemState, initDropdownItemState } from './dropdown.svelte.js';
|
|
26
27
|
// Generate a unique ID for the dropdown item
|
|
27
28
|
const uid = $props.id();
|
|
@@ -40,6 +41,8 @@ const itemState = initDropdownItemState({
|
|
|
40
41
|
return isDisabled;
|
|
41
42
|
}
|
|
42
43
|
});
|
|
44
|
+
onMount(() => itemState.root.scheduleReorderItems());
|
|
45
|
+
onDestroy(() => itemState.root.unregisterItem(itemState));
|
|
43
46
|
let isAnchor = $derived(Boolean(href));
|
|
44
47
|
let elementType = $derived(isAnchor ? 'a' : 'button');
|
|
45
48
|
let classes = $derived(uniqueClsx('dropdown-item', {
|
|
@@ -47,6 +50,10 @@ let classes = $derived(uniqueClsx('dropdown-item', {
|
|
|
47
50
|
disabled: isDisabled
|
|
48
51
|
}, classValues));
|
|
49
52
|
const handleClick = (event) => {
|
|
53
|
+
if (isDisabled) {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
50
57
|
if (href && href === '#!') {
|
|
51
58
|
event.preventDefault();
|
|
52
59
|
}
|
|
@@ -13,6 +13,7 @@ export class DropdownRootState {
|
|
|
13
13
|
#isNavItem = $state(false);
|
|
14
14
|
#isShown = $state(false);
|
|
15
15
|
#items = $state([]);
|
|
16
|
+
#reorderScheduled = false;
|
|
16
17
|
ariaLabelledBy = $state(undefined); // Used for accessibility, linking the dropdown to a label.
|
|
17
18
|
constructor(props) {
|
|
18
19
|
this.props = props;
|
|
@@ -25,7 +26,10 @@ export class DropdownRootState {
|
|
|
25
26
|
this.focusOnNextItem = this.focusOnNextItem.bind(this);
|
|
26
27
|
this.focusOnPreviousItem = this.focusOnPreviousItem.bind(this);
|
|
27
28
|
this.registerItem = this.registerItem.bind(this);
|
|
29
|
+
this.reorderItems = this.reorderItems.bind(this);
|
|
30
|
+
this.scheduleReorderItems = this.scheduleReorderItems.bind(this);
|
|
28
31
|
this.toggleIsShown = this.toggleIsShown.bind(this);
|
|
32
|
+
this.unregisterItem = this.unregisterItem.bind(this);
|
|
29
33
|
}
|
|
30
34
|
get activeItemIndex() {
|
|
31
35
|
return this.#activeItemIndex;
|
|
@@ -79,22 +83,50 @@ export class DropdownRootState {
|
|
|
79
83
|
return this.#isShown;
|
|
80
84
|
}
|
|
81
85
|
focusOnNextItem() {
|
|
82
|
-
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
this.#activeItemIndex = (this.#activeItemIndex + 1) % this.#items.length;
|
|
86
|
-
this.#items[this.#activeItemIndex]?.props.elementRef?.focus();
|
|
86
|
+
this.focusOnItemInDirection(1);
|
|
87
87
|
}
|
|
88
88
|
focusOnPreviousItem() {
|
|
89
|
-
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
this.#activeItemIndex = (this.#activeItemIndex - 1 + this.#items.length) % this.#items.length;
|
|
93
|
-
this.#items[this.#activeItemIndex]?.props.elementRef?.focus();
|
|
89
|
+
this.focusOnItemInDirection(-1);
|
|
94
90
|
}
|
|
95
91
|
registerItem(item) {
|
|
96
92
|
item.itemIndex = this.#items.length; // Set the item's index based on current length.
|
|
97
|
-
this.#items
|
|
93
|
+
this.#items = [...this.#items, item];
|
|
94
|
+
}
|
|
95
|
+
unregisterItem(item) {
|
|
96
|
+
const removedIndex = this.#items.indexOf(item);
|
|
97
|
+
if (removedIndex === -1) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const activeItem = this.#items[this.#activeItemIndex];
|
|
101
|
+
const remainingItems = this.#items.filter((registeredItem) => registeredItem !== item);
|
|
102
|
+
remainingItems.forEach((registeredItem, index) => (registeredItem.itemIndex = index));
|
|
103
|
+
this.#items = remainingItems;
|
|
104
|
+
this.#activeItemIndex = activeItem && activeItem !== item ? remainingItems.indexOf(activeItem) : -1;
|
|
105
|
+
}
|
|
106
|
+
reorderItems() {
|
|
107
|
+
const activeItem = this.#items[this.#activeItemIndex];
|
|
108
|
+
const orderedItems = [...this.#items].sort((first, second) => {
|
|
109
|
+
const firstElement = first.props.elementRef;
|
|
110
|
+
const secondElement = second.props.elementRef;
|
|
111
|
+
if (!firstElement || !secondElement || firstElement === secondElement) {
|
|
112
|
+
return firstElement ? -1 : secondElement ? 1 : 0;
|
|
113
|
+
}
|
|
114
|
+
const position = firstElement.compareDocumentPosition(secondElement);
|
|
115
|
+
return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : position & Node.DOCUMENT_POSITION_PRECEDING ? 1 : 0;
|
|
116
|
+
});
|
|
117
|
+
orderedItems.forEach((item, index) => (item.itemIndex = index));
|
|
118
|
+
this.#items = orderedItems;
|
|
119
|
+
this.#activeItemIndex = activeItem ? orderedItems.indexOf(activeItem) : -1;
|
|
120
|
+
}
|
|
121
|
+
scheduleReorderItems() {
|
|
122
|
+
if (this.#reorderScheduled) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
this.#reorderScheduled = true;
|
|
126
|
+
queueMicrotask(() => {
|
|
127
|
+
this.#reorderScheduled = false;
|
|
128
|
+
this.reorderItems();
|
|
129
|
+
});
|
|
98
130
|
}
|
|
99
131
|
toggleIsShown(event) {
|
|
100
132
|
if (!this.#isShown) {
|
|
@@ -112,6 +144,24 @@ export class DropdownRootState {
|
|
|
112
144
|
this.props.onHidden?.(event);
|
|
113
145
|
}
|
|
114
146
|
}
|
|
147
|
+
focusOnItemInDirection(direction) {
|
|
148
|
+
this.reorderItems();
|
|
149
|
+
let itemIndex = this.#activeItemIndex === -1
|
|
150
|
+
? direction === 1
|
|
151
|
+
? 0
|
|
152
|
+
: this.#items.length - 1
|
|
153
|
+
: (this.#activeItemIndex + direction + this.#items.length) % this.#items.length;
|
|
154
|
+
for (let count = 0; count < this.#items.length; count += 1) {
|
|
155
|
+
const item = this.#items[itemIndex];
|
|
156
|
+
if (item && !item.props.isDisabled) {
|
|
157
|
+
this.#activeItemIndex = itemIndex;
|
|
158
|
+
item.props.elementRef?.focus();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
itemIndex = (itemIndex + direction + this.#items.length) % this.#items.length;
|
|
162
|
+
}
|
|
163
|
+
this.#activeItemIndex = -1;
|
|
164
|
+
}
|
|
115
165
|
}
|
|
116
166
|
export class DropdownToggleState {
|
|
117
167
|
props;
|
|
@@ -210,15 +260,9 @@ export class DropdownMenuState {
|
|
|
210
260
|
if (!this.isShown) {
|
|
211
261
|
return;
|
|
212
262
|
}
|
|
213
|
-
if (!this.root.props.id) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
263
|
const target = event.target;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const nearestDropdown = target.closest(`#${CSS.escape(this.root.props.id)}`);
|
|
220
|
-
if (nearestDropdown && nearestDropdown.id === this.root.props.id) {
|
|
221
|
-
// The click is within the current dropdown, so do nothing.
|
|
264
|
+
if (this.#elementRef?.contains(target) || this.#rootElementRef?.contains(target)) {
|
|
265
|
+
// The click is within this menu or its toggle/root, so do nothing.
|
|
222
266
|
return;
|
|
223
267
|
}
|
|
224
268
|
// Only close on outside clicks if autoClose is true or 'outside'
|
|
@@ -351,6 +395,9 @@ export class DropdownItemState {
|
|
|
351
395
|
}
|
|
352
396
|
// When a dropdown item is clicked, it should typically close the dropdown.
|
|
353
397
|
onclick(event) {
|
|
398
|
+
if (this.props.isDisabled) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
354
401
|
// Only close on item clicks if autoClose is true or 'inside'
|
|
355
402
|
// false: only close on toggle click (ignore item clicks)
|
|
356
403
|
// 'inside': close on item/toggle clicks (ignore body clicks)
|
|
@@ -44,7 +44,7 @@ Bootstrap-styled checkbox input component for boolean selections.
|
|
|
44
44
|
- `isValid` (boolean): Optional. Indicates whether the input is valid. If undefined, no validation styles are applied.
|
|
45
45
|
-->
|
|
46
46
|
<script lang="ts">import { noop, uniqueClsx } from '../common/index.js';
|
|
47
|
-
let { checked = $bindable(false), class: classValues, elementRef = $bindable(null), group = $bindable(null), isIndeterminate, isInvalid, isValid, onchange = noop, value = undefined, ...restOfProps } = $props();
|
|
47
|
+
let { 'aria-invalid': ariaInvalid, checked = $bindable(false), class: classValues, elementRef = $bindable(null), group = $bindable(null), isIndeterminate, isInvalid, isValid, onchange = noop, value = undefined, ...restOfProps } = $props();
|
|
48
48
|
let classes = $derived(uniqueClsx('form-check-input', {
|
|
49
49
|
'is-valid': isValid,
|
|
50
50
|
'is-invalid': isInvalid
|
|
@@ -70,8 +70,8 @@ const handleChange = (event) => {
|
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
$effect(() => {
|
|
73
|
-
if (elementRef
|
|
74
|
-
elementRef.indeterminate = isIndeterminate;
|
|
73
|
+
if (elementRef) {
|
|
74
|
+
elementRef.indeterminate = isIndeterminate === true;
|
|
75
75
|
}
|
|
76
76
|
});
|
|
77
77
|
$effect(() => {
|
|
@@ -82,11 +82,11 @@ $effect(() => {
|
|
|
82
82
|
</script>
|
|
83
83
|
|
|
84
84
|
<input
|
|
85
|
-
|
|
85
|
+
{...restOfProps}
|
|
86
|
+
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' : ariaInvalid}
|
|
86
87
|
bind:this={elementRef}
|
|
87
88
|
bind:checked
|
|
88
89
|
class={classes}
|
|
89
90
|
onchange={handleChange}
|
|
90
|
-
type="checkbox"
|
|
91
91
|
{value}
|
|
92
|
-
|
|
92
|
+
type="checkbox" />
|
|
@@ -26,7 +26,8 @@ Bootstrap-styled color picker input component.
|
|
|
26
26
|
- `value` (string): Optional. The color value in hex format (e.g., "#563d7c"). If undefined, the input is empty.
|
|
27
27
|
-->
|
|
28
28
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
29
|
-
|
|
29
|
+
import { noop } from '../common/noop.js';
|
|
30
|
+
let { 'aria-invalid': ariaInvalid, class: classValues, elementRef = $bindable(null), isInvalid, isValid, oninput = noop, sizing, value = $bindable('#000000'), ...restOfProps } = $props();
|
|
30
31
|
let classes = $derived(uniqueClsx('form-control', 'form-control-color', {
|
|
31
32
|
[`form-control-${sizing}`]: !!sizing,
|
|
32
33
|
'is-valid': isValid,
|
|
@@ -43,12 +44,16 @@ $effect(() => {
|
|
|
43
44
|
input.value = normalized;
|
|
44
45
|
}
|
|
45
46
|
});
|
|
47
|
+
const handleInput = (event) => {
|
|
48
|
+
value = event.currentTarget.value;
|
|
49
|
+
oninput?.(event);
|
|
50
|
+
};
|
|
46
51
|
</script>
|
|
47
52
|
|
|
48
53
|
<input
|
|
49
54
|
{...restOfProps}
|
|
50
|
-
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' :
|
|
55
|
+
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' : ariaInvalid}
|
|
51
56
|
bind:this={elementRef}
|
|
52
57
|
class={classes}
|
|
53
|
-
oninput={
|
|
58
|
+
oninput={handleInput}
|
|
54
59
|
type="color" />
|
|
@@ -42,7 +42,7 @@ Bootstrap-styled date input component for selecting dates.
|
|
|
42
42
|
- `value` (string): Optional. The date value in the format "YYYY-MM-DD". If undefined, the input is empty.
|
|
43
43
|
-->
|
|
44
44
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
45
|
-
let { autocomplete = 'off', class: classValues, 'data-form-type': dataFormType = 'email', 'data-lpignore': dataLpignore = undefined, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
45
|
+
let { 'aria-invalid': ariaInvalid, autocomplete = 'off', class: classValues, 'data-form-type': dataFormType = 'email', 'data-lpignore': dataLpignore = undefined, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
46
46
|
let classes = $derived(uniqueClsx({
|
|
47
47
|
'form-control': !isPlaintext,
|
|
48
48
|
'form-control-plaintext': isPlaintext,
|
|
@@ -53,12 +53,12 @@ let classes = $derived(uniqueClsx({
|
|
|
53
53
|
</script>
|
|
54
54
|
|
|
55
55
|
<input
|
|
56
|
-
|
|
56
|
+
{...restOfProps}
|
|
57
|
+
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' : ariaInvalid}
|
|
57
58
|
{autocomplete}
|
|
58
59
|
bind:this={elementRef}
|
|
59
60
|
bind:value
|
|
60
61
|
class={classes}
|
|
61
62
|
data-form-type={autocomplete == 'off' ? 'other' : dataFormType}
|
|
62
63
|
data-lpignore={autocomplete == 'off' ? 'true' : dataLpignore}
|
|
63
|
-
type="date"
|
|
64
|
-
{...restOfProps} />
|
|
64
|
+
type="date" />
|
|
@@ -41,7 +41,7 @@ Bootstrap-styled datetime-local input component for selecting both date and time
|
|
|
41
41
|
- `value` (string): Optional. The datetime-local value in the format "YYYY-MM-DDTHH:MM". If undefined, the input is empty.
|
|
42
42
|
-->
|
|
43
43
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
44
|
-
let { class: classValues, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
44
|
+
let { 'aria-invalid': ariaInvalid, class: classValues, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
45
45
|
let classes = $derived(uniqueClsx({
|
|
46
46
|
'form-control': !isPlaintext,
|
|
47
47
|
'form-control-plaintext': isPlaintext,
|
|
@@ -52,9 +52,9 @@ let classes = $derived(uniqueClsx({
|
|
|
52
52
|
</script>
|
|
53
53
|
|
|
54
54
|
<input
|
|
55
|
-
|
|
55
|
+
{...restOfProps}
|
|
56
|
+
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' : ariaInvalid}
|
|
56
57
|
bind:this={elementRef}
|
|
57
58
|
bind:value
|
|
58
59
|
class={classes}
|
|
59
|
-
type="datetime-local"
|
|
60
|
-
{...restOfProps} />
|
|
60
|
+
type="datetime-local" />
|
|
@@ -42,7 +42,7 @@ Bootstrap-styled email input component for email address entry.
|
|
|
42
42
|
- `value` (string): Optional. The email address value. If undefined, the input is empty.
|
|
43
43
|
-->
|
|
44
44
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
45
|
-
let { autocomplete = 'off', class: classValues, 'data-form-type': dataFormType = 'email', 'data-lpignore': dataLpignore = undefined, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
45
|
+
let { 'aria-invalid': ariaInvalid, autocomplete = 'off', class: classValues, 'data-form-type': dataFormType = 'email', 'data-lpignore': dataLpignore = undefined, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
46
46
|
let classes = $derived(uniqueClsx({
|
|
47
47
|
'form-control': !isPlaintext,
|
|
48
48
|
'form-control-plaintext': isPlaintext,
|
|
@@ -53,12 +53,12 @@ let classes = $derived(uniqueClsx({
|
|
|
53
53
|
</script>
|
|
54
54
|
|
|
55
55
|
<input
|
|
56
|
-
|
|
56
|
+
{...restOfProps}
|
|
57
|
+
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' : ariaInvalid}
|
|
57
58
|
{autocomplete}
|
|
58
59
|
bind:this={elementRef}
|
|
59
60
|
bind:value
|
|
60
61
|
class={classes}
|
|
61
62
|
data-form-type={autocomplete == 'off' ? 'other' : dataFormType}
|
|
62
63
|
data-lpignore={autocomplete == 'off' ? 'true' : dataLpignore}
|
|
63
|
-
type="email"
|
|
64
|
-
{...restOfProps} />
|
|
64
|
+
type="email" />
|
|
@@ -23,21 +23,61 @@ Bootstrap-styled file input component for file uploads.
|
|
|
23
23
|
- `isInvalid` (boolean): Optional. Indicates whether the input is invalid. If undefined, no validation styles are applied.
|
|
24
24
|
- `isValid` (boolean): Optional. Indicates whether the input is valid. If undefined, no validation styles are applied.
|
|
25
25
|
- `sizing` ('sm' | 'lg'): Optional. Controls the size of the input element.
|
|
26
|
-
- `
|
|
26
|
+
- `files` (FileList | null): Optional. Binds the platform file list. Defaults to null.
|
|
27
|
+
- `value` (string): Deprecated compatibility binding. Prefer `files`; non-empty parent values are ignored by the platform input.
|
|
27
28
|
-->
|
|
28
29
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
29
|
-
let { class: classValues, elementRef = $bindable(null), isInvalid, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
30
|
+
let { 'aria-invalid': ariaInvalid, class: classValues, elementRef = $bindable(null), files = $bindable(null), isInvalid, isValid, onchange, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
30
31
|
let classes = $derived(uniqueClsx('form-control', {
|
|
31
32
|
[`form-control-${sizing}`]: !!sizing,
|
|
32
33
|
'is-valid': isValid,
|
|
33
34
|
'is-invalid': isInvalid
|
|
34
35
|
}, classValues));
|
|
36
|
+
const handleChange = (event) => {
|
|
37
|
+
files = event.currentTarget.files;
|
|
38
|
+
value = event.currentTarget.value;
|
|
39
|
+
onchange?.(event);
|
|
40
|
+
};
|
|
41
|
+
$effect(() => {
|
|
42
|
+
const input = elementRef;
|
|
43
|
+
const requestedValue = value;
|
|
44
|
+
if (input && requestedValue === '' && input.value !== '') {
|
|
45
|
+
input.value = '';
|
|
46
|
+
if (files !== null) {
|
|
47
|
+
files = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (input && files === null) {
|
|
51
|
+
if (input.value !== '') {
|
|
52
|
+
input.value = '';
|
|
53
|
+
if (value !== '') {
|
|
54
|
+
value = '';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (input && input.files !== files) {
|
|
60
|
+
try {
|
|
61
|
+
// Let the platform setter validate FileList identity so genuine
|
|
62
|
+
// cross-realm FileLists are not rejected by an instanceof check.
|
|
63
|
+
input.files = files;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
input.value = '';
|
|
67
|
+
value = '';
|
|
68
|
+
if (files !== null) {
|
|
69
|
+
files = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
35
74
|
</script>
|
|
36
75
|
|
|
37
76
|
<input
|
|
38
|
-
|
|
77
|
+
{...restOfProps}
|
|
78
|
+
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' : ariaInvalid}
|
|
39
79
|
bind:this={elementRef}
|
|
40
|
-
bind:value
|
|
41
80
|
class={classes}
|
|
42
|
-
|
|
43
|
-
{
|
|
81
|
+
onchange={handleChange}
|
|
82
|
+
value={undefined}
|
|
83
|
+
type="file" />
|
|
@@ -23,8 +23,9 @@ import type { Form } from './index.js';
|
|
|
23
23
|
* - `isInvalid` (boolean): Optional. Indicates whether the input is invalid. If undefined, no validation styles are applied.
|
|
24
24
|
* - `isValid` (boolean): Optional. Indicates whether the input is valid. If undefined, no validation styles are applied.
|
|
25
25
|
* - `sizing` ('sm' | 'lg'): Optional. Controls the size of the input element.
|
|
26
|
-
* - `
|
|
26
|
+
* - `files` (FileList | null): Optional. Binds the platform file list. Defaults to null.
|
|
27
|
+
* - `value` (string): Deprecated compatibility binding. Prefer `files`; non-empty parent values are ignored by the platform input.
|
|
27
28
|
*/
|
|
28
|
-
declare const FormFileInput: import("svelte").Component<Form.FileInputProps, {}, "value" | "elementRef">;
|
|
29
|
+
declare const FormFileInput: import("svelte").Component<Form.FileInputProps, {}, "value" | "elementRef" | "files">;
|
|
29
30
|
type FormFileInput = ReturnType<typeof FormFileInput>;
|
|
30
31
|
export default FormFileInput;
|
|
@@ -11,6 +11,6 @@ import type { Form } from './index.js';
|
|
|
11
11
|
* - `elementRef` (HTMLInputElement): Optional. Reference to the input DOM element.
|
|
12
12
|
* - `value` (any): Optional. The value of the hidden input. If undefined, the input is empty.
|
|
13
13
|
*/
|
|
14
|
-
declare const FormHiddenInput: import("svelte").Component<Form.
|
|
14
|
+
declare const FormHiddenInput: import("svelte").Component<Form.HiddenInputProps, {}, "value" | "elementRef">;
|
|
15
15
|
type FormHiddenInput = ReturnType<typeof FormHiddenInput>;
|
|
16
16
|
export default FormHiddenInput;
|
|
@@ -27,4 +27,4 @@ let { class: classValues, elementRef = $bindable(null), ...restOfProps } = $prop
|
|
|
27
27
|
let classes = $derived(uniqueClsx(classValues));
|
|
28
28
|
</script>
|
|
29
29
|
|
|
30
|
-
<input bind:this={elementRef} class={classes} type="image"
|
|
30
|
+
<input {...restOfProps} bind:this={elementRef} class={classes} type="image" />
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Form } from './index.js';
|
|
1
2
|
/**
|
|
2
3
|
* ## Form.ImageInput
|
|
3
4
|
* Bootstrap-styled image input component that acts as a graphical submit button.
|
|
@@ -21,6 +22,6 @@
|
|
|
21
22
|
* - `class` (string): Optional. Additional CSS classes to apply to the image input.
|
|
22
23
|
* - `elementRef` (HTMLInputElement): Optional. Reference to the input DOM element.
|
|
23
24
|
*/
|
|
24
|
-
declare const FormImageInput: import("svelte").Component<
|
|
25
|
+
declare const FormImageInput: import("svelte").Component<Form.ImageInputProps, {}, "elementRef">;
|
|
25
26
|
type FormImageInput = ReturnType<typeof FormImageInput>;
|
|
26
27
|
export default FormImageInput;
|
|
@@ -44,7 +44,7 @@ Bootstrap-styled month input component for selecting a specific month and year.
|
|
|
44
44
|
- `value` (string): Optional. The month value in the format "YYYY-MM". If undefined, the input is empty.
|
|
45
45
|
-->
|
|
46
46
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
47
|
-
let { class: classValues, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
47
|
+
let { 'aria-invalid': ariaInvalid, class: classValues, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
48
48
|
let classes = $derived(uniqueClsx({
|
|
49
49
|
'form-control': !isPlaintext,
|
|
50
50
|
'form-control-plaintext': isPlaintext,
|
|
@@ -55,9 +55,9 @@ let classes = $derived(uniqueClsx({
|
|
|
55
55
|
</script>
|
|
56
56
|
|
|
57
57
|
<input
|
|
58
|
-
|
|
58
|
+
{...restOfProps}
|
|
59
|
+
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' : ariaInvalid}
|
|
59
60
|
bind:this={elementRef}
|
|
60
61
|
bind:value
|
|
61
62
|
class={classes}
|
|
62
|
-
type="month"
|
|
63
|
-
{...restOfProps} />
|
|
63
|
+
type="month" />
|
|
@@ -43,7 +43,7 @@ Bootstrap-styled numeric input component for entering numbers.
|
|
|
43
43
|
- `value` (number): Optional. The numeric value of the input. If undefined, the input is empty.
|
|
44
44
|
-->
|
|
45
45
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
46
|
-
let { class: classValues, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
46
|
+
let { 'aria-invalid': ariaInvalid, class: classValues, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, value = $bindable(undefined), ...restOfProps } = $props();
|
|
47
47
|
let classes = $derived(uniqueClsx({
|
|
48
48
|
'form-control': !isPlaintext,
|
|
49
49
|
'form-control-plaintext': isPlaintext,
|
|
@@ -54,9 +54,9 @@ let classes = $derived(uniqueClsx({
|
|
|
54
54
|
</script>
|
|
55
55
|
|
|
56
56
|
<input
|
|
57
|
-
|
|
57
|
+
{...restOfProps}
|
|
58
|
+
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' : ariaInvalid}
|
|
58
59
|
bind:this={elementRef}
|
|
59
60
|
bind:value
|
|
60
61
|
class={classes}
|
|
61
|
-
type="number"
|
|
62
|
-
{...restOfProps} />
|
|
62
|
+
type="number" />
|
|
@@ -37,7 +37,7 @@ Bootstrap-styled password input component with support for various states.
|
|
|
37
37
|
- `value` (string): Optional. The password value. If undefined, the input is empty.
|
|
38
38
|
-->
|
|
39
39
|
<script lang="ts">import { uniqueClsx } from '../common/css.js';
|
|
40
|
-
let { autocomplete = 'off', class: classValues, 'data-form-type': dataFormType = 'password', 'data-lpignore': dataLpignore = undefined, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, showPassword = false, value = $bindable(undefined), ...restOfProps } = $props();
|
|
40
|
+
let { 'aria-invalid': ariaInvalid, autocomplete = 'off', class: classValues, 'data-form-type': dataFormType = 'password', 'data-lpignore': dataLpignore = undefined, elementRef = $bindable(null), isInvalid, isPlaintext = false, isValid, sizing, showPassword = false, value = $bindable(undefined), ...restOfProps } = $props();
|
|
41
41
|
let classes = $derived(uniqueClsx({
|
|
42
42
|
'form-control': !isPlaintext,
|
|
43
43
|
'form-control-plaintext': isPlaintext,
|
|
@@ -49,12 +49,12 @@ let inputType = $derived(showPassword ? 'text' : 'password');
|
|
|
49
49
|
</script>
|
|
50
50
|
|
|
51
51
|
<input
|
|
52
|
-
|
|
52
|
+
{...restOfProps}
|
|
53
|
+
aria-invalid={isInvalid === true ? 'true' : isValid === true ? 'false' : ariaInvalid}
|
|
53
54
|
{autocomplete}
|
|
54
55
|
bind:this={elementRef}
|
|
55
56
|
bind:value
|
|
56
57
|
class={classes}
|
|
57
58
|
data-form-type={autocomplete == 'off' ? 'other' : dataFormType}
|
|
58
59
|
data-lpignore={autocomplete == 'off' ? 'true' : dataLpignore}
|
|
59
|
-
type={inputType}
|
|
60
|
-
{...restOfProps} />
|
|
60
|
+
type={inputType} />
|