@spaethtech/svelte-ui 0.1.10 → 0.3.0
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 +166 -42
- package/dist/components/Alert.svelte +119 -0
- package/dist/components/Alert.svelte.d.ts +29 -0
- package/dist/components/Badge/Badge.svelte +142 -69
- package/dist/components/Badge/Badge.svelte.d.ts +26 -6
- package/dist/components/Badge/index.d.ts +1 -1
- package/dist/components/Badge/index.js +1 -1
- package/dist/components/Banner.svelte +133 -0
- package/dist/components/Banner.svelte.d.ts +31 -0
- package/dist/components/Button.svelte +240 -0
- package/dist/components/Button.svelte.d.ts +33 -0
- package/dist/components/ButtonDropdown.svelte +145 -0
- package/dist/components/ButtonDropdown.svelte.d.ts +31 -0
- package/dist/components/Calendar.svelte +259 -0
- package/dist/components/Calendar.svelte.d.ts +27 -0
- package/dist/components/Card.svelte +78 -0
- package/dist/components/Card.svelte.d.ts +17 -0
- package/dist/components/CardBody.svelte +41 -0
- package/dist/components/CardBody.svelte.d.ts +17 -0
- package/dist/components/CardFooter.svelte +49 -0
- package/dist/components/CardFooter.svelte.d.ts +16 -0
- package/dist/components/CardHeader.svelte +49 -0
- package/dist/components/CardHeader.svelte.d.ts +16 -0
- package/dist/components/Checkbox.svelte +131 -0
- package/dist/components/Checkbox.svelte.d.ts +16 -0
- package/dist/components/DataTable.svelte +518 -0
- package/dist/components/DataTable.svelte.d.ts +74 -0
- package/dist/components/DatePicker.svelte +312 -0
- package/dist/components/DatePicker.svelte.d.ts +32 -0
- package/dist/components/DateTimeInput.svelte +93 -0
- package/dist/components/DateTimeInput.svelte.d.ts +20 -0
- package/dist/components/Dialog.svelte +132 -0
- package/dist/components/Dialog.svelte.d.ts +24 -0
- package/dist/components/{EmailInput/EmailInput.svelte → EmailInput.svelte} +17 -8
- package/dist/components/EmailInput.svelte.d.ts +21 -0
- package/dist/components/Input.svelte +369 -0
- package/dist/components/Input.svelte.d.ts +35 -0
- package/dist/components/{List/List.svelte → List.svelte} +194 -133
- package/dist/components/{List/List.svelte.d.ts → List.svelte.d.ts} +9 -1
- package/dist/components/Menu.svelte +117 -0
- package/dist/components/Menu.svelte.d.ts +20 -0
- package/dist/components/NotesEditor.svelte +127 -0
- package/dist/components/NotesEditor.svelte.d.ts +17 -0
- package/dist/components/{NumberInput/NumberInput.svelte → NumberInput.svelte} +146 -105
- package/dist/components/{NumberInput/NumberInput.svelte.d.ts → NumberInput.svelte.d.ts} +11 -4
- package/dist/components/PasswordInput.svelte +52 -0
- package/dist/components/PasswordInput.svelte.d.ts +20 -0
- package/dist/components/Popup.svelte +140 -0
- package/dist/components/Popup.svelte.d.ts +31 -0
- package/dist/components/Query.svelte +284 -0
- package/dist/components/Query.svelte.d.ts +15 -0
- package/dist/components/{Rating/Rating.svelte → Rating.svelte} +19 -10
- package/dist/components/Rating.svelte.d.ts +13 -0
- package/dist/components/{SearchInput/SearchInput.svelte → SearchInput.svelte} +19 -8
- package/dist/components/{SearchInput/SearchInput.svelte.d.ts → SearchInput.svelte.d.ts} +9 -2
- package/dist/components/{Select/Select.svelte → Select.svelte} +48 -76
- package/dist/components/{Select/Select.svelte.d.ts → Select.svelte.d.ts} +11 -1
- package/dist/components/SideBarMenu/SideBarMenu.svelte +724 -0
- package/dist/components/SideBarMenu/SideBarMenu.svelte.d.ts +109 -0
- package/dist/components/SideBarMenu/index.d.ts +2 -0
- package/dist/components/SideBarMenu/index.js +1 -0
- package/dist/components/TabStrip/TabStrip.svelte +384 -0
- package/dist/components/TabStrip/TabStrip.svelte.d.ts +50 -0
- package/dist/components/TabStrip/index.d.ts +3 -0
- package/dist/components/TabStrip/index.js +2 -0
- package/dist/components/{TextArea/TextArea.svelte → TextArea.svelte} +143 -112
- package/dist/components/TextArea.svelte.d.ts +38 -0
- package/dist/components/ThemeSelector.svelte +81 -0
- package/dist/components/ThemeSelector.svelte.d.ts +10 -0
- package/dist/components/TimePicker.svelte +148 -0
- package/dist/components/TimePicker.svelte.d.ts +28 -0
- package/dist/components/TimeRangeInput.svelte +203 -0
- package/dist/components/TimeRangeInput.svelte.d.ts +29 -0
- package/dist/components/TimeSpinner.svelte +202 -0
- package/dist/components/TimeSpinner.svelte.d.ts +26 -0
- package/dist/components/Toast/Toaster.svelte +51 -0
- package/dist/components/Toast/Toaster.svelte.d.ts +12 -0
- package/dist/components/Toast/toast.svelte.d.ts +29 -0
- package/dist/components/Toast/toast.svelte.js +27 -0
- package/dist/components/Toggle.svelte +93 -0
- package/dist/components/Toggle.svelte.d.ts +15 -0
- package/dist/data/dataset.d.ts +42 -0
- package/dist/data/dataset.js +3 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +3 -0
- package/dist/data/query/ast.d.ts +62 -0
- package/dist/data/query/ast.js +12 -0
- package/dist/data/query/index.d.ts +3 -0
- package/dist/data/query/index.js +3 -0
- package/dist/data/query/lexer.d.ts +33 -0
- package/dist/data/query/lexer.js +225 -0
- package/dist/data/query/parser.d.ts +11 -0
- package/dist/data/query/parser.js +252 -0
- package/dist/data/table/grid.svelte.d.ts +57 -0
- package/dist/data/table/grid.svelte.js +139 -0
- package/dist/data/table/index.d.ts +2 -0
- package/dist/data/table/index.js +2 -0
- package/dist/data/table/types.d.ts +79 -0
- package/dist/index.d.ts +48 -22
- package/dist/index.js +45 -21
- package/dist/positioning/anchored.d.ts +61 -0
- package/dist/positioning/anchored.js +122 -0
- package/dist/positioning/tooltip.d.ts +41 -0
- package/dist/positioning/tooltip.js +147 -0
- package/dist/theme.css +205 -0
- package/dist/types/breakpoints.d.ts +24 -0
- package/dist/types/breakpoints.js +13 -0
- package/dist/types/responsive.d.ts +32 -0
- package/dist/types/responsive.js +54 -0
- package/dist/types/sizes.d.ts +10 -3
- package/dist/types/time.d.ts +19 -0
- package/dist/types/time.js +10 -0
- package/dist/types/variants.d.ts +8 -1
- package/dist/types/variants.js +16 -1
- package/package.json +93 -63
- package/dist/components/Button/Button.svelte +0 -172
- package/dist/components/Button/Button.svelte.d.ts +0 -32
- package/dist/components/Button/index.d.ts +0 -1
- package/dist/components/Button/index.js +0 -1
- package/dist/components/Dialog/Dialog.svelte +0 -101
- package/dist/components/Dialog/Dialog.svelte.d.ts +0 -18
- package/dist/components/Dialog/index.d.ts +0 -1
- package/dist/components/Dialog/index.js +0 -1
- package/dist/components/ElementManager/ElementManager.svelte +0 -397
- package/dist/components/ElementManager/ElementManager.svelte.d.ts +0 -43
- package/dist/components/ElementManager/index.d.ts +0 -1
- package/dist/components/ElementManager/index.js +0 -1
- package/dist/components/EmailInput/EmailInput.svelte.d.ts +0 -14
- package/dist/components/EmailInput/index.d.ts +0 -1
- package/dist/components/EmailInput/index.js +0 -1
- package/dist/components/Icon/Icon.svelte +0 -74
- package/dist/components/Icon/Icon.svelte.d.ts +0 -13
- package/dist/components/Icon/index.d.ts +0 -1
- package/dist/components/Icon/index.js +0 -1
- package/dist/components/Input/Input.svelte +0 -268
- package/dist/components/Input/Input.svelte.d.ts +0 -18
- package/dist/components/Input/index.d.ts +0 -1
- package/dist/components/Input/index.js +0 -1
- package/dist/components/List/index.d.ts +0 -1
- package/dist/components/List/index.js +0 -1
- package/dist/components/ListItem/ListItem.svelte +0 -175
- package/dist/components/ListItem/ListItem.svelte.d.ts +0 -24
- package/dist/components/ListItem/index.d.ts +0 -2
- package/dist/components/ListItem/index.js +0 -2
- package/dist/components/ListView/ListView.svelte +0 -463
- package/dist/components/ListView/ListView.svelte.d.ts +0 -37
- package/dist/components/ListView/index.d.ts +0 -2
- package/dist/components/ListView/index.js +0 -2
- package/dist/components/NodeGraph/BaseNode.d.ts +0 -57
- package/dist/components/NodeGraph/BaseNode.js +0 -30
- package/dist/components/NodeGraph/Edge.svelte +0 -60
- package/dist/components/NodeGraph/Edge.svelte.d.ts +0 -16
- package/dist/components/NodeGraph/ExpressionEvaluator.d.ts +0 -82
- package/dist/components/NodeGraph/ExpressionEvaluator.js +0 -152
- package/dist/components/NodeGraph/Node.svelte +0 -100
- package/dist/components/NodeGraph/Node.svelte.d.ts +0 -10
- package/dist/components/NodeGraph/NodeGraph.svelte +0 -52
- package/dist/components/NodeGraph/NodeGraph.svelte.d.ts +0 -14
- package/dist/components/NodeGraph/NodeInstance.svelte.d.ts +0 -80
- package/dist/components/NodeGraph/NodeInstance.svelte.js +0 -241
- package/dist/components/NodeGraph/NodePort.svelte +0 -75
- package/dist/components/NodeGraph/NodePort.svelte.d.ts +0 -11
- package/dist/components/NodeGraph/decorators.d.ts +0 -81
- package/dist/components/NodeGraph/decorators.js +0 -148
- package/dist/components/NodeGraph/index.d.ts +0 -9
- package/dist/components/NodeGraph/index.js +0 -9
- package/dist/components/NodeGraph/types.d.ts +0 -94
- package/dist/components/NodeGraph/types.js +0 -33
- package/dist/components/NotesEditor/NotesEditor.svelte +0 -203
- package/dist/components/NotesEditor/NotesEditor.svelte.d.ts +0 -9
- package/dist/components/NotesEditor/index.d.ts +0 -1
- package/dist/components/NotesEditor/index.js +0 -1
- package/dist/components/NumberInput/index.d.ts +0 -1
- package/dist/components/NumberInput/index.js +0 -1
- package/dist/components/PasswordInput/PasswordInput.svelte +0 -41
- package/dist/components/PasswordInput/PasswordInput.svelte.d.ts +0 -13
- package/dist/components/PasswordInput/index.d.ts +0 -1
- package/dist/components/PasswordInput/index.js +0 -1
- package/dist/components/Popup/index.d.ts +0 -1
- package/dist/components/Popup/index.js +0 -1
- package/dist/components/Rating/Rating.svelte.d.ts +0 -7
- package/dist/components/Rating/index.d.ts +0 -1
- package/dist/components/Rating/index.js +0 -1
- package/dist/components/ScrollView/ScrollView.svelte +0 -285
- package/dist/components/ScrollView/ScrollView.svelte.d.ts +0 -19
- package/dist/components/ScrollView/index.d.ts +0 -1
- package/dist/components/ScrollView/index.js +0 -1
- package/dist/components/SearchInput/index.d.ts +0 -1
- package/dist/components/SearchInput/index.js +0 -1
- package/dist/components/Select/index.d.ts +0 -1
- package/dist/components/Select/index.js +0 -1
- package/dist/components/TextArea/TextArea.svelte.d.ts +0 -19
- package/dist/components/TextArea/index.d.ts +0 -1
- package/dist/components/TextArea/index.js +0 -1
- package/dist/components/ThemeSelector/ThemeSelector.svelte +0 -64
- package/dist/components/ThemeSelector/ThemeSelector.svelte.d.ts +0 -3
- package/dist/components/ThemeSelector/index.d.ts +0 -1
- package/dist/components/ThemeSelector/index.js +0 -1
- package/dist/components/TooltipHandler/TooltipHandler.svelte +0 -593
- package/dist/components/TooltipHandler/TooltipHandler.svelte.d.ts +0 -10
- package/dist/components/TooltipHandler/index.d.ts +0 -1
- package/dist/components/TooltipHandler/index.js +0 -1
- package/dist/styles/sizes.d.ts +0 -78
- package/dist/styles/sizes.js +0 -120
- package/dist/styles/variants.d.ts +0 -106
- package/dist/styles/variants.js +0 -194
- package/dist/types/ManagerTypes.d.ts +0 -42
- /package/dist/{types/ManagerTypes.js → data/table/types.js} +0 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import Input from
|
|
3
|
-
import type { HTMLInputAttributes } from
|
|
2
|
+
import Input from "./Input.svelte";
|
|
3
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
4
|
+
import type { Size } from "../types/sizes.js";
|
|
5
|
+
import type { Responsive } from "../types/responsive.js";
|
|
6
|
+
import type { Variant } from "../types/variants.js";
|
|
4
7
|
|
|
5
|
-
interface Props extends Omit<HTMLInputAttributes,
|
|
8
|
+
interface Props extends Omit<HTMLInputAttributes, "type" | "value" | "size"> {
|
|
6
9
|
value: number | null;
|
|
7
10
|
class?: string;
|
|
8
11
|
inputClass?: string;
|
|
@@ -10,6 +13,10 @@
|
|
|
10
13
|
valid?: boolean;
|
|
11
14
|
touched?: boolean;
|
|
12
15
|
element?: HTMLInputElement;
|
|
16
|
+
size?: Responsive<Size>;
|
|
17
|
+
/** Shared axes — forwarded to the underlying Input. */
|
|
18
|
+
variant?: Variant;
|
|
19
|
+
borderless?: boolean;
|
|
13
20
|
min?: number;
|
|
14
21
|
max?: number;
|
|
15
22
|
step?: number;
|
|
@@ -21,8 +28,8 @@
|
|
|
21
28
|
currency?: string; // e.g., '$', '€', '£', '¥', etc.
|
|
22
29
|
decimalSeparator?: string; // Decimal separator (default: '.')
|
|
23
30
|
thousandsChar?: string; // Thousands separator character (default: ',')
|
|
24
|
-
roundingMode?:
|
|
25
|
-
negativeMode?:
|
|
31
|
+
roundingMode?: "round" | "floor" | "ceil" | "trunc"; // Rounding behavior for integers
|
|
32
|
+
negativeMode?: "minus" | "parentheses" | "both"; // How to display/accept negative numbers
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
let {
|
|
@@ -35,30 +42,36 @@
|
|
|
35
42
|
max,
|
|
36
43
|
step = 1,
|
|
37
44
|
allowDecimals = true,
|
|
38
|
-
|
|
45
|
+
// Integer by default. Currency / accounting callers already
|
|
46
|
+
// pass `decimalPlaces={2}` explicitly (see the showcase), so
|
|
47
|
+
// this default only affects bare `<NumberInput bind:value=… />`
|
|
48
|
+
// which reads as an integer field 99% of the time (port numbers,
|
|
49
|
+
// intervals, counts). Prevents the `22 → 22.00` surprise where
|
|
50
|
+
// callers had to opt OUT of decimals to display a whole number.
|
|
51
|
+
decimalPlaces = 0,
|
|
39
52
|
thousandsSeparator: enableThousandsSeparator = false,
|
|
40
|
-
prefix =
|
|
41
|
-
suffix =
|
|
53
|
+
prefix = "",
|
|
54
|
+
suffix = "",
|
|
42
55
|
currency,
|
|
43
|
-
decimalSeparator =
|
|
44
|
-
thousandsChar =
|
|
45
|
-
roundingMode =
|
|
46
|
-
negativeMode =
|
|
47
|
-
placeholder =
|
|
56
|
+
decimalSeparator = ".",
|
|
57
|
+
thousandsChar = ",",
|
|
58
|
+
roundingMode = "round",
|
|
59
|
+
negativeMode = "minus",
|
|
60
|
+
placeholder = "Enter a number...",
|
|
48
61
|
...restProps
|
|
49
62
|
}: Props = $props();
|
|
50
63
|
|
|
51
|
-
let displayValue = $state(
|
|
64
|
+
let displayValue = $state("");
|
|
52
65
|
let focused = $state(false);
|
|
53
66
|
|
|
54
67
|
// Convert number to formatted display string
|
|
55
68
|
function formatNumber(num: number | null): string {
|
|
56
|
-
if (num === null || num === undefined || isNaN(num)) return
|
|
57
|
-
|
|
69
|
+
if (num === null || num === undefined || isNaN(num)) return "";
|
|
70
|
+
|
|
58
71
|
const isNegative = num < 0;
|
|
59
72
|
const absoluteNum = Math.abs(num);
|
|
60
73
|
let formatted = absoluteNum.toString();
|
|
61
|
-
|
|
74
|
+
|
|
62
75
|
// Handle decimal places and rounding
|
|
63
76
|
if (allowDecimals && decimalPlaces > 0) {
|
|
64
77
|
formatted = absoluteNum.toFixed(decimalPlaces);
|
|
@@ -66,13 +79,13 @@
|
|
|
66
79
|
// Apply rounding mode for integers
|
|
67
80
|
let rounded: number;
|
|
68
81
|
switch (roundingMode) {
|
|
69
|
-
case
|
|
82
|
+
case "floor":
|
|
70
83
|
rounded = Math.floor(absoluteNum);
|
|
71
84
|
break;
|
|
72
|
-
case
|
|
85
|
+
case "ceil":
|
|
73
86
|
rounded = Math.ceil(absoluteNum);
|
|
74
87
|
break;
|
|
75
|
-
case
|
|
88
|
+
case "trunc":
|
|
76
89
|
rounded = Math.trunc(absoluteNum);
|
|
77
90
|
break;
|
|
78
91
|
default: // 'round'
|
|
@@ -80,22 +93,22 @@
|
|
|
80
93
|
}
|
|
81
94
|
formatted = rounded.toString();
|
|
82
95
|
}
|
|
83
|
-
|
|
96
|
+
|
|
84
97
|
// Add thousands separator and convert to custom format
|
|
85
98
|
if (enableThousandsSeparator) {
|
|
86
|
-
const parts = formatted.split(
|
|
99
|
+
const parts = formatted.split(".");
|
|
87
100
|
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsChar);
|
|
88
101
|
formatted = parts.join(decimalSeparator);
|
|
89
|
-
} else if (decimalSeparator !==
|
|
102
|
+
} else if (decimalSeparator !== ".") {
|
|
90
103
|
// Even without thousands separator, we need to convert decimal separator
|
|
91
|
-
formatted = formatted.replace(
|
|
104
|
+
formatted = formatted.replace(".", decimalSeparator);
|
|
92
105
|
}
|
|
93
|
-
|
|
106
|
+
|
|
94
107
|
const finalPrefix = currency || prefix;
|
|
95
|
-
|
|
108
|
+
|
|
96
109
|
// Handle negative number display
|
|
97
110
|
if (isNegative) {
|
|
98
|
-
if (negativeMode ===
|
|
111
|
+
if (negativeMode === "parentheses") {
|
|
99
112
|
// For parentheses, put everything (prefix, number, suffix) inside
|
|
100
113
|
return `(${finalPrefix}${formatted}${suffix})`;
|
|
101
114
|
} else {
|
|
@@ -103,56 +116,62 @@
|
|
|
103
116
|
return `-${finalPrefix}${formatted}${suffix}`;
|
|
104
117
|
}
|
|
105
118
|
}
|
|
106
|
-
|
|
119
|
+
|
|
107
120
|
return `${finalPrefix}${formatted}${suffix}`;
|
|
108
121
|
}
|
|
109
122
|
|
|
110
123
|
// Parse display string back to number
|
|
111
124
|
function parseNumber(str: string): number | null {
|
|
112
|
-
if (!str || str.trim() ===
|
|
113
|
-
|
|
125
|
+
if (!str || str.trim() === "") return null;
|
|
126
|
+
|
|
114
127
|
// Check for negative indicators
|
|
115
|
-
const hasParentheses = str.includes(
|
|
116
|
-
const hasMinus = str.includes(
|
|
128
|
+
const hasParentheses = str.includes("(") && str.includes(")");
|
|
129
|
+
const hasMinus = str.includes("-");
|
|
117
130
|
const isNegative = hasParentheses || hasMinus;
|
|
118
|
-
|
|
131
|
+
|
|
119
132
|
// Remove prefix, suffix, currency
|
|
120
133
|
const finalPrefix = currency || prefix;
|
|
121
134
|
let cleaned = str;
|
|
122
|
-
|
|
135
|
+
|
|
123
136
|
if (finalPrefix) {
|
|
124
|
-
const escapedPrefix = finalPrefix.replace(/[.*+?^${}()|[\]\\]/g,
|
|
137
|
+
const escapedPrefix = finalPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
125
138
|
// Remove prefix from beginning, or after negative indicators
|
|
126
|
-
cleaned = cleaned.replace(new RegExp(`^${escapedPrefix}`),
|
|
127
|
-
cleaned = cleaned.replace(new RegExp(`^-${escapedPrefix}`),
|
|
128
|
-
cleaned = cleaned.replace(new RegExp(`^\\(${escapedPrefix}`),
|
|
139
|
+
cleaned = cleaned.replace(new RegExp(`^${escapedPrefix}`), ""); // $123
|
|
140
|
+
cleaned = cleaned.replace(new RegExp(`^-${escapedPrefix}`), "-"); // -$123 -> -123
|
|
141
|
+
cleaned = cleaned.replace(new RegExp(`^\\(${escapedPrefix}`), "("); // ($123 -> (123
|
|
129
142
|
}
|
|
130
|
-
|
|
143
|
+
|
|
131
144
|
if (suffix) {
|
|
132
|
-
const escapedSuffix = suffix.replace(/[.*+?^${}()|[\]\\]/g,
|
|
133
|
-
cleaned = cleaned.replace(new RegExp(`${escapedSuffix}$`),
|
|
145
|
+
const escapedSuffix = suffix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
146
|
+
cleaned = cleaned.replace(new RegExp(`${escapedSuffix}$`), "");
|
|
134
147
|
}
|
|
135
|
-
|
|
148
|
+
|
|
136
149
|
// Remove negative indicators
|
|
137
|
-
cleaned = cleaned.replace(/[()]/g,
|
|
138
|
-
cleaned = cleaned.replace(/-/g,
|
|
139
|
-
|
|
150
|
+
cleaned = cleaned.replace(/[()]/g, ""); // Remove parentheses
|
|
151
|
+
cleaned = cleaned.replace(/-/g, ""); // Remove minus signs
|
|
152
|
+
|
|
140
153
|
// Remove thousands separators (both , and . could be thousands separators)
|
|
141
|
-
cleaned = cleaned.replace(
|
|
142
|
-
|
|
154
|
+
cleaned = cleaned.replace(
|
|
155
|
+
new RegExp(`\\${thousandsChar.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "g"),
|
|
156
|
+
"",
|
|
157
|
+
);
|
|
158
|
+
|
|
143
159
|
// Convert decimal separator to standard decimal point
|
|
144
|
-
if (decimalSeparator !==
|
|
160
|
+
if (decimalSeparator !== ".") {
|
|
145
161
|
// Replace the decimal separator with standard decimal point
|
|
146
162
|
// But only the last occurrence to handle cases like "1.234,56"
|
|
147
163
|
const lastSeparatorIndex = cleaned.lastIndexOf(decimalSeparator);
|
|
148
164
|
if (lastSeparatorIndex !== -1) {
|
|
149
|
-
cleaned =
|
|
165
|
+
cleaned =
|
|
166
|
+
cleaned.substring(0, lastSeparatorIndex) +
|
|
167
|
+
"." +
|
|
168
|
+
cleaned.substring(lastSeparatorIndex + 1);
|
|
150
169
|
}
|
|
151
170
|
}
|
|
152
|
-
|
|
171
|
+
|
|
153
172
|
const parsed = parseFloat(cleaned);
|
|
154
173
|
if (isNaN(parsed)) return null;
|
|
155
|
-
|
|
174
|
+
|
|
156
175
|
return isNegative ? -parsed : parsed;
|
|
157
176
|
}
|
|
158
177
|
|
|
@@ -166,12 +185,12 @@
|
|
|
166
185
|
// Default validation
|
|
167
186
|
const defaultValidator = (val: number | null) => {
|
|
168
187
|
if (val === null) return true; // Allow null/empty values
|
|
169
|
-
|
|
188
|
+
|
|
170
189
|
if (min !== undefined && val < min) return `Value must be at least ${min}`;
|
|
171
190
|
if (max !== undefined && val > max) return `Value must be at most ${max}`;
|
|
172
|
-
|
|
173
|
-
if (!allowDecimals && val % 1 !== 0) return
|
|
174
|
-
|
|
191
|
+
|
|
192
|
+
if (!allowDecimals && val % 1 !== 0) return "Decimal values are not allowed";
|
|
193
|
+
|
|
175
194
|
return true;
|
|
176
195
|
};
|
|
177
196
|
|
|
@@ -180,49 +199,49 @@
|
|
|
180
199
|
|
|
181
200
|
function handleInput(inputValue: string) {
|
|
182
201
|
displayValue = inputValue;
|
|
183
|
-
|
|
202
|
+
|
|
184
203
|
// Only skip parsing for truly incomplete inputs that have no numeric content
|
|
185
204
|
const finalPrefix = currency || prefix;
|
|
186
|
-
|
|
205
|
+
|
|
187
206
|
// Check if input is incomplete (has negative indicators but no actual numbers)
|
|
188
207
|
const incompletePatterns = [
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
208
|
+
"-", // Just minus
|
|
209
|
+
"(", // Just opening paren
|
|
210
|
+
"()", // Empty parens
|
|
192
211
|
];
|
|
193
|
-
|
|
212
|
+
|
|
194
213
|
// Add prefix-related incomplete patterns
|
|
195
214
|
if (finalPrefix) {
|
|
196
215
|
incompletePatterns.push(
|
|
197
|
-
`-${finalPrefix}`,
|
|
198
|
-
`(${finalPrefix}`,
|
|
199
|
-
`(${finalPrefix})
|
|
216
|
+
`-${finalPrefix}`, // -$, -€, etc.
|
|
217
|
+
`(${finalPrefix}`, // ($, (€, etc.
|
|
218
|
+
`(${finalPrefix})`, // ($), (€), etc.
|
|
200
219
|
);
|
|
201
220
|
}
|
|
202
|
-
|
|
203
|
-
// Add suffix-related incomplete patterns
|
|
221
|
+
|
|
222
|
+
// Add suffix-related incomplete patterns
|
|
204
223
|
if (suffix) {
|
|
205
224
|
incompletePatterns.push(
|
|
206
|
-
`-${suffix}`,
|
|
207
|
-
`(${suffix}`,
|
|
208
|
-
`(${suffix})
|
|
225
|
+
`-${suffix}`, // -%, etc.
|
|
226
|
+
`(${suffix}`, // (%, etc.
|
|
227
|
+
`(${suffix})`, // (%), etc.
|
|
209
228
|
);
|
|
210
|
-
|
|
229
|
+
|
|
211
230
|
// Combined prefix and suffix patterns
|
|
212
231
|
if (finalPrefix) {
|
|
213
232
|
incompletePatterns.push(
|
|
214
|
-
`-${finalPrefix}${suffix}`,
|
|
215
|
-
`(${finalPrefix}${suffix}`,
|
|
216
|
-
`(${finalPrefix}${suffix})
|
|
233
|
+
`-${finalPrefix}${suffix}`, // -$%, etc.
|
|
234
|
+
`(${finalPrefix}${suffix}`, // ($%, etc.
|
|
235
|
+
`(${finalPrefix}${suffix})`, // ($%), etc.
|
|
217
236
|
);
|
|
218
237
|
}
|
|
219
238
|
}
|
|
220
|
-
|
|
239
|
+
|
|
221
240
|
if (incompletePatterns.includes(inputValue)) {
|
|
222
241
|
// Keep the current value but update display for incomplete inputs
|
|
223
242
|
return;
|
|
224
243
|
}
|
|
225
|
-
|
|
244
|
+
|
|
226
245
|
const parsed = parseNumber(inputValue);
|
|
227
246
|
value = parsed;
|
|
228
247
|
}
|
|
@@ -244,15 +263,16 @@
|
|
|
244
263
|
function handleKeyDown(event: KeyboardEvent) {
|
|
245
264
|
// Allow control keys (backspace, delete, arrow keys, tab, etc.)
|
|
246
265
|
if (
|
|
247
|
-
event.key ===
|
|
248
|
-
event.key ===
|
|
249
|
-
event.key ===
|
|
250
|
-
event.key ===
|
|
251
|
-
event.key ===
|
|
252
|
-
event.key.startsWith(
|
|
253
|
-
event.key ===
|
|
254
|
-
event.key ===
|
|
255
|
-
|
|
266
|
+
event.key === "Backspace" ||
|
|
267
|
+
event.key === "Delete" ||
|
|
268
|
+
event.key === "Tab" ||
|
|
269
|
+
event.key === "Escape" ||
|
|
270
|
+
event.key === "Enter" ||
|
|
271
|
+
event.key.startsWith("Arrow") ||
|
|
272
|
+
event.key === "Home" ||
|
|
273
|
+
event.key === "End" ||
|
|
274
|
+
event.ctrlKey ||
|
|
275
|
+
event.metaKey // Allow Ctrl/Cmd shortcuts
|
|
256
276
|
) {
|
|
257
277
|
return;
|
|
258
278
|
}
|
|
@@ -261,18 +281,18 @@
|
|
|
261
281
|
const finalPrefix = currency || prefix;
|
|
262
282
|
const input = event.target as HTMLInputElement;
|
|
263
283
|
const cursorPosition = input.selectionStart || 0;
|
|
264
|
-
|
|
284
|
+
|
|
265
285
|
// Basic allowed characters
|
|
266
|
-
const allowedChars = new Set([
|
|
267
|
-
|
|
286
|
+
const allowedChars = new Set(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]);
|
|
287
|
+
|
|
268
288
|
// Always allow both comma and period for international input flexibility
|
|
269
|
-
allowedChars.add(
|
|
270
|
-
allowedChars.add(
|
|
271
|
-
|
|
289
|
+
allowedChars.add(",");
|
|
290
|
+
allowedChars.add(".");
|
|
291
|
+
|
|
272
292
|
// Add currency characters (but validate position)
|
|
273
293
|
if (finalPrefix && event.key === finalPrefix) {
|
|
274
294
|
// Only allow currency at the beginning (after minus if present)
|
|
275
|
-
const hasMinusAtStart = currentValue.startsWith(
|
|
295
|
+
const hasMinusAtStart = currentValue.startsWith("-");
|
|
276
296
|
const expectedPosition = hasMinusAtStart ? 1 : 0;
|
|
277
297
|
if (cursorPosition === expectedPosition && !currentValue.includes(finalPrefix)) {
|
|
278
298
|
allowedChars.add(finalPrefix);
|
|
@@ -281,7 +301,7 @@
|
|
|
281
301
|
return;
|
|
282
302
|
}
|
|
283
303
|
}
|
|
284
|
-
|
|
304
|
+
|
|
285
305
|
// Add suffix characters (but validate position and prevent duplicates)
|
|
286
306
|
if (suffix && event.key === suffix) {
|
|
287
307
|
// Only allow suffix at the end and if not already present
|
|
@@ -292,31 +312,35 @@
|
|
|
292
312
|
return;
|
|
293
313
|
}
|
|
294
314
|
}
|
|
295
|
-
|
|
315
|
+
|
|
296
316
|
// Always allow both minus and parentheses for input (negativeMode only affects display formatting)
|
|
297
|
-
if (event.key ===
|
|
317
|
+
if (event.key === "-") {
|
|
298
318
|
// Only allow minus at the very beginning and if not already present
|
|
299
|
-
if (cursorPosition === 0 && !currentValue.includes(
|
|
300
|
-
allowedChars.add(
|
|
319
|
+
if (cursorPosition === 0 && !currentValue.includes("-")) {
|
|
320
|
+
allowedChars.add("-");
|
|
301
321
|
} else {
|
|
302
322
|
event.preventDefault();
|
|
303
323
|
return;
|
|
304
324
|
}
|
|
305
325
|
}
|
|
306
|
-
|
|
307
|
-
if (event.key ===
|
|
326
|
+
|
|
327
|
+
if (event.key === "(") {
|
|
308
328
|
// Only allow opening parenthesis at the beginning and if not already present
|
|
309
|
-
if (cursorPosition === 0 && !currentValue.includes(
|
|
310
|
-
allowedChars.add(
|
|
329
|
+
if (cursorPosition === 0 && !currentValue.includes("(")) {
|
|
330
|
+
allowedChars.add("(");
|
|
311
331
|
} else {
|
|
312
332
|
event.preventDefault();
|
|
313
333
|
return;
|
|
314
334
|
}
|
|
315
335
|
}
|
|
316
|
-
if (event.key ===
|
|
336
|
+
if (event.key === ")") {
|
|
317
337
|
// Only allow closing parenthesis at the end, if opening exists, and no closing yet
|
|
318
|
-
if (
|
|
319
|
-
|
|
338
|
+
if (
|
|
339
|
+
cursorPosition === currentValue.length &&
|
|
340
|
+
currentValue.includes("(") &&
|
|
341
|
+
!currentValue.includes(")")
|
|
342
|
+
) {
|
|
343
|
+
allowedChars.add(")");
|
|
320
344
|
} else {
|
|
321
345
|
event.preventDefault();
|
|
322
346
|
return;
|
|
@@ -331,6 +355,23 @@
|
|
|
331
355
|
|
|
332
356
|
// Convert our number value to string for the Input component
|
|
333
357
|
let stringValue = $derived(displayValue);
|
|
358
|
+
|
|
359
|
+
// Use native <input type="number"> when the caller isn't using any
|
|
360
|
+
// of the custom-formatting features (prefix / suffix / currency /
|
|
361
|
+
// thousands / non-`.` decimal / paren-negative). That gives them
|
|
362
|
+
// the browser spinner + arrow-key increment + guaranteed mobile
|
|
363
|
+
// numeric keypad. When formatting IS used, stay on type="text" so
|
|
364
|
+
// the browser doesn't strip `$`, `%`, `,`, `(`, etc.
|
|
365
|
+
const inputType = $derived(
|
|
366
|
+
prefix ||
|
|
367
|
+
suffix ||
|
|
368
|
+
currency ||
|
|
369
|
+
enableThousandsSeparator ||
|
|
370
|
+
decimalSeparator !== "." ||
|
|
371
|
+
negativeMode === "parentheses"
|
|
372
|
+
? "text"
|
|
373
|
+
: "number",
|
|
374
|
+
);
|
|
334
375
|
</script>
|
|
335
376
|
|
|
336
377
|
<Input
|
|
@@ -338,7 +379,7 @@
|
|
|
338
379
|
bind:valid
|
|
339
380
|
bind:touched
|
|
340
381
|
bind:element
|
|
341
|
-
type=
|
|
382
|
+
type={inputType}
|
|
342
383
|
inputmode="numeric"
|
|
343
384
|
validate={(val) => {
|
|
344
385
|
const parsed = parseNumber(val);
|
|
@@ -350,4 +391,4 @@
|
|
|
350
391
|
onkeydown={handleKeyDown}
|
|
351
392
|
{placeholder}
|
|
352
393
|
{...restProps}
|
|
353
|
-
/>
|
|
394
|
+
/>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { HTMLInputAttributes } from
|
|
2
|
-
|
|
1
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
2
|
+
import type { Size } from "../types/sizes.js";
|
|
3
|
+
import type { Responsive } from "../types/responsive.js";
|
|
4
|
+
import type { Variant } from "../types/variants.js";
|
|
5
|
+
interface Props extends Omit<HTMLInputAttributes, "type" | "value" | "size"> {
|
|
3
6
|
value: number | null;
|
|
4
7
|
class?: string;
|
|
5
8
|
inputClass?: string;
|
|
@@ -7,6 +10,10 @@ interface Props extends Omit<HTMLInputAttributes, 'type' | 'value'> {
|
|
|
7
10
|
valid?: boolean;
|
|
8
11
|
touched?: boolean;
|
|
9
12
|
element?: HTMLInputElement;
|
|
13
|
+
size?: Responsive<Size>;
|
|
14
|
+
/** Shared axes — forwarded to the underlying Input. */
|
|
15
|
+
variant?: Variant;
|
|
16
|
+
borderless?: boolean;
|
|
10
17
|
min?: number;
|
|
11
18
|
max?: number;
|
|
12
19
|
step?: number;
|
|
@@ -18,8 +25,8 @@ interface Props extends Omit<HTMLInputAttributes, 'type' | 'value'> {
|
|
|
18
25
|
currency?: string;
|
|
19
26
|
decimalSeparator?: string;
|
|
20
27
|
thousandsChar?: string;
|
|
21
|
-
roundingMode?:
|
|
22
|
-
negativeMode?:
|
|
28
|
+
roundingMode?: "round" | "floor" | "ceil" | "trunc";
|
|
29
|
+
negativeMode?: "minus" | "parentheses" | "both";
|
|
23
30
|
}
|
|
24
31
|
declare const NumberInput: import("svelte").Component<Props, {}, "element" | "value" | "valid" | "touched">;
|
|
25
32
|
type NumberInput = ReturnType<typeof NumberInput>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Input from "./Input.svelte";
|
|
3
|
+
import Eye from "~icons/mdi/eye";
|
|
4
|
+
import EyeOff from "~icons/mdi/eye-off";
|
|
5
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
6
|
+
import type { Size } from "../types/sizes.js";
|
|
7
|
+
import type { Responsive } from "../types/responsive.js";
|
|
8
|
+
import type { Variant } from "../types/variants.js";
|
|
9
|
+
|
|
10
|
+
interface Props extends Omit<HTMLInputAttributes, "type" | "size"> {
|
|
11
|
+
value: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
inputClass?: string;
|
|
14
|
+
validate?: (value: string) => boolean | string;
|
|
15
|
+
valid?: boolean;
|
|
16
|
+
touched?: boolean;
|
|
17
|
+
element?: HTMLInputElement;
|
|
18
|
+
size?: Responsive<Size>;
|
|
19
|
+
/** Shared axes — forwarded to the underlying Input. */
|
|
20
|
+
variant?: Variant;
|
|
21
|
+
borderless?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
value = $bindable(),
|
|
26
|
+
valid = $bindable(true),
|
|
27
|
+
touched = $bindable(false),
|
|
28
|
+
element = $bindable(),
|
|
29
|
+
...restProps
|
|
30
|
+
}: Props = $props();
|
|
31
|
+
|
|
32
|
+
let showPassword = $state(false);
|
|
33
|
+
|
|
34
|
+
function toggleVisibility() {
|
|
35
|
+
showPassword = !showPassword;
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<Input
|
|
40
|
+
bind:value
|
|
41
|
+
bind:valid
|
|
42
|
+
bind:touched
|
|
43
|
+
bind:element
|
|
44
|
+
type={showPassword ? "text" : "password"}
|
|
45
|
+
iconClickable={true}
|
|
46
|
+
onIconClick={toggleVisibility}
|
|
47
|
+
{...restProps}
|
|
48
|
+
>
|
|
49
|
+
{#snippet icon()}
|
|
50
|
+
{#if showPassword}<EyeOff />{:else}<Eye />{/if}
|
|
51
|
+
{/snippet}
|
|
52
|
+
</Input>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HTMLInputAttributes } from "svelte/elements";
|
|
2
|
+
import type { Size } from "../types/sizes.js";
|
|
3
|
+
import type { Responsive } from "../types/responsive.js";
|
|
4
|
+
import type { Variant } from "../types/variants.js";
|
|
5
|
+
interface Props extends Omit<HTMLInputAttributes, "type" | "size"> {
|
|
6
|
+
value: string;
|
|
7
|
+
class?: string;
|
|
8
|
+
inputClass?: string;
|
|
9
|
+
validate?: (value: string) => boolean | string;
|
|
10
|
+
valid?: boolean;
|
|
11
|
+
touched?: boolean;
|
|
12
|
+
element?: HTMLInputElement;
|
|
13
|
+
size?: Responsive<Size>;
|
|
14
|
+
/** Shared axes — forwarded to the underlying Input. */
|
|
15
|
+
variant?: Variant;
|
|
16
|
+
borderless?: boolean;
|
|
17
|
+
}
|
|
18
|
+
declare const PasswordInput: import("svelte").Component<Props, {}, "element" | "value" | "valid" | "touched">;
|
|
19
|
+
type PasswordInput = ReturnType<typeof PasswordInput>;
|
|
20
|
+
export default PasswordInput;
|