@lavalogic/scoria 0.40.1 → 0.40.2
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 +58 -58
- package/dist/Components/AccordionGroup.svelte +46 -46
- package/dist/Components/AccordionGroupButton.svelte +76 -76
- package/dist/Components/Action.svelte +136 -136
- package/dist/Components/AlertModal.svelte +195 -195
- package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +729 -729
- package/dist/Components/Base/BaseModal.svelte +125 -125
- package/dist/Components/Base/ContextWrapper.svelte +72 -72
- package/dist/Components/Base/Pagination.svelte +198 -198
- package/dist/Components/Base/Snippets.svelte +36 -36
- package/dist/Components/BigRadioSet.svelte +38 -38
- package/dist/Components/Bubble.svelte +101 -101
- package/dist/Components/Button.svelte +209 -209
- package/dist/Components/Checkbox.svelte +86 -86
- package/dist/Components/CollapsibleCard.svelte +78 -78
- package/dist/Components/Contexts/ContextMenu.svelte +164 -164
- package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
- package/dist/Components/Contexts/ContextMenuOption.svelte +60 -60
- package/dist/Components/Contexts/Toast.svelte +86 -86
- package/dist/Components/Contexts/ToastContainer.svelte +65 -65
- package/dist/Components/Contexts/Tooltip.svelte +37 -37
- package/dist/Components/DataMatrixIcon.svelte +112 -112
- package/dist/Components/DateInput.svelte +148 -148
- package/dist/Components/DatePicker.svelte +300 -300
- package/dist/Components/DesktopModal.svelte +90 -90
- package/dist/Components/Dial.svelte +204 -204
- package/dist/Components/DimensionInput.svelte +170 -170
- package/dist/Components/DragMenu/DragMenuHolder.svelte +119 -119
- package/dist/Components/DragMenu/DraggableCard.svelte +77 -77
- package/dist/Components/FileCard.svelte +24 -24
- package/dist/Components/FileUpload.svelte +236 -236
- package/dist/Components/GridInput.svelte +196 -196
- package/dist/Components/GridInputSet.svelte +29 -29
- package/dist/Components/HorizontalTabGroup.svelte +82 -82
- package/dist/Components/HorizontalTabGroupButton.svelte +44 -44
- package/dist/Components/Icon.svelte +128 -128
- package/dist/Components/Icons.js +357 -357
- package/dist/Components/InfoAlert.svelte +35 -35
- package/dist/Components/LoadingAnimation.svelte +29 -29
- package/dist/Components/LoadingModal.svelte +45 -45
- package/dist/Components/LoadingOverlay.svelte +25 -25
- package/dist/Components/Modal.svelte +258 -258
- package/dist/Components/MultiSelect.svelte +220 -220
- package/dist/Components/Nav/Nav.svelte +110 -110
- package/dist/Components/Nav/NavServiceMenuList.svelte +43 -43
- package/dist/Components/Nav/NavTab.svelte +62 -62
- package/dist/Components/Nav/ServiceMenuItem.svelte +112 -112
- package/dist/Components/Nav/ServicesNav.svelte +80 -80
- package/dist/Components/NumberInput.svelte +157 -157
- package/dist/Components/PageHeading.svelte +25 -25
- package/dist/Components/PasswordInput.svelte +88 -88
- package/dist/Components/Portal.svelte +36 -36
- package/dist/Components/ProgressBubble.svelte +107 -107
- package/dist/Components/QuerySelect.svelte +366 -366
- package/dist/Components/SingleSelect.svelte +250 -250
- package/dist/Components/StepButton.svelte +70 -70
- package/dist/Components/StepForm.svelte +140 -140
- package/dist/Components/Switch.svelte +69 -69
- package/dist/Components/Table/ARCHITECTURE.md +298 -298
- package/dist/Components/Table/Body/QuickSearchCell.svelte +679 -679
- package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +51 -51
- package/dist/Components/Table/Body/Rows/ColumnList.svelte +59 -59
- package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +454 -454
- package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +79 -79
- package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +185 -185
- package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +124 -124
- package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +131 -120
- package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +234 -234
- package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +105 -105
- package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +87 -87
- package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +126 -126
- package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +159 -159
- package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +246 -246
- package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +216 -216
- package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +252 -252
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +179 -179
- package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +221 -221
- package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +197 -197
- package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +190 -190
- package/dist/Components/Table/Body/Rows/TableRow.svelte +290 -290
- package/dist/Components/Table/Body/TableBody.svelte +155 -155
- package/dist/Components/Table/ColumnFactory.svelte.js +1 -0
- package/dist/Components/Table/Headers/HighlightAllHeader.svelte +41 -41
- package/dist/Components/Table/Headers/SelectAllHeader.svelte +30 -30
- package/dist/Components/Table/Headers/TableHead.svelte +415 -415
- package/dist/Components/Table/Misc/ColumnPanel.svelte +39 -39
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte +39 -39
- package/dist/Components/Table/Misc/FilterInput.svelte +641 -641
- package/dist/Components/Table/Misc/FilterPanel.svelte +190 -190
- package/dist/Components/Table/Misc/FilterPanelModal.svelte +31 -31
- package/dist/Components/Table/Misc/ShareViewModal.svelte +291 -291
- package/dist/Components/Table/Misc/SidePanel.svelte +35 -35
- package/dist/Components/Table/Misc/TableConfigurationModal.svelte +644 -644
- package/dist/Components/Table/Misc/TableFooter.svelte +340 -340
- package/dist/Components/Table/Misc/TableHorizontalBar.svelte +140 -140
- package/dist/Components/Table/Misc/TableSidebar.svelte +200 -200
- package/dist/Components/Table/Misc/TableViewDropdown.svelte +105 -105
- package/dist/Components/Table/Misc/ToolboxPanel.svelte +100 -100
- package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +107 -107
- package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +192 -192
- package/dist/Components/Table/NestedTable.svelte +130 -130
- package/dist/Components/Table/Table.svelte +692 -692
- package/dist/Components/Table/Types/Columns/Definitions/Expand/ExpandDef.svelte.d.ts +5 -0
- package/dist/Components/Table/Types/Columns/Definitions/Expand/ExpandDef.svelte.js +3 -0
- package/dist/Components/Table/Types/Public/ColumnOptions.d.ts +10 -0
- package/dist/Components/Test/TestContextConsumer.svelte +13 -13
- package/dist/Components/TextArea.svelte +130 -130
- package/dist/Components/TextInput.svelte +176 -176
- package/dist/Components/ThreeStateRadio.svelte +131 -131
- package/dist/Components/Touch/BoolCard.svelte +45 -45
- package/dist/Components/Touch/DateModal.svelte +209 -209
- package/dist/Components/Touch/EditCard.svelte +59 -59
- package/dist/Components/Touch/InfoCard.svelte +31 -31
- package/dist/Components/Touch/InfoTextCard.svelte +28 -28
- package/dist/Components/Touch/ModalExplanation.svelte +17 -17
- package/dist/Components/Touch/NumberKeyboard.svelte +225 -225
- package/dist/Components/Touch/NumberModal.svelte +79 -79
- package/dist/Components/Touch/PlusMinusCard.svelte +46 -46
- package/dist/Components/Touch/SelectModal.svelte +127 -127
- package/dist/Components/Touch/SummaryCard.svelte +93 -93
- package/dist/Components/Touch/TextAreaModal.svelte +95 -95
- package/dist/Components/Touch/TextModal.svelte +106 -106
- package/dist/Components/Touch/TouchCard.svelte +33 -33
- package/dist/Components/Touch/TouchModal.svelte +274 -274
- package/dist/Components/Touch/UtilityButton.svelte +109 -109
- package/dist/Components/VerticalTabGroup.svelte +79 -79
- package/dist/Components/VerticalTabGroupButton.svelte +58 -58
- package/dist/Helpers/Datamatrix.d.ts +19 -19
- package/dist/Helpers/Datamatrix.js +291 -291
- package/dist/Types/Examples/ExampleModal.svelte +35 -35
- package/dist/scss/_basics.scss +12 -12
- package/dist/scss/_colours.scss +134 -134
- package/dist/scss/_mixins.scss +3392 -3392
- package/dist/scss/_motion.scss +57 -57
- package/dist/scss/_sizing.scss +85 -85
- package/dist/scss/_transitions.scss +8 -8
- package/package.json +1 -1
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script lang="ts">
|
|
4
|
-
import { Size } from '../../Types/Internal/Size.js';
|
|
5
|
-
import Icon from '../Icon.svelte';
|
|
6
|
-
import type { InfoCardProps } from './InfoCardProps.js';
|
|
7
|
-
|
|
8
|
-
const { noborder, label, iconName, children }: InfoCardProps = $props();
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<div
|
|
12
|
-
class="card"
|
|
13
|
-
class:pad-vertical={!label}
|
|
14
|
-
class:noborder
|
|
15
|
-
>
|
|
16
|
-
{#if label}
|
|
17
|
-
<span class="card-label">{label}</span>
|
|
18
|
-
{/if}
|
|
19
|
-
|
|
20
|
-
<span class="value">{@render children()}</span>
|
|
21
|
-
{#if iconName}
|
|
22
|
-
<div class="icon">
|
|
23
|
-
<Icon
|
|
24
|
-
name={iconName}
|
|
25
|
-
size={Size.Large}
|
|
26
|
-
/>
|
|
27
|
-
</div>
|
|
28
|
-
{/if}
|
|
29
|
-
</div>
|
|
30
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { Size } from '../../Types/Internal/Size.js';
|
|
5
|
+
import Icon from '../Icon.svelte';
|
|
6
|
+
import type { InfoCardProps } from './InfoCardProps.js';
|
|
7
|
+
|
|
8
|
+
const { noborder, label, iconName, children }: InfoCardProps = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div
|
|
12
|
+
class="card"
|
|
13
|
+
class:pad-vertical={!label}
|
|
14
|
+
class:noborder
|
|
15
|
+
>
|
|
16
|
+
{#if label}
|
|
17
|
+
<span class="card-label">{label}</span>
|
|
18
|
+
{/if}
|
|
19
|
+
|
|
20
|
+
<span class="value">{@render children()}</span>
|
|
21
|
+
{#if iconName}
|
|
22
|
+
<div class="icon">
|
|
23
|
+
<Icon
|
|
24
|
+
name={iconName}
|
|
25
|
+
size={Size.Large}
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
{/if}
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
31
|
<style>.card {
|
|
32
32
|
color: #3a4952;
|
|
33
33
|
fill: #3a4952;
|
|
@@ -77,4 +77,4 @@
|
|
|
77
77
|
align-items: center;
|
|
78
78
|
position: absolute;
|
|
79
79
|
right: 0.75rem;
|
|
80
|
-
}</style>
|
|
80
|
+
}</style>
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script lang="ts">
|
|
4
|
-
import { Colour } from '../../scss/colours.js';
|
|
5
|
-
import { Size } from '../../Types/Internal/Size.js';
|
|
6
|
-
import Icon from '../Icon.svelte';
|
|
7
|
-
import type { InfoTextCardProps } from './InfoTextCardProps.js';
|
|
8
|
-
let { label, iconName, children }: InfoTextCardProps = $props();
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<div
|
|
12
|
-
class="card"
|
|
13
|
-
class:pad-vertical={!label}
|
|
14
|
-
>
|
|
15
|
-
{#if iconName}
|
|
16
|
-
<div class="icon">
|
|
17
|
-
<Icon
|
|
18
|
-
name={iconName}
|
|
19
|
-
size={Size.Large}
|
|
20
|
-
colour={Colour['$brand-primary']}
|
|
21
|
-
/>
|
|
22
|
-
</div>
|
|
23
|
-
{/if}
|
|
24
|
-
|
|
25
|
-
<span class="value">{@render children()}</span>
|
|
26
|
-
</div>
|
|
27
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { Colour } from '../../scss/colours.js';
|
|
5
|
+
import { Size } from '../../Types/Internal/Size.js';
|
|
6
|
+
import Icon from '../Icon.svelte';
|
|
7
|
+
import type { InfoTextCardProps } from './InfoTextCardProps.js';
|
|
8
|
+
let { label, iconName, children }: InfoTextCardProps = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div
|
|
12
|
+
class="card"
|
|
13
|
+
class:pad-vertical={!label}
|
|
14
|
+
>
|
|
15
|
+
{#if iconName}
|
|
16
|
+
<div class="icon">
|
|
17
|
+
<Icon
|
|
18
|
+
name={iconName}
|
|
19
|
+
size={Size.Large}
|
|
20
|
+
colour={Colour['$brand-primary']}
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
{/if}
|
|
24
|
+
|
|
25
|
+
<span class="value">{@render children()}</span>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
28
|
<style>.card {
|
|
29
29
|
color: #3a4952;
|
|
30
30
|
fill: #3a4952;
|
|
@@ -50,4 +50,4 @@
|
|
|
50
50
|
.card .value {
|
|
51
51
|
padding: 0 0.25rem;
|
|
52
52
|
color: #3a4952;
|
|
53
|
-
}</style>
|
|
53
|
+
}</style>
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script lang="ts">
|
|
4
|
-
import type { ModalExplanationProps } from './ModalExplanationProps.js';
|
|
5
|
-
|
|
6
|
-
let { width, children, nowrap }: ModalExplanationProps = $props();
|
|
7
|
-
</script>
|
|
8
|
-
|
|
9
|
-
<p
|
|
10
|
-
class:nowrap
|
|
11
|
-
style:--width={width}
|
|
12
|
-
style:--padding="0"
|
|
13
|
-
>
|
|
14
|
-
{@render children()}
|
|
15
|
-
</p>
|
|
16
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import type { ModalExplanationProps } from './ModalExplanationProps.js';
|
|
5
|
+
|
|
6
|
+
let { width, children, nowrap }: ModalExplanationProps = $props();
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<p
|
|
10
|
+
class:nowrap
|
|
11
|
+
style:--width={width}
|
|
12
|
+
style:--padding="0"
|
|
13
|
+
>
|
|
14
|
+
{@render children()}
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
17
|
<style>p {
|
|
18
18
|
padding: var(--padding);
|
|
19
19
|
color: #3a4952;
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
}
|
|
22
22
|
p.nowrap {
|
|
23
23
|
white-space: pre-line;
|
|
24
|
-
}</style>
|
|
24
|
+
}</style>
|
|
@@ -1,227 +1,227 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script
|
|
4
|
-
module
|
|
5
|
-
lang="ts"
|
|
6
|
-
>
|
|
7
|
-
import { devCatch, passthrough } from '../../Helpers/Helpers.svelte.js';
|
|
8
|
-
import { InputVariant } from '../../Types/Internal/InputVariant.js';
|
|
9
|
-
import { tick, untrack } from 'svelte';
|
|
10
|
-
import Icon from '../Icon.svelte';
|
|
11
|
-
import TextInput from '../TextInput.svelte';
|
|
12
|
-
import type { NumberKeyboardProps } from './NumberKeyboardProps.js';
|
|
13
|
-
|
|
14
|
-
const DIGITS = Object.freeze([1, 2, 3, 4, 5, 6, 7, 8, 9] as const);
|
|
15
|
-
</script>
|
|
16
|
-
|
|
17
|
-
<script lang="ts">
|
|
18
|
-
let {
|
|
19
|
-
value = $bindable(null),
|
|
20
|
-
label,
|
|
21
|
-
labelRight,
|
|
22
|
-
placeholder,
|
|
23
|
-
min,
|
|
24
|
-
max,
|
|
25
|
-
decimal = false,
|
|
26
|
-
inputRef = $bindable(),
|
|
27
|
-
coerceValue = false,
|
|
28
|
-
}: NumberKeyboardProps = $props();
|
|
29
|
-
|
|
30
|
-
let _value: string = $derived(
|
|
31
|
-
value != null && value % 1 == 0 ? value.toString() : getDecimalValue()
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
function getDecimalPlaces(value: number): number {
|
|
35
|
-
const str = value.toString();
|
|
36
|
-
if (!str.includes('.')) {
|
|
37
|
-
return decimal ? 2 : 0;
|
|
38
|
-
}
|
|
39
|
-
return str.split('.')[1].length;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const decimalPlaces = $derived(
|
|
43
|
-
min != null ? getDecimalPlaces(min) : max != null ? getDecimalPlaces(max) : decimal ? 2 : 0
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
function getDecimalValue(): string {
|
|
47
|
-
// Use toFixed to bound decimal places, then parseFloat to drop trailing
|
|
48
|
-
// zeros without re-introducing scientific notation. There is no built-in
|
|
49
|
-
// "as many decimals as requested but no more" formatter.
|
|
50
|
-
return value ? parseFloat(value.toFixed(decimalPlaces)).toString() : '';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const newValue = $derived.by(() => {
|
|
54
|
-
const v = decimal ? parseFloat(_value) : parseInt(_value);
|
|
55
|
-
if (isNaN(v)) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
return v;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
$effect(() => {
|
|
62
|
-
if (coerceValue) {
|
|
63
|
-
const v =
|
|
64
|
-
min != undefined && newValue != null && min > newValue
|
|
65
|
-
? min
|
|
66
|
-
: max != undefined && newValue != null && max < newValue
|
|
67
|
-
? max
|
|
68
|
-
: newValue;
|
|
69
|
-
|
|
70
|
-
value = v;
|
|
71
|
-
} else {
|
|
72
|
-
value = newValue;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
function refocus() {
|
|
77
|
-
tick()
|
|
78
|
-
.then(() => {
|
|
79
|
-
if (inputRef && document.activeElement !== inputRef) {
|
|
80
|
-
inputRef.focus();
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
.catch(devCatch);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function getPrevValue() {
|
|
87
|
-
if (inputRef) {
|
|
88
|
-
if (
|
|
89
|
-
(inputRef.selectionStart === 0 && inputRef.selectionEnd === _value.length) ||
|
|
90
|
-
(inputRef.selectionEnd === 0 && inputRef.selectionStart === _value.length)
|
|
91
|
-
) {
|
|
92
|
-
return '';
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return _value;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function addDecimal() {
|
|
99
|
-
_value = getPrevValue() + '.';
|
|
100
|
-
untrack(refocus);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/* due to the fact that number inputs won't allow a leading dot, we have to use a text input
|
|
104
|
-
this doesn't add a number, but concatenated the digit to the end of the string
|
|
105
|
-
*/
|
|
106
|
-
function addDigit(digit: string) {
|
|
107
|
-
const prevValue = getPrevValue();
|
|
108
|
-
_value = prevValue + digit;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function backspace() {
|
|
112
|
-
const preVvalue = getPrevValue();
|
|
113
|
-
const v = preVvalue && _value.slice(0, _value.length - 1);
|
|
114
|
-
_value = v;
|
|
115
|
-
untrack(refocus);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function minus() {
|
|
119
|
-
const v = ((decimal ? parseFloat(_value) : parseInt(_value)) || 0) - 1;
|
|
120
|
-
|
|
121
|
-
_value = (min != null ? Math.max(min, v) : v).toString();
|
|
122
|
-
untrack(refocus);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function plus() {
|
|
126
|
-
const v = ((decimal ? parseFloat(_value) : parseInt(_value)) || 0) + 1;
|
|
127
|
-
|
|
128
|
-
_value = (max != null ? Math.min(max, v) : v).toString();
|
|
129
|
-
untrack(refocus);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const lessThanMin = $derived(min != null && (value == null || value < min));
|
|
133
|
-
const moreThanMax = $derived(max != null && (value == null || value > max));
|
|
134
|
-
|
|
135
|
-
// Precompute disabled state per digit so the 10 buttons do not each evaluate
|
|
136
|
-
// the same arithmetic on every render.
|
|
137
|
-
const digitDisabled = $derived.by(() => {
|
|
138
|
-
const out = new Array<boolean>(10);
|
|
139
|
-
const base = (value ?? 0) * 10;
|
|
140
|
-
for (let i = 0; i < 10; i++) {
|
|
141
|
-
out[i] = max != null && base + i > max;
|
|
142
|
-
}
|
|
143
|
-
return out;
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
function onDigitClick(e: MouseEvent) {
|
|
147
|
-
const digit = (e.currentTarget as HTMLButtonElement).dataset.digit;
|
|
148
|
-
if (digit != null) {
|
|
149
|
-
addDigit(digit);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
</script>
|
|
153
|
-
|
|
154
|
-
<div class="number-input-wrapper">
|
|
155
|
-
<div class="left-side">
|
|
156
|
-
<div class="value-container">
|
|
157
|
-
<!-- textInput is the only available option when you try to add a decimal point programmatically -->
|
|
158
|
-
<TextInput
|
|
159
|
-
labelTop={label}
|
|
160
|
-
{placeholder}
|
|
161
|
-
{labelRight}
|
|
162
|
-
bind:inputRef
|
|
163
|
-
bind:value={_value}
|
|
164
|
-
variant={InputVariant.Modal}
|
|
165
|
-
required
|
|
166
|
-
invalid={lessThanMin || moreThanMax}
|
|
167
|
-
/>
|
|
168
|
-
{#if max != undefined}
|
|
169
|
-
<TextInput
|
|
170
|
-
value={`${max}`}
|
|
171
|
-
labelTop="Maximum"
|
|
172
|
-
selectNone
|
|
173
|
-
disabled
|
|
174
|
-
variant={InputVariant.Modal}
|
|
175
|
-
/>
|
|
176
|
-
{/if}
|
|
177
|
-
</div>
|
|
178
|
-
<div class="plus-minus">
|
|
179
|
-
<button
|
|
180
|
-
onkeydown={passthrough}
|
|
181
|
-
type="button"
|
|
182
|
-
disabled={value != null && min != null && value <= min}
|
|
183
|
-
onclick={minus}><Icon name="minus" /></button
|
|
184
|
-
>
|
|
185
|
-
<button
|
|
186
|
-
onkeydown={passthrough}
|
|
187
|
-
type="button"
|
|
188
|
-
disabled={value != null && max != null && value >= max}
|
|
189
|
-
onclick={plus}><Icon name="plus" /></button
|
|
190
|
-
>
|
|
191
|
-
</div>
|
|
192
|
-
</div>
|
|
193
|
-
<div class="number-grid">
|
|
194
|
-
{#each DIGITS as digit (digit)}
|
|
195
|
-
<button
|
|
196
|
-
onkeydown={passthrough}
|
|
197
|
-
type="button"
|
|
198
|
-
data-digit={digit}
|
|
199
|
-
disabled={digitDisabled[digit]}
|
|
200
|
-
onclick={onDigitClick}>{digit}</button
|
|
201
|
-
>
|
|
202
|
-
{/each}
|
|
203
|
-
<button
|
|
204
|
-
onkeydown={passthrough}
|
|
205
|
-
type="button"
|
|
206
|
-
disabled={!decimal || (value != null && value % 1 !== 0) || _value.endsWith('.')}
|
|
207
|
-
onclick={addDecimal}>.</button
|
|
208
|
-
>
|
|
209
|
-
<button
|
|
210
|
-
onkeydown={passthrough}
|
|
211
|
-
type="button"
|
|
212
|
-
data-digit="0"
|
|
213
|
-
disabled={digitDisabled[0]}
|
|
214
|
-
onclick={onDigitClick}>0</button
|
|
215
|
-
>
|
|
216
|
-
<button
|
|
217
|
-
onkeydown={passthrough}
|
|
218
|
-
type="button"
|
|
219
|
-
disabled={_value.length === 0}
|
|
220
|
-
onclick={backspace}>⌫</button
|
|
221
|
-
>
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script
|
|
4
|
+
module
|
|
5
|
+
lang="ts"
|
|
6
|
+
>
|
|
7
|
+
import { devCatch, passthrough } from '../../Helpers/Helpers.svelte.js';
|
|
8
|
+
import { InputVariant } from '../../Types/Internal/InputVariant.js';
|
|
9
|
+
import { tick, untrack } from 'svelte';
|
|
10
|
+
import Icon from '../Icon.svelte';
|
|
11
|
+
import TextInput from '../TextInput.svelte';
|
|
12
|
+
import type { NumberKeyboardProps } from './NumberKeyboardProps.js';
|
|
13
|
+
|
|
14
|
+
const DIGITS = Object.freeze([1, 2, 3, 4, 5, 6, 7, 8, 9] as const);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
let {
|
|
19
|
+
value = $bindable(null),
|
|
20
|
+
label,
|
|
21
|
+
labelRight,
|
|
22
|
+
placeholder,
|
|
23
|
+
min,
|
|
24
|
+
max,
|
|
25
|
+
decimal = false,
|
|
26
|
+
inputRef = $bindable(),
|
|
27
|
+
coerceValue = false,
|
|
28
|
+
}: NumberKeyboardProps = $props();
|
|
29
|
+
|
|
30
|
+
let _value: string = $derived(
|
|
31
|
+
value != null && value % 1 == 0 ? value.toString() : getDecimalValue()
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
function getDecimalPlaces(value: number): number {
|
|
35
|
+
const str = value.toString();
|
|
36
|
+
if (!str.includes('.')) {
|
|
37
|
+
return decimal ? 2 : 0;
|
|
38
|
+
}
|
|
39
|
+
return str.split('.')[1].length;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const decimalPlaces = $derived(
|
|
43
|
+
min != null ? getDecimalPlaces(min) : max != null ? getDecimalPlaces(max) : decimal ? 2 : 0
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
function getDecimalValue(): string {
|
|
47
|
+
// Use toFixed to bound decimal places, then parseFloat to drop trailing
|
|
48
|
+
// zeros without re-introducing scientific notation. There is no built-in
|
|
49
|
+
// "as many decimals as requested but no more" formatter.
|
|
50
|
+
return value ? parseFloat(value.toFixed(decimalPlaces)).toString() : '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const newValue = $derived.by(() => {
|
|
54
|
+
const v = decimal ? parseFloat(_value) : parseInt(_value);
|
|
55
|
+
if (isNaN(v)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return v;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
$effect(() => {
|
|
62
|
+
if (coerceValue) {
|
|
63
|
+
const v =
|
|
64
|
+
min != undefined && newValue != null && min > newValue
|
|
65
|
+
? min
|
|
66
|
+
: max != undefined && newValue != null && max < newValue
|
|
67
|
+
? max
|
|
68
|
+
: newValue;
|
|
69
|
+
|
|
70
|
+
value = v;
|
|
71
|
+
} else {
|
|
72
|
+
value = newValue;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
function refocus() {
|
|
77
|
+
tick()
|
|
78
|
+
.then(() => {
|
|
79
|
+
if (inputRef && document.activeElement !== inputRef) {
|
|
80
|
+
inputRef.focus();
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
.catch(devCatch);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getPrevValue() {
|
|
87
|
+
if (inputRef) {
|
|
88
|
+
if (
|
|
89
|
+
(inputRef.selectionStart === 0 && inputRef.selectionEnd === _value.length) ||
|
|
90
|
+
(inputRef.selectionEnd === 0 && inputRef.selectionStart === _value.length)
|
|
91
|
+
) {
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return _value;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function addDecimal() {
|
|
99
|
+
_value = getPrevValue() + '.';
|
|
100
|
+
untrack(refocus);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* due to the fact that number inputs won't allow a leading dot, we have to use a text input
|
|
104
|
+
this doesn't add a number, but concatenated the digit to the end of the string
|
|
105
|
+
*/
|
|
106
|
+
function addDigit(digit: string) {
|
|
107
|
+
const prevValue = getPrevValue();
|
|
108
|
+
_value = prevValue + digit;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function backspace() {
|
|
112
|
+
const preVvalue = getPrevValue();
|
|
113
|
+
const v = preVvalue && _value.slice(0, _value.length - 1);
|
|
114
|
+
_value = v;
|
|
115
|
+
untrack(refocus);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function minus() {
|
|
119
|
+
const v = ((decimal ? parseFloat(_value) : parseInt(_value)) || 0) - 1;
|
|
120
|
+
|
|
121
|
+
_value = (min != null ? Math.max(min, v) : v).toString();
|
|
122
|
+
untrack(refocus);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function plus() {
|
|
126
|
+
const v = ((decimal ? parseFloat(_value) : parseInt(_value)) || 0) + 1;
|
|
127
|
+
|
|
128
|
+
_value = (max != null ? Math.min(max, v) : v).toString();
|
|
129
|
+
untrack(refocus);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const lessThanMin = $derived(min != null && (value == null || value < min));
|
|
133
|
+
const moreThanMax = $derived(max != null && (value == null || value > max));
|
|
134
|
+
|
|
135
|
+
// Precompute disabled state per digit so the 10 buttons do not each evaluate
|
|
136
|
+
// the same arithmetic on every render.
|
|
137
|
+
const digitDisabled = $derived.by(() => {
|
|
138
|
+
const out = new Array<boolean>(10);
|
|
139
|
+
const base = (value ?? 0) * 10;
|
|
140
|
+
for (let i = 0; i < 10; i++) {
|
|
141
|
+
out[i] = max != null && base + i > max;
|
|
142
|
+
}
|
|
143
|
+
return out;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
function onDigitClick(e: MouseEvent) {
|
|
147
|
+
const digit = (e.currentTarget as HTMLButtonElement).dataset.digit;
|
|
148
|
+
if (digit != null) {
|
|
149
|
+
addDigit(digit);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
</script>
|
|
153
|
+
|
|
154
|
+
<div class="number-input-wrapper">
|
|
155
|
+
<div class="left-side">
|
|
156
|
+
<div class="value-container">
|
|
157
|
+
<!-- textInput is the only available option when you try to add a decimal point programmatically -->
|
|
158
|
+
<TextInput
|
|
159
|
+
labelTop={label}
|
|
160
|
+
{placeholder}
|
|
161
|
+
{labelRight}
|
|
162
|
+
bind:inputRef
|
|
163
|
+
bind:value={_value}
|
|
164
|
+
variant={InputVariant.Modal}
|
|
165
|
+
required
|
|
166
|
+
invalid={lessThanMin || moreThanMax}
|
|
167
|
+
/>
|
|
168
|
+
{#if max != undefined}
|
|
169
|
+
<TextInput
|
|
170
|
+
value={`${max}`}
|
|
171
|
+
labelTop="Maximum"
|
|
172
|
+
selectNone
|
|
173
|
+
disabled
|
|
174
|
+
variant={InputVariant.Modal}
|
|
175
|
+
/>
|
|
176
|
+
{/if}
|
|
177
|
+
</div>
|
|
178
|
+
<div class="plus-minus">
|
|
179
|
+
<button
|
|
180
|
+
onkeydown={passthrough}
|
|
181
|
+
type="button"
|
|
182
|
+
disabled={value != null && min != null && value <= min}
|
|
183
|
+
onclick={minus}><Icon name="minus" /></button
|
|
184
|
+
>
|
|
185
|
+
<button
|
|
186
|
+
onkeydown={passthrough}
|
|
187
|
+
type="button"
|
|
188
|
+
disabled={value != null && max != null && value >= max}
|
|
189
|
+
onclick={plus}><Icon name="plus" /></button
|
|
190
|
+
>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
<div class="number-grid">
|
|
194
|
+
{#each DIGITS as digit (digit)}
|
|
195
|
+
<button
|
|
196
|
+
onkeydown={passthrough}
|
|
197
|
+
type="button"
|
|
198
|
+
data-digit={digit}
|
|
199
|
+
disabled={digitDisabled[digit]}
|
|
200
|
+
onclick={onDigitClick}>{digit}</button
|
|
201
|
+
>
|
|
202
|
+
{/each}
|
|
203
|
+
<button
|
|
204
|
+
onkeydown={passthrough}
|
|
205
|
+
type="button"
|
|
206
|
+
disabled={!decimal || (value != null && value % 1 !== 0) || _value.endsWith('.')}
|
|
207
|
+
onclick={addDecimal}>.</button
|
|
208
|
+
>
|
|
209
|
+
<button
|
|
210
|
+
onkeydown={passthrough}
|
|
211
|
+
type="button"
|
|
212
|
+
data-digit="0"
|
|
213
|
+
disabled={digitDisabled[0]}
|
|
214
|
+
onclick={onDigitClick}>0</button
|
|
215
|
+
>
|
|
216
|
+
<button
|
|
217
|
+
onkeydown={passthrough}
|
|
218
|
+
type="button"
|
|
219
|
+
disabled={_value.length === 0}
|
|
220
|
+
onclick={backspace}>⌫</button
|
|
221
|
+
>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
225
|
<style>button {
|
|
226
226
|
color: #3a4952;
|
|
227
227
|
fill: #3a4952;
|
|
@@ -309,4 +309,4 @@ button {
|
|
|
309
309
|
}
|
|
310
310
|
.number-input-wrapper .number-grid {
|
|
311
311
|
flex-grow: 2;
|
|
312
|
-
}</style>
|
|
312
|
+
}</style>
|