@placeholderco/placeholder-ui 1.0.12 → 2.0.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/LICENSE +1 -1
- package/README.md +134 -130
- package/dist/app.css +178 -0
- package/dist/bootstrap.min.css +3600 -0
- package/dist/cards/GitCard.svelte +115 -0
- package/dist/cards/GitCard.svelte.d.ts +22 -0
- package/dist/display/Avatar.svelte +29 -17
- package/dist/display/Avatar.svelte.d.ts +12 -2
- package/dist/display/LinkCollection.svelte +40 -18
- package/dist/display/LinkCollection.svelte.d.ts +9 -2
- package/dist/display/Paper.svelte +31 -19
- package/dist/display/Paper.svelte.d.ts +12 -3
- package/dist/display/alert/Alert.svelte +129 -0
- package/dist/display/alert/Alert.svelte.d.ts +15 -0
- package/dist/display/alert/AlertVariant.d.ts +1 -0
- package/dist/display/alert/AlertVariant.js +1 -0
- package/dist/display/filetree/FileTree.svelte +259 -0
- package/dist/display/filetree/FileTree.svelte.d.ts +76 -0
- package/dist/display/filetree/FileTreeItem.svelte +396 -0
- package/dist/display/filetree/FileTreeItem.svelte.d.ts +40 -0
- package/dist/display/filetree/FileTreeNode.d.ts +46 -0
- package/dist/display/filetree/FileTreeNode.js +115 -0
- package/dist/display/filetree/index.d.ts +5 -0
- package/dist/display/filetree/index.js +3 -0
- package/dist/editors/CodeMirror.svelte +306 -0
- package/dist/editors/CodeMirror.svelte.d.ts +31 -0
- package/dist/editors/tiptap/LinkActionMenu.svelte +307 -0
- package/dist/editors/tiptap/LinkActionMenu.svelte.d.ts +8 -0
- package/dist/editors/tiptap/TipTap.svelte +856 -0
- package/dist/editors/tiptap/TipTap.svelte.d.ts +54 -0
- package/dist/editors/tiptap/TipTapBubble.svelte +790 -0
- package/dist/editors/tiptap/TipTapBubble.svelte.d.ts +44 -0
- package/dist/editors/tiptap/collaboration.d.ts +55 -0
- package/dist/editors/tiptap/collaboration.js +1 -0
- package/dist/editors/tiptap/extensions/Alert.d.ts +18 -0
- package/dist/editors/tiptap/extensions/Alert.js +72 -0
- package/dist/editors/tiptap/extensions/CodeBlockEditor.d.ts +21 -0
- package/dist/editors/tiptap/extensions/CodeBlockEditor.js +326 -0
- package/dist/editors/tiptap/extensions/Div.d.ts +19 -0
- package/dist/editors/tiptap/extensions/Div.js +89 -0
- package/dist/editors/tiptap/extensions/Span.d.ts +5 -0
- package/dist/editors/tiptap/extensions/Span.js +56 -0
- package/dist/editors/tiptap/extensions/codeBlockEditor.css +236 -0
- package/dist/editors/tiptap/extensions/codeBlockLanguages.d.ts +17 -0
- package/dist/editors/tiptap/extensions/codeBlockLanguages.js +124 -0
- package/dist/editors/tiptap/extensions/index.d.ts +7 -0
- package/dist/editors/tiptap/extensions/index.js +5 -0
- package/dist/editors/tiptap/toolbar/AlertButton.svelte +261 -0
- package/dist/editors/tiptap/toolbar/AlertButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignRightButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/BoldButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/BoldButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/BulletListButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/BulletListButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/CodeButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/CodeButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/ColorButton.svelte +299 -0
- package/dist/editors/tiptap/toolbar/ColorButton.svelte.d.ts +20 -0
- package/dist/editors/tiptap/toolbar/DocumentButton.svelte +58 -0
- package/dist/editors/tiptap/toolbar/DocumentButton.svelte.d.ts +9 -0
- package/dist/editors/tiptap/toolbar/HeadingButton.svelte +30 -0
- package/dist/editors/tiptap/toolbar/HeadingButton.svelte.d.ts +8 -0
- package/dist/editors/tiptap/toolbar/HtmlButton.svelte +68 -0
- package/dist/editors/tiptap/toolbar/HtmlButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/ImageButton.svelte +54 -0
- package/dist/editors/tiptap/toolbar/ImageButton.svelte.d.ts +8 -0
- package/dist/editors/tiptap/toolbar/ItalicButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/ItalicButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/LinkButton.svelte +257 -0
- package/dist/editors/tiptap/toolbar/LinkButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/OrderedListButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/RedoButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/RedoButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/SubscriptButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/TableButton.svelte +258 -0
- package/dist/editors/tiptap/toolbar/TableButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +84 -0
- package/dist/editors/tiptap/toolbar/ToolbarButton.svelte.d.ts +10 -0
- package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/UnderlineButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/UndoButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/UndoButton.svelte.d.ts +7 -0
- package/dist/form/Autocomplete.svelte +86 -135
- package/dist/form/Autocomplete.svelte.d.ts +6 -8
- package/dist/form/AutocompleteMulti.svelte +385 -227
- package/dist/form/AutocompleteMulti.svelte.d.ts +59 -10
- package/dist/form/Checkbox.svelte +59 -54
- package/dist/form/Checkbox.svelte.d.ts +14 -5
- package/dist/form/Chips.svelte +29 -13
- package/dist/form/Chips.svelte.d.ts +16 -2
- package/dist/form/ComboBox.svelte +30 -20
- package/dist/form/ComboBox.svelte.d.ts +1 -1
- package/dist/form/ComboBoxItemBuilder.svelte +161 -188
- package/dist/form/ComboBoxItemBuilder.svelte.d.ts +19 -2
- package/dist/form/ComboBoxMulti.svelte +38 -24
- package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
- package/dist/form/CronBuilder.svelte +84 -67
- package/dist/form/CronBuilder.svelte.d.ts +16 -3
- package/dist/form/DatePicker.svelte +148 -195
- package/dist/form/DateRangePicker.svelte +101 -98
- package/dist/form/DateTimePicker.svelte +167 -208
- package/dist/form/DateTimePicker.svelte.d.ts +10 -2
- package/dist/form/FormGroup.svelte +33 -10
- package/dist/form/FormGroup.svelte.d.ts +12 -2
- package/dist/form/Number.svelte +70 -47
- package/dist/form/Number.svelte.d.ts +36 -6
- package/dist/form/Radio.svelte +18 -18
- package/dist/form/RadioGroup.svelte +23 -12
- package/dist/form/RadioGroup.svelte.d.ts +13 -2
- package/dist/form/SegmentedControl.svelte +76 -56
- package/dist/form/SegmentedControl.svelte.d.ts +9 -4
- package/dist/form/Select.svelte +150 -233
- package/dist/form/Select.svelte.d.ts +29 -5
- package/dist/form/SelectMulti.svelte +163 -277
- package/dist/form/SelectMulti.svelte.d.ts +36 -7
- package/dist/form/Slider.svelte +183 -269
- package/dist/form/Slider.svelte.d.ts +32 -17
- package/dist/form/StringArrayBuilder.svelte +249 -0
- package/dist/form/StringArrayBuilder.svelte.d.ts +15 -0
- package/dist/form/Switch.svelte +164 -106
- package/dist/form/Switch.svelte.d.ts +22 -3
- package/dist/form/TextArea.svelte +49 -29
- package/dist/form/TextArea.svelte.d.ts +23 -3
- package/dist/form/Textbox.svelte +69 -68
- package/dist/form/Textbox.svelte.d.ts +32 -4
- package/dist/form/TimePicker.svelte +42 -47
- package/dist/form/TimePicker.svelte.d.ts +12 -2
- package/dist/form/comboBoxCore.svelte.d.ts +99 -0
- package/dist/form/comboBoxCore.svelte.js +243 -0
- package/dist/icon/Icon.svelte +8 -3
- package/dist/icon/Icon.svelte.d.ts +8 -3
- package/dist/icon/file-type-bmp.svg +1 -0
- package/dist/icon/file-type-csv.svg +1 -0
- package/dist/icon/file-type-doc.svg +1 -0
- package/dist/icon/file-type-docx.svg +1 -0
- package/dist/icon/file-type-jpg.svg +1 -0
- package/dist/icon/file-type-pdf.svg +1 -0
- package/dist/icon/file-type-png.svg +1 -0
- package/dist/icon/file-type-ppt.svg +1 -0
- package/dist/icon/file-type-svg.svg +1 -0
- package/dist/icon/file-type-txt.svg +1 -0
- package/dist/icon/file-type-xls.svg +1 -0
- package/dist/icon/file-type-zip.svg +1 -0
- package/dist/icon/index.d.ts +166 -151
- package/dist/icon/index.js +166 -152
- package/dist/icon/logo-placeholder.svg +6 -0
- package/dist/icon/source-code.svg +1 -0
- package/dist/icon/sun-moon.svg +1 -0
- package/dist/icon/table.svg +6 -0
- package/dist/index.d.ts +87 -26
- package/dist/index.js +25 -28
- package/dist/layout/AppShell.svelte +12 -162
- package/dist/layout/AppShell.svelte.d.ts +6 -27
- package/dist/layout/CustomNavbar.svelte +9 -7
- package/dist/layout/CustomNavbar.svelte.d.ts +4 -2
- package/dist/layout/Navbar.svelte +161 -129
- package/dist/layout/Navbar.svelte.d.ts +29 -15
- package/dist/layout/NavbarItemDisplay.svelte +2 -2
- package/dist/layout/NavbarItemView.svelte +182 -0
- package/dist/layout/NavbarItemView.svelte.d.ts +10 -0
- package/dist/layout/Sidenav.svelte +280 -341
- package/dist/layout/Sidenav.svelte.d.ts +36 -4
- package/dist/models/ComboBoxItem.d.ts +2 -0
- package/dist/models/Employee.d.ts +53 -0
- package/dist/models/Employee.js +23 -0
- package/dist/models/Hyperlink.d.ts +3 -1
- package/dist/models/LinkCollectionItem.d.ts +3 -1
- package/dist/models/NavbarItem.d.ts +6 -2
- package/dist/theme.svelte.d.ts +0 -1
- package/dist/theme.svelte.js +46 -58
- package/dist/ui/Accordion.svelte +81 -242
- package/dist/ui/Accordion.svelte.d.ts +37 -14
- package/dist/ui/AccordionItem.svelte +303 -0
- package/dist/ui/AccordionItem.svelte.d.ts +21 -0
- package/dist/ui/ActionIcon.svelte +25 -21
- package/dist/ui/ActionIcon.svelte.d.ts +15 -2
- package/dist/ui/Badge.svelte +163 -171
- package/dist/ui/Badge.svelte.d.ts +13 -5
- package/dist/ui/Button.svelte +178 -205
- package/dist/ui/Button.svelte.d.ts +19 -2
- package/dist/ui/ButtonVariant.d.ts +15 -1
- package/dist/ui/ContextMenu.svelte +146 -0
- package/dist/ui/ContextMenu.svelte.d.ts +37 -0
- package/dist/ui/ContextMenuList.svelte +188 -0
- package/dist/ui/ContextMenuList.svelte.d.ts +12 -0
- package/dist/ui/Dialog.svelte +183 -111
- package/dist/ui/Dialog.svelte.d.ts +21 -4
- package/dist/ui/Drawer.svelte +237 -184
- package/dist/ui/Drawer.svelte.d.ts +20 -2
- package/dist/ui/Dropdown.svelte +24 -14
- package/dist/ui/Dropdown.svelte.d.ts +11 -2
- package/dist/ui/Dropzone.svelte +159 -57
- package/dist/ui/Dropzone.svelte.d.ts +29 -2
- package/dist/ui/Link.svelte +12 -4
- package/dist/ui/Link.svelte.d.ts +8 -2
- package/dist/ui/Loader.svelte +23 -18
- package/dist/ui/Loader.svelte.d.ts +5 -2
- package/dist/ui/Logo.svelte +37 -0
- package/dist/ui/Logo.svelte.d.ts +16 -0
- package/dist/ui/MultiSortable.svelte +287 -0
- package/dist/ui/MultiSortable.svelte.d.ts +50 -0
- package/dist/ui/Pagination.svelte +15 -9
- package/dist/ui/Pagination.svelte.d.ts +6 -2
- package/dist/ui/Popover.svelte +43 -65
- package/dist/ui/Popover.svelte.d.ts +14 -5
- package/dist/ui/ProgressBar.svelte +28 -30
- package/dist/ui/ProgressBarVariant.d.ts +7 -1
- package/dist/ui/SegmentedControl.svelte +0 -0
- package/dist/ui/SegmentedControl.svelte.d.ts +26 -0
- package/dist/ui/Sortable.svelte +163 -0
- package/dist/ui/Sortable.svelte.d.ts +47 -0
- package/dist/ui/Table.svelte +383 -80
- package/dist/ui/Table.svelte.d.ts +43 -2
- package/dist/ui/Tabs.svelte +22 -18
- package/dist/ui/Tabs.svelte.d.ts +6 -2
- package/dist/ui/ThemeSwitcher.svelte +26 -7
- package/dist/ui/ThemeSwitcher.svelte.d.ts +4 -2
- package/dist/ui/Tooltip.svelte +312 -180
- package/dist/ui/Tooltip.svelte.d.ts +4 -3
- package/dist/uiTheme.svelte.d.ts +86 -0
- package/dist/uiTheme.svelte.js +214 -0
- package/dist/util/DateFunctions.js +1 -1
- package/dist/util/DragDrop.d.ts +50 -0
- package/dist/util/DragDrop.js +154 -0
- package/dist/util/NavigateTo.js +2 -2
- package/dist/util/dayjs.d.ts +4 -0
- package/dist/util/dayjs.js +15 -0
- package/package.json +66 -26
- package/dist/display/Alert.svelte +0 -179
- package/dist/display/Alert.svelte.d.ts +0 -13
- package/dist/form/FileInput.svelte +0 -235
- package/dist/form/FileInput.svelte.d.ts +0 -23
- package/dist/form/PasswordInput.svelte +0 -253
- package/dist/form/PasswordInput.svelte.d.ts +0 -34
- package/dist/form/Rating.svelte +0 -235
- package/dist/form/Rating.svelte.d.ts +0 -21
- package/dist/styles/components.css +0 -199
- package/dist/styles/dark.css +0 -146
- package/dist/styles/index.css +0 -116
- package/dist/styles/reset.css +0 -110
- package/dist/styles/semantic.css +0 -86
- package/dist/styles/tokens.css +0 -203
- package/dist/styles/utilities.css +0 -523
- package/dist/ui/Breadcrumbs.svelte +0 -131
- package/dist/ui/Breadcrumbs.svelte.d.ts +0 -16
- package/dist/ui/LoadingOverlay.svelte +0 -53
- package/dist/ui/LoadingOverlay.svelte.d.ts +0 -11
- package/dist/ui/Progress.svelte +0 -191
- package/dist/ui/Progress.svelte.d.ts +0 -15
- package/dist/ui/RingProgress.svelte +0 -141
- package/dist/ui/RingProgress.svelte.d.ts +0 -18
- package/dist/ui/Skeleton.svelte +0 -85
- package/dist/ui/Skeleton.svelte.d.ts +0 -12
- package/dist/ui/Stepper.svelte +0 -355
- package/dist/ui/Stepper.svelte.d.ts +0 -24
- package/dist/ui/Timeline.svelte +0 -225
- package/dist/ui/Timeline.svelte.d.ts +0 -23
- /package/dist/{actions → util}/ClickOutside.d.ts +0 -0
- /package/dist/{actions → util}/ClickOutside.js +0 -0
- /package/dist/{transitions → util}/Transitions.d.ts +0 -0
- /package/dist/{transitions → util}/Transitions.js +0 -0
|
@@ -1,106 +1,110 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script module lang="ts">
|
|
2
2
|
import type { ComboBoxItem } from '../models/ComboBoxItem.js';
|
|
3
|
-
import ActionIcon from '../ui/ActionIcon.svelte';
|
|
4
|
-
import Button from '../ui/Button.svelte';
|
|
5
|
-
import Checkbox from './Checkbox.svelte';
|
|
6
|
-
import { iconPlus, iconTrash } from '../icon/index.js';
|
|
7
|
-
import FormGroup from './FormGroup.svelte';
|
|
8
3
|
|
|
9
|
-
interface
|
|
4
|
+
export interface ComboBoxItemBuilderProps {
|
|
5
|
+
/** Label text displayed above the builder */
|
|
10
6
|
label?: string;
|
|
7
|
+
/** Array of combo box items (bindable) */
|
|
11
8
|
items?: ComboBoxItem[];
|
|
9
|
+
/** Array of default selected values (bindable) */
|
|
12
10
|
defaultValues?: string[];
|
|
11
|
+
/** Header text for the label column */
|
|
12
|
+
labelHeader?: string;
|
|
13
|
+
/** Header text for the value column */
|
|
14
|
+
valueHeader?: string;
|
|
15
|
+
/** Placeholder for label input field (defaults to labelHeader) */
|
|
13
16
|
labelPlaceholder?: string;
|
|
17
|
+
/** Placeholder for value input field (defaults to valueHeader) */
|
|
14
18
|
valuePlaceholder?: string;
|
|
19
|
+
/** Hide the "Selected" column and skip default-value tracking */
|
|
20
|
+
hideSelected?: boolean;
|
|
21
|
+
/** Disable auto-copying the label into the value field */
|
|
22
|
+
disableValueSync?: boolean;
|
|
23
|
+
/** Allow multiple items to share the same value. Note: default-value tracking is keyed by value, so items sharing a value also share their selected state */
|
|
24
|
+
allowDuplicateValues?: boolean;
|
|
25
|
+
/** Additional CSS classes */
|
|
15
26
|
class?: string;
|
|
27
|
+
/** Callback when items or default values change */
|
|
16
28
|
onchange?: (items: ComboBoxItem[], defaultValues: string[]) => void;
|
|
17
29
|
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<script lang="ts">
|
|
33
|
+
import { tick } from 'svelte';
|
|
34
|
+
import ActionIcon from '../ui/ActionIcon.svelte';
|
|
35
|
+
import Checkbox from './Checkbox.svelte';
|
|
36
|
+
import { iconTrash } from '../icon/index.js';
|
|
37
|
+
import FormGroup from './FormGroup.svelte';
|
|
18
38
|
|
|
19
39
|
let {
|
|
20
40
|
label = '',
|
|
21
41
|
items = $bindable([]),
|
|
22
42
|
defaultValues = $bindable([]),
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
labelHeader = 'Label',
|
|
44
|
+
valueHeader = 'Value',
|
|
45
|
+
labelPlaceholder = undefined,
|
|
46
|
+
valuePlaceholder = undefined,
|
|
47
|
+
hideSelected = false,
|
|
48
|
+
disableValueSync = false,
|
|
49
|
+
allowDuplicateValues = false,
|
|
25
50
|
class: classes = '',
|
|
26
51
|
onchange
|
|
27
|
-
}:
|
|
52
|
+
}: ComboBoxItemBuilderProps = $props();
|
|
28
53
|
|
|
29
|
-
let
|
|
30
|
-
let
|
|
31
|
-
let valueManuallyEdited = $state(false);
|
|
32
|
-
let labelInputEl: HTMLInputElement | undefined = $state(undefined);
|
|
54
|
+
let effectiveLabelPlaceholder = $derived(labelPlaceholder ?? labelHeader);
|
|
55
|
+
let effectiveValuePlaceholder = $derived(valuePlaceholder ?? valueHeader);
|
|
33
56
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
let labelInputEl: HTMLInputElement | undefined = $state(undefined);
|
|
58
|
+
let itemLabelInputs: HTMLInputElement[] = $state([]);
|
|
59
|
+
let itemValueInputs: HTMLInputElement[] = $state([]);
|
|
60
|
+
|
|
61
|
+
// Typing in the empty bottom row immediately creates a real item and moves focus into it
|
|
62
|
+
async function handleNewInput(
|
|
63
|
+
field: 'label' | 'value',
|
|
64
|
+
e: Event & { currentTarget: EventTarget & HTMLInputElement }
|
|
65
|
+
) {
|
|
66
|
+
const text = e.currentTarget.value;
|
|
67
|
+
e.currentTarget.value = '';
|
|
68
|
+
if (!text) return;
|
|
69
|
+
|
|
70
|
+
const newItem: ComboBoxItem =
|
|
71
|
+
field === 'label'
|
|
72
|
+
? { label: text, value: disableValueSync ? '' : text }
|
|
73
|
+
: { label: '', value: text };
|
|
38
74
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
newLabel = value;
|
|
42
|
-
if (!valueManuallyEdited) {
|
|
43
|
-
newValue = labelToValue(value);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
75
|
+
items = [...items, newItem];
|
|
76
|
+
notifyChange();
|
|
46
77
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
78
|
+
await tick();
|
|
79
|
+
const input =
|
|
80
|
+
field === 'label' ? itemLabelInputs[items.length - 1] : itemValueInputs[items.length - 1];
|
|
81
|
+
input?.focus();
|
|
82
|
+
input?.setSelectionRange(text.length, text.length);
|
|
53
83
|
}
|
|
54
84
|
|
|
55
85
|
// Check if a value already exists (excluding a specific index for editing)
|
|
56
|
-
function valueExists(value: string, excludeIndex
|
|
86
|
+
function valueExists(value: string, excludeIndex: number | undefined = undefined): boolean {
|
|
57
87
|
return items.some((item, i) =>
|
|
58
88
|
item.value === value && i !== excludeIndex
|
|
59
89
|
);
|
|
60
90
|
}
|
|
61
91
|
|
|
62
92
|
// Check if a label already exists (excluding a specific index for editing)
|
|
63
|
-
function labelExists(label: string, excludeIndex
|
|
93
|
+
function labelExists(label: string, excludeIndex: number | undefined = undefined): boolean {
|
|
64
94
|
return items.some((item, i) =>
|
|
65
95
|
item.label === label && i !== excludeIndex
|
|
66
96
|
);
|
|
67
97
|
}
|
|
68
98
|
|
|
69
|
-
// Check if new value/label would be a duplicate
|
|
70
|
-
let isDuplicateNewValue = $derived(
|
|
71
|
-
newValue.trim() !== '' && valueExists(newValue.trim())
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
let isDuplicateNewLabel = $derived(
|
|
75
|
-
newLabel.trim() !== '' && labelExists(newLabel.trim())
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
function addItem() {
|
|
79
|
-
if (!newLabel.trim() || !newValue.trim()) return;
|
|
80
|
-
if (isDuplicateNewValue || isDuplicateNewLabel) return;
|
|
81
|
-
|
|
82
|
-
const newItem: ComboBoxItem = {
|
|
83
|
-
label: newLabel.trim(),
|
|
84
|
-
value: newValue.trim()
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
items = [...items, newItem];
|
|
88
|
-
newLabel = '';
|
|
89
|
-
newValue = '';
|
|
90
|
-
valueManuallyEdited = false;
|
|
91
|
-
|
|
92
|
-
notifyChange();
|
|
93
|
-
|
|
94
|
-
// Focus back on label input for quick entry of next item
|
|
95
|
-
labelInputEl?.focus();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
99
|
function removeItem(index: number) {
|
|
99
100
|
const removedItem = items[index];
|
|
100
101
|
items = items.filter((_, i) => i !== index);
|
|
101
102
|
|
|
102
|
-
// Remove from defaults if it was selected
|
|
103
|
-
if (
|
|
103
|
+
// Remove from defaults if it was selected and no remaining item shares the value
|
|
104
|
+
if (
|
|
105
|
+
defaultValues.includes(removedItem.value) &&
|
|
106
|
+
!items.some(item => item.value === removedItem.value)
|
|
107
|
+
) {
|
|
104
108
|
defaultValues = defaultValues.filter(v => v !== removedItem.value);
|
|
105
109
|
}
|
|
106
110
|
|
|
@@ -123,17 +127,20 @@
|
|
|
123
127
|
const currentItem = items[index];
|
|
124
128
|
|
|
125
129
|
// Check if label and value were in sync (exact case-sensitive match)
|
|
126
|
-
const wasInSync = currentItem.label === currentItem.value;
|
|
130
|
+
const wasInSync = !disableValueSync && currentItem.label === currentItem.value;
|
|
127
131
|
|
|
128
132
|
// If they were in sync, also update the value to maintain exact match (if it wouldn't create a duplicate)
|
|
129
|
-
if (wasInSync && !valueExists(label, index)) {
|
|
133
|
+
if (wasInSync && (allowDuplicateValues || !valueExists(label, index))) {
|
|
130
134
|
const oldValue = currentItem.value;
|
|
131
135
|
items = items.map((item, i) =>
|
|
132
136
|
i === index ? { ...item, label, value: label } : item
|
|
133
137
|
);
|
|
134
138
|
|
|
135
|
-
// Update defaults if the value was changed
|
|
136
|
-
if (
|
|
139
|
+
// Update defaults if the value was changed and no remaining item shares the old value
|
|
140
|
+
if (
|
|
141
|
+
defaultValues.includes(oldValue) &&
|
|
142
|
+
!items.some(item => item.value === oldValue)
|
|
143
|
+
) {
|
|
137
144
|
defaultValues = defaultValues.map(v => v === oldValue ? label : v);
|
|
138
145
|
}
|
|
139
146
|
} else {
|
|
@@ -147,15 +154,18 @@
|
|
|
147
154
|
|
|
148
155
|
function updateItemValue(index: number, newVal: string) {
|
|
149
156
|
// Don't allow duplicate values
|
|
150
|
-
if (valueExists(newVal, index)) return;
|
|
157
|
+
if (!allowDuplicateValues && valueExists(newVal, index)) return;
|
|
151
158
|
|
|
152
159
|
const oldValue = items[index].value;
|
|
153
160
|
items = items.map((item, i) =>
|
|
154
161
|
i === index ? { ...item, value: newVal } : item
|
|
155
162
|
);
|
|
156
163
|
|
|
157
|
-
// Update defaults if the value was changed
|
|
158
|
-
if (
|
|
164
|
+
// Update defaults if the value was changed and no remaining item shares the old value
|
|
165
|
+
if (
|
|
166
|
+
defaultValues.includes(oldValue) &&
|
|
167
|
+
!items.some(item => item.value === oldValue)
|
|
168
|
+
) {
|
|
159
169
|
defaultValues = defaultValues.map(v => v === oldValue ? newVal : v);
|
|
160
170
|
}
|
|
161
171
|
|
|
@@ -166,10 +176,11 @@
|
|
|
166
176
|
onchange?.(items, defaultValues);
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
|
|
179
|
+
// Enter in an item row jumps back to the empty row for quick entry of the next item
|
|
180
|
+
function handleItemKeyDown(e: KeyboardEvent) {
|
|
170
181
|
if (e.key === 'Enter') {
|
|
171
182
|
e.preventDefault();
|
|
172
|
-
|
|
183
|
+
labelInputEl?.focus();
|
|
173
184
|
}
|
|
174
185
|
}
|
|
175
186
|
|
|
@@ -196,40 +207,48 @@
|
|
|
196
207
|
</FormGroup>
|
|
197
208
|
{/if}
|
|
198
209
|
|
|
199
|
-
<div class="builder-content">
|
|
210
|
+
<div class="builder-content" class:no-selected={hideSelected}>
|
|
200
211
|
<!-- Header row -->
|
|
201
212
|
<div class="header-row">
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
213
|
+
{#if !hideSelected}
|
|
214
|
+
<div class="col-default">Selected</div>
|
|
215
|
+
{/if}
|
|
216
|
+
<div class="col-label">{labelHeader}</div>
|
|
217
|
+
<div class="col-value">{valueHeader}</div>
|
|
205
218
|
<div class="col-actions"></div>
|
|
206
219
|
</div>
|
|
207
220
|
|
|
208
221
|
<!-- Existing items -->
|
|
209
222
|
{#each items as item, index (index)}
|
|
210
223
|
<div class="item-row">
|
|
211
|
-
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
224
|
+
{#if !hideSelected}
|
|
225
|
+
<div class="col-default">
|
|
226
|
+
<Checkbox
|
|
227
|
+
checked={defaultValues.includes(item.value)}
|
|
228
|
+
onchange={(checked) => toggleDefault(item.value, checked)}
|
|
229
|
+
/>
|
|
230
|
+
</div>
|
|
231
|
+
{/if}
|
|
217
232
|
<div class="col-label">
|
|
218
233
|
<input
|
|
234
|
+
bind:this={itemLabelInputs[index]}
|
|
219
235
|
type="text"
|
|
220
236
|
class="item-input"
|
|
221
237
|
value={item.label}
|
|
222
238
|
oninput={(e) => updateItemLabel(index, e.currentTarget.value)}
|
|
223
|
-
|
|
239
|
+
onkeydown={handleItemKeyDown}
|
|
240
|
+
placeholder={effectiveLabelPlaceholder}
|
|
224
241
|
/>
|
|
225
242
|
</div>
|
|
226
243
|
<div class="col-value">
|
|
227
244
|
<input
|
|
245
|
+
bind:this={itemValueInputs[index]}
|
|
228
246
|
type="text"
|
|
229
247
|
class="item-input"
|
|
230
248
|
value={item.value}
|
|
231
249
|
oninput={(e) => updateItemValue(index, e.currentTarget.value)}
|
|
232
|
-
|
|
250
|
+
onkeydown={handleItemKeyDown}
|
|
251
|
+
placeholder={effectiveValuePlaceholder}
|
|
233
252
|
/>
|
|
234
253
|
</div>
|
|
235
254
|
<div class="col-actions">
|
|
@@ -246,69 +265,35 @@
|
|
|
246
265
|
|
|
247
266
|
<!-- Add new item row -->
|
|
248
267
|
<div class="add-row">
|
|
249
|
-
|
|
268
|
+
{#if !hideSelected}
|
|
269
|
+
<div class="col-default"></div>
|
|
270
|
+
{/if}
|
|
250
271
|
<div class="col-label">
|
|
251
272
|
<input
|
|
252
273
|
bind:this={labelInputEl}
|
|
253
274
|
type="text"
|
|
254
275
|
class="item-input new-input"
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
oninput={(e) => handleLabelInput(e.currentTarget.value)}
|
|
258
|
-
placeholder={labelPlaceholder}
|
|
259
|
-
onkeydown={handleKeyDown}
|
|
276
|
+
oninput={(e) => handleNewInput('label', e)}
|
|
277
|
+
placeholder={effectiveLabelPlaceholder}
|
|
260
278
|
/>
|
|
261
279
|
</div>
|
|
262
280
|
<div class="col-value">
|
|
263
281
|
<input
|
|
264
282
|
type="text"
|
|
265
283
|
class="item-input new-input"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
oninput={(e) => handleValueInput(e.currentTarget.value)}
|
|
269
|
-
placeholder={valuePlaceholder}
|
|
270
|
-
onkeydown={handleKeyDown}
|
|
271
|
-
/>
|
|
272
|
-
</div>
|
|
273
|
-
<div class="col-actions">
|
|
274
|
-
<ActionIcon
|
|
275
|
-
variant="auto-subtle"
|
|
276
|
-
svg={iconPlus}
|
|
277
|
-
size="1rem"
|
|
278
|
-
onclick={addItem}
|
|
279
|
-
disabled={!newLabel.trim() || !newValue.trim() || isDuplicateNewValue || isDuplicateNewLabel}
|
|
280
|
-
tooltip="Add item"
|
|
284
|
+
oninput={(e) => handleNewInput('value', e)}
|
|
285
|
+
placeholder={effectiveValuePlaceholder}
|
|
281
286
|
/>
|
|
282
287
|
</div>
|
|
288
|
+
<div class="col-actions"></div>
|
|
283
289
|
</div>
|
|
284
|
-
|
|
285
|
-
{#if isDuplicateNewLabel || isDuplicateNewValue}
|
|
286
|
-
<div class="error-message">
|
|
287
|
-
{#if isDuplicateNewLabel && isDuplicateNewValue}
|
|
288
|
-
Label "{newLabel}" and value "{newValue}" already exist
|
|
289
|
-
{:else if isDuplicateNewLabel}
|
|
290
|
-
Label "{newLabel}" already exists
|
|
291
|
-
{:else}
|
|
292
|
-
Value "{newValue}" already exists
|
|
293
|
-
{/if}
|
|
294
|
-
</div>
|
|
295
|
-
{/if}
|
|
296
|
-
|
|
297
|
-
<!-- Quick add button for mobile/convenience -->
|
|
298
|
-
{#if newLabel.trim() && newValue.trim() && !isDuplicateNewValue && !isDuplicateNewLabel}
|
|
299
|
-
<div class="quick-add">
|
|
300
|
-
<Button variant="auto-subtle" onclick={addItem}>
|
|
301
|
-
Add "{newLabel}"
|
|
302
|
-
</Button>
|
|
303
|
-
</div>
|
|
304
|
-
{/if}
|
|
305
290
|
</div>
|
|
306
291
|
|
|
307
292
|
<!-- Summary -->
|
|
308
293
|
{#if items.length > 0}
|
|
309
294
|
<div class="summary">
|
|
310
295
|
<span class="summary-count">{items.length} item{items.length !== 1 ? 's' : ''}</span>
|
|
311
|
-
{#if defaultValues.length > 0}
|
|
296
|
+
{#if !hideSelected && defaultValues.length > 0}
|
|
312
297
|
<span class="summary-default">
|
|
313
298
|
Selected: {defaultLabels}
|
|
314
299
|
</span>
|
|
@@ -323,21 +308,27 @@
|
|
|
323
308
|
}
|
|
324
309
|
|
|
325
310
|
.builder-content {
|
|
326
|
-
border: 1px solid var(--
|
|
327
|
-
border-radius:
|
|
311
|
+
border: 1px solid var(--border-color);
|
|
312
|
+
border-radius: 0.375rem;
|
|
328
313
|
overflow: hidden;
|
|
329
314
|
}
|
|
330
315
|
|
|
316
|
+
.no-selected .header-row,
|
|
317
|
+
.no-selected .item-row,
|
|
318
|
+
.no-selected .add-row {
|
|
319
|
+
grid-template-columns: 1fr 1fr 2.5rem;
|
|
320
|
+
}
|
|
321
|
+
|
|
331
322
|
.header-row {
|
|
332
323
|
display: grid;
|
|
333
324
|
grid-template-columns: 3.5rem 1fr 1fr 2.5rem;
|
|
334
|
-
gap:
|
|
335
|
-
padding:
|
|
336
|
-
background-color: var(--
|
|
337
|
-
border-bottom: 1px solid var(--
|
|
338
|
-
font-size:
|
|
339
|
-
font-weight:
|
|
340
|
-
color: var(--
|
|
325
|
+
gap: 0.5rem;
|
|
326
|
+
padding: 0.5rem 0.75rem;
|
|
327
|
+
background-color: var(--input-bg-color);
|
|
328
|
+
border-bottom: 1px solid var(--border-color);
|
|
329
|
+
font-size: 0.75rem;
|
|
330
|
+
font-weight: 600;
|
|
331
|
+
color: var(--text-muted);
|
|
341
332
|
text-transform: uppercase;
|
|
342
333
|
letter-spacing: 0.025em;
|
|
343
334
|
}
|
|
@@ -346,18 +337,18 @@
|
|
|
346
337
|
.add-row {
|
|
347
338
|
display: grid;
|
|
348
339
|
grid-template-columns: 3.5rem 1fr 1fr 2.5rem;
|
|
349
|
-
gap:
|
|
350
|
-
padding:
|
|
340
|
+
gap: 0.5rem;
|
|
341
|
+
padding: 0.5rem 0.75rem;
|
|
351
342
|
align-items: center;
|
|
352
|
-
border-bottom: 1px solid var(--
|
|
343
|
+
border-bottom: 1px solid var(--border-color);
|
|
353
344
|
}
|
|
354
345
|
|
|
355
346
|
.item-row:last-of-type {
|
|
356
|
-
border-bottom: 1px solid var(--
|
|
347
|
+
border-bottom: 1px solid var(--border-color);
|
|
357
348
|
}
|
|
358
349
|
|
|
359
350
|
.add-row {
|
|
360
|
-
background-color: var(--
|
|
351
|
+
background-color: rgba(var(--ui-primary-rgbc), 0.02);
|
|
361
352
|
border-bottom: none;
|
|
362
353
|
}
|
|
363
354
|
|
|
@@ -375,71 +366,47 @@
|
|
|
375
366
|
|
|
376
367
|
.item-input {
|
|
377
368
|
width: 100%;
|
|
378
|
-
padding:
|
|
379
|
-
border: 1px solid var(--
|
|
380
|
-
border-radius:
|
|
381
|
-
background-color: var(--
|
|
382
|
-
color: var(--
|
|
383
|
-
font-size:
|
|
384
|
-
transition: border-color
|
|
369
|
+
padding: 0.375rem 0.5rem;
|
|
370
|
+
border: 1px solid var(--border-color);
|
|
371
|
+
border-radius: 0.25rem;
|
|
372
|
+
background-color: var(--input-bg-color);
|
|
373
|
+
color: var(--text-color);
|
|
374
|
+
font-size: 0.875rem;
|
|
375
|
+
transition: border-color 0.15s;
|
|
385
376
|
}
|
|
386
377
|
|
|
387
378
|
.item-input:focus {
|
|
388
379
|
outline: none;
|
|
389
|
-
border-color: var(--
|
|
380
|
+
border-color: var(--accent-color);
|
|
390
381
|
}
|
|
391
382
|
|
|
392
383
|
.item-input::placeholder {
|
|
393
|
-
color: var(--
|
|
384
|
+
color: var(--placeholder-color);
|
|
394
385
|
}
|
|
395
386
|
|
|
396
387
|
.new-input {
|
|
397
388
|
background-color: transparent;
|
|
398
389
|
}
|
|
399
390
|
|
|
400
|
-
.item-input.error {
|
|
401
|
-
border-color: var(--pui-text-danger);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
.item-input.error:focus {
|
|
405
|
-
border-color: var(--pui-text-danger);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
.error-message {
|
|
409
|
-
padding: var(--pui-spacing-1_5) var(--pui-spacing-3);
|
|
410
|
-
font-size: var(--pui-font-size-xs);
|
|
411
|
-
color: var(--pui-text-danger);
|
|
412
|
-
background-color: var(--pui-bg-subtle);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
.quick-add {
|
|
416
|
-
padding: var(--pui-spacing-2) var(--pui-spacing-3);
|
|
417
|
-
background-color: var(--pui-bg-subtle);
|
|
418
|
-
display: flex;
|
|
419
|
-
justify-content: flex-end;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
391
|
.summary {
|
|
423
392
|
display: flex;
|
|
424
|
-
gap:
|
|
425
|
-
padding:
|
|
426
|
-
font-size:
|
|
427
|
-
color: var(--
|
|
428
|
-
margin-top:
|
|
393
|
+
gap: 1rem;
|
|
394
|
+
padding: 0.5rem 0;
|
|
395
|
+
font-size: 0.875rem;
|
|
396
|
+
color: var(--text-muted);
|
|
397
|
+
margin-top: 0.5rem;
|
|
429
398
|
}
|
|
430
399
|
|
|
431
400
|
.summary-count {
|
|
432
|
-
font-weight:
|
|
401
|
+
font-weight: 500;
|
|
433
402
|
}
|
|
434
403
|
|
|
435
404
|
.summary-default {
|
|
436
|
-
color: var(--
|
|
405
|
+
color: var(--accent-color);
|
|
437
406
|
}
|
|
438
407
|
|
|
439
|
-
:global(.dark) .add-row
|
|
440
|
-
|
|
441
|
-
:global(.dark) .error-message {
|
|
442
|
-
background-color: var(--pui-bg-subtle);
|
|
408
|
+
:global(.dark) .add-row {
|
|
409
|
+
background-color: rgba(var(--ui-accent-rgbc), 0.02);
|
|
443
410
|
}
|
|
444
411
|
|
|
445
412
|
/* Responsive adjustments */
|
|
@@ -448,13 +415,19 @@
|
|
|
448
415
|
.item-row,
|
|
449
416
|
.add-row {
|
|
450
417
|
grid-template-columns: 2.5rem 1fr 1fr 2rem;
|
|
451
|
-
gap:
|
|
452
|
-
padding:
|
|
418
|
+
gap: 0.25rem;
|
|
419
|
+
padding: 0.375rem 0.5rem;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.no-selected .header-row,
|
|
423
|
+
.no-selected .item-row,
|
|
424
|
+
.no-selected .add-row {
|
|
425
|
+
grid-template-columns: 1fr 1fr 2rem;
|
|
453
426
|
}
|
|
454
427
|
|
|
455
428
|
.item-input {
|
|
456
|
-
padding:
|
|
457
|
-
font-size:
|
|
429
|
+
padding: 0.25rem 0.375rem;
|
|
430
|
+
font-size: 0.8125rem;
|
|
458
431
|
}
|
|
459
432
|
}
|
|
460
433
|
</style>
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import type { ComboBoxItem } from '../models/ComboBoxItem.js';
|
|
2
|
-
interface
|
|
2
|
+
export interface ComboBoxItemBuilderProps {
|
|
3
|
+
/** Label text displayed above the builder */
|
|
3
4
|
label?: string;
|
|
5
|
+
/** Array of combo box items (bindable) */
|
|
4
6
|
items?: ComboBoxItem[];
|
|
7
|
+
/** Array of default selected values (bindable) */
|
|
5
8
|
defaultValues?: string[];
|
|
9
|
+
/** Header text for the label column */
|
|
10
|
+
labelHeader?: string;
|
|
11
|
+
/** Header text for the value column */
|
|
12
|
+
valueHeader?: string;
|
|
13
|
+
/** Placeholder for label input field (defaults to labelHeader) */
|
|
6
14
|
labelPlaceholder?: string;
|
|
15
|
+
/** Placeholder for value input field (defaults to valueHeader) */
|
|
7
16
|
valuePlaceholder?: string;
|
|
17
|
+
/** Hide the "Selected" column and skip default-value tracking */
|
|
18
|
+
hideSelected?: boolean;
|
|
19
|
+
/** Disable auto-copying the label into the value field */
|
|
20
|
+
disableValueSync?: boolean;
|
|
21
|
+
/** Allow multiple items to share the same value. Note: default-value tracking is keyed by value, so items sharing a value also share their selected state */
|
|
22
|
+
allowDuplicateValues?: boolean;
|
|
23
|
+
/** Additional CSS classes */
|
|
8
24
|
class?: string;
|
|
25
|
+
/** Callback when items or default values change */
|
|
9
26
|
onchange?: (items: ComboBoxItem[], defaultValues: string[]) => void;
|
|
10
27
|
}
|
|
11
|
-
declare const ComboBoxItemBuilder: import("svelte").Component<
|
|
28
|
+
declare const ComboBoxItemBuilder: import("svelte").Component<ComboBoxItemBuilderProps, {}, "items" | "defaultValues">;
|
|
12
29
|
type ComboBoxItemBuilder = ReturnType<typeof ComboBoxItemBuilder>;
|
|
13
30
|
export default ComboBoxItemBuilder;
|