@dosgato/dialog 1.2.7 → 1.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/dist/Button.svelte +15 -13
- package/dist/ButtonGroup.svelte +44 -40
- package/dist/Checkbox.svelte +13 -12
- package/dist/Container.svelte +50 -46
- package/dist/Dialog.svelte +56 -41
- package/dist/FieldAutocomplete.svelte +77 -70
- package/dist/FieldCheckbox.svelte +25 -22
- package/dist/FieldChoices.svelte +74 -68
- package/dist/FieldChooserLink.svelte +148 -150
- package/dist/FieldDate.svelte +19 -18
- package/dist/FieldDateTime.svelte +36 -34
- package/dist/FieldDualListbox.svelte +80 -79
- package/dist/FieldHidden.svelte +16 -15
- package/dist/FieldIdentifier.svelte +18 -17
- package/dist/FieldMultiple.svelte +71 -74
- package/dist/FieldMultiselect.svelte +86 -59
- package/dist/FieldMultiselect.svelte.d.ts +13 -1
- package/dist/FieldNumber.svelte +20 -19
- package/dist/FieldRadio.svelte +42 -41
- package/dist/FieldSelect.svelte +45 -45
- package/dist/FieldStandard.svelte +28 -27
- package/dist/FieldText.svelte +27 -24
- package/dist/FieldTextArea.svelte +24 -24
- package/dist/FileIcon.svelte +10 -8
- package/dist/Form.svelte +40 -18
- package/dist/Form.svelte.d.ts +15 -13
- package/dist/FormDialog.svelte +40 -25
- package/dist/FormDialog.svelte.d.ts +23 -17
- package/dist/Icon.svelte +38 -33
- package/dist/InlineMessage.svelte +31 -29
- package/dist/InlineMessages.svelte +10 -7
- package/dist/Input.svelte +40 -39
- package/dist/Listbox.svelte +102 -109
- package/dist/MaxLength.svelte +19 -18
- package/dist/Radio.svelte +18 -15
- package/dist/Switcher.svelte +37 -33
- package/dist/Tab.svelte +23 -21
- package/dist/Tabs.svelte +111 -110
- package/dist/Tooltip.svelte +7 -7
- package/dist/chooser/Chooser.svelte +83 -76
- package/dist/chooser/ChooserPreview.svelte +16 -14
- package/dist/chooser/Details.svelte +6 -4
- package/dist/chooser/Thumbnail.svelte +20 -16
- package/dist/chooser/UploadUI.svelte +78 -69
- package/dist/code/CodeEditor.svelte +63 -66
- package/dist/code/FieldCodeEditor.svelte +21 -19
- package/dist/colorpicker/FieldColorPicker.svelte +36 -35
- package/dist/cropper/FieldCropper.svelte +142 -141
- package/dist/iconpicker/FieldIconPicker.svelte +102 -94
- package/dist/imageposition/FieldImagePosition.svelte +107 -98
- package/dist/tagpicker/FieldTagPicker.svelte +104 -38
- package/dist/tagpicker/FieldTagPicker.svelte.d.ts +7 -0
- package/dist/tree/LoadIcon.svelte +0 -1
- package/dist/tree/Tree.svelte +198 -192
- package/dist/tree/Tree.svelte.d.ts +5 -5
- package/dist/tree/TreeCell.svelte +10 -6
- package/dist/tree/TreeCell.svelte.d.ts +5 -5
- package/dist/tree/TreeNode.svelte +213 -241
- package/dist/tree/TreeNode.svelte.d.ts +5 -5
- package/package.json +9 -10
|
@@ -1,87 +1,89 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export let
|
|
13
|
-
export let
|
|
14
|
-
export let
|
|
15
|
-
export let
|
|
16
|
-
export let
|
|
17
|
-
export let
|
|
18
|
-
export let
|
|
19
|
-
export let
|
|
20
|
-
export let
|
|
21
|
-
export let
|
|
22
|
-
export let
|
|
23
|
-
let
|
|
24
|
-
let
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import menuRight from '@iconify-icons/mdi/menu-right.js'
|
|
3
|
+
import menuLeft from '@iconify-icons/mdi/menu-left.js'
|
|
4
|
+
import { type PopupMenuItem, ScreenReaderOnly, modifierKey } from '@txstate-mws/svelte-components'
|
|
5
|
+
import { arraySerialize } from '@txstate-mws/svelte-forms'
|
|
6
|
+
import { randomid } from 'txstate-utils'
|
|
7
|
+
import FieldStandard from './FieldStandard.svelte'
|
|
8
|
+
import Icon from './Icon.svelte'
|
|
9
|
+
import Listbox from './Listbox.svelte'
|
|
10
|
+
import { getDescribedBy } from './'
|
|
11
|
+
|
|
12
|
+
export let id: string | undefined = undefined
|
|
13
|
+
export let path: string
|
|
14
|
+
export let label: string = ''
|
|
15
|
+
export let sourceLabel: string = 'Available'
|
|
16
|
+
export let selectedLabel: string = 'Selected'
|
|
17
|
+
export let multiselect: boolean = false
|
|
18
|
+
export let choices: PopupMenuItem[]
|
|
19
|
+
export let defaultValue: string[] = []
|
|
20
|
+
export let conditional: boolean | undefined = undefined
|
|
21
|
+
export let required = false
|
|
22
|
+
export let related: true | number = 0
|
|
23
|
+
export let extradescid: string | undefined = undefined
|
|
24
|
+
export let helptext: string | undefined = undefined
|
|
25
|
+
|
|
26
|
+
let itemsToAdd: PopupMenuItem[] = [] // the items selected in the left listbox
|
|
27
|
+
let itemsToRemove: PopupMenuItem[] = [] // the items selected in the right listbox
|
|
28
|
+
let instructions: string = 'test'
|
|
29
|
+
|
|
30
|
+
$: {
|
|
31
|
+
if (itemsToAdd.length === 1) instructions = `Press right arrow key to move selected ${sourceLabel} items to ${selectedLabel} items list.`
|
|
32
|
+
else instructions = ''
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
$: {
|
|
36
|
+
if (itemsToRemove.length === 1) instructions = `Press left arrow key to move selected ${selectedLabel} items to ${sourceLabel} list.`
|
|
37
|
+
else instructions = ''
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const descid = randomid()
|
|
41
|
+
|
|
42
|
+
function addToSelected (value: string[], setVal: (val: any) => void) {
|
|
40
43
|
return () => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
44
|
+
const selected = value.concat(itemsToAdd.map(item => item.value))
|
|
45
|
+
itemsToAdd = []
|
|
46
|
+
setVal(selected)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function addToAvailable (value: string[], setVal: (val: any) => void) {
|
|
47
51
|
return () => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
52
|
+
const itemsToRemoveSet = new Set(itemsToRemove.map(i => i.value))
|
|
53
|
+
const selected = value.filter(v => !itemsToRemoveSet.has(v))
|
|
54
|
+
itemsToRemove = []
|
|
55
|
+
setVal(selected)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function valueToSelectedChoices (value: string[]) {
|
|
55
60
|
// keep the selected options ordered as they were in the available options
|
|
56
|
-
const valueSet = new Set(value)
|
|
57
|
-
const ret = []
|
|
61
|
+
const valueSet = new Set(value)
|
|
62
|
+
const ret: PopupMenuItem[] = []
|
|
58
63
|
for (const choice of choices) {
|
|
59
|
-
|
|
60
|
-
ret.push({ value: choice.value, label: choice.label || choice.value });
|
|
64
|
+
if (valueSet.has(choice.value)) ret.push({ value: choice.value, label: choice.label || choice.value })
|
|
61
65
|
}
|
|
62
|
-
return ret
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
return ret
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getAvailable (value: string[]) {
|
|
70
|
+
return choices.filter(choice => !value.includes(choice.value))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function onkeydown (value: string[], setVal: (val: any) => void) {
|
|
68
74
|
return (e) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
addToAvailable(value, setVal)();
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
75
|
+
if (modifierKey(e)) return
|
|
76
|
+
if (e.key === 'ArrowRight') {
|
|
77
|
+
e.preventDefault()
|
|
78
|
+
if (itemsToAdd.length === 0) return
|
|
79
|
+
addToSelected(value, setVal)()
|
|
80
|
+
} else if (e.key === 'ArrowLeft') {
|
|
81
|
+
e.preventDefault()
|
|
82
|
+
if (itemsToRemove.length === 0) return
|
|
83
|
+
addToAvailable(value, setVal)()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
85
87
|
</script>
|
|
86
88
|
|
|
87
89
|
<FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} {descid} {related} {helptext} let:value let:valid let:invalid let:id let:onBlur let:setVal let:helptextid let:messagesid serialize={arraySerialize}>
|
|
@@ -117,5 +119,4 @@ function onkeydown(value, setVal) {
|
|
|
117
119
|
border: 0;
|
|
118
120
|
padding: 0;
|
|
119
121
|
}
|
|
120
|
-
|
|
121
122
|
</style>
|
package/dist/FieldHidden.svelte
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export let
|
|
6
|
-
export let
|
|
7
|
-
export let
|
|
8
|
-
export let
|
|
9
|
-
export let
|
|
10
|
-
export let
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Field, type FormStore, FORM_CONTEXT, FORM_INHERITED_PATH } from '@txstate-mws/svelte-forms'
|
|
3
|
+
import { getContext } from 'svelte'
|
|
4
|
+
import { isNotBlank } from 'txstate-utils'
|
|
5
|
+
export let id: string | undefined = undefined
|
|
6
|
+
export let path: string
|
|
7
|
+
export let value: string | boolean | number = ''
|
|
8
|
+
export let notNull = false
|
|
9
|
+
export let boolean = false
|
|
10
|
+
export let number = false
|
|
11
|
+
export let conditional: boolean | undefined = undefined
|
|
12
|
+
const store = getContext<FormStore>(FORM_CONTEXT)
|
|
13
|
+
const inheritedPath = getContext<string>(FORM_INHERITED_PATH)
|
|
14
|
+
const finalPath = [inheritedPath, path].filter(isNotBlank).join('.')
|
|
15
|
+
$: void store.setField(finalPath, value)
|
|
15
16
|
</script>
|
|
16
17
|
|
|
17
|
-
<Field {path} {conditional} {boolean} {number}
|
|
18
|
+
<Field {path} {conditional} {boolean} {number} {notNull} let:value>
|
|
18
19
|
<input type="hidden" name={path} {value} {id}>
|
|
19
20
|
</Field>
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import {
|
|
3
|
-
import { onMount } from 'svelte'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export let
|
|
7
|
-
let
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Field, FORM_INHERITED_PATH, type FormStore, FORM_CONTEXT } from '@txstate-mws/svelte-forms'
|
|
3
|
+
import { getContext, onMount } from 'svelte'
|
|
4
|
+
import { isNotBlank, randomid } from 'txstate-utils'
|
|
5
|
+
|
|
6
|
+
export let path: string
|
|
7
|
+
export let conditional: boolean | undefined = undefined
|
|
8
|
+
export let length: number = 10
|
|
9
|
+
|
|
10
|
+
const store = getContext<FormStore>(FORM_CONTEXT)
|
|
11
|
+
const inheritedPath = getContext<string>(FORM_INHERITED_PATH)
|
|
12
|
+
const finalPath = [inheritedPath, path].filter(isNotBlank).join('.')
|
|
13
|
+
const valueStore = store.getField<string | null>(finalPath)
|
|
14
|
+
|
|
15
|
+
onMount(() => {
|
|
16
|
+
if (!$valueStore) store.setField(finalPath, randomid(length))
|
|
17
|
+
})
|
|
16
18
|
</script>
|
|
17
19
|
|
|
18
|
-
<Field {path} {conditional} defaultValue={randomid(length)}
|
|
19
|
-
{@const _ = updateValue(value, setVal)}
|
|
20
|
+
<Field {path} {conditional} defaultValue={randomid(length)} notNull let:value>
|
|
20
21
|
<input type="hidden" name={path} {value}>
|
|
21
22
|
</Field>
|
|
@@ -1,84 +1,82 @@
|
|
|
1
|
-
<script context="module">
|
|
2
|
-
|
|
1
|
+
<script lang="ts" context="module">
|
|
2
|
+
export const DG_DIALOG_FIELD_MULTIPLE = {}
|
|
3
|
+
function noOp (..._: any[]) { return '' }
|
|
3
4
|
</script>
|
|
4
|
-
<script>
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import Button from './Button.svelte'
|
|
13
|
-
import Container from './Container.svelte'
|
|
14
|
-
import Icon from './Icon.svelte'
|
|
15
|
-
|
|
16
|
-
export let
|
|
17
|
-
export let
|
|
18
|
-
export let
|
|
19
|
-
export let
|
|
20
|
-
export let
|
|
21
|
-
export let
|
|
22
|
-
export let
|
|
23
|
-
export let
|
|
24
|
-
export let
|
|
25
|
-
export let
|
|
26
|
-
export let
|
|
27
|
-
export let
|
|
28
|
-
export let
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
function moveUpAndFocus(onMove, idx) {
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import caretCircleDown from '@iconify-icons/ph/caret-circle-down-fill'
|
|
7
|
+
import caretCircleUp from '@iconify-icons/ph/caret-circle-up-fill'
|
|
8
|
+
import plusCircleLight from '@iconify-icons/ph/plus-circle-light'
|
|
9
|
+
import xCircle from '@iconify-icons/ph/x-circle-fill'
|
|
10
|
+
import { AddMore, type Feedback } from '@txstate-mws/svelte-forms'
|
|
11
|
+
import { setContext } from 'svelte'
|
|
12
|
+
import { writable } from 'svelte/store'
|
|
13
|
+
import Button from './Button.svelte'
|
|
14
|
+
import Container from './Container.svelte'
|
|
15
|
+
import Icon from './Icon.svelte'
|
|
16
|
+
|
|
17
|
+
export let path: string
|
|
18
|
+
export let label: string
|
|
19
|
+
export let initialState: any | ((index: number) => any) = undefined
|
|
20
|
+
export let minLength = 1
|
|
21
|
+
export let maxLength: number | undefined = undefined
|
|
22
|
+
export let compact = false
|
|
23
|
+
export let removable = false
|
|
24
|
+
export let reorder = false
|
|
25
|
+
export let conditional: boolean | undefined = undefined
|
|
26
|
+
export let addMoreText = 'Add'
|
|
27
|
+
export let maxedText = addMoreText
|
|
28
|
+
export let addMoreClass: string | undefined = undefined
|
|
29
|
+
export let related: true | number = 0
|
|
30
|
+
export let helptext: string | undefined = undefined
|
|
31
|
+
/**
|
|
32
|
+
* If you want to ask users if they're sure before removing an array element, fill this
|
|
33
|
+
* prop with the question that should be in the confirmation dialog.
|
|
34
|
+
*
|
|
35
|
+
* Alternatively, you can provide a function to generate the question from the item being
|
|
36
|
+
* deleted. e.g. (item) => `Are you sure you want to delete ${item.name}?`
|
|
37
|
+
*/
|
|
38
|
+
export let confirmDelete: string | ((item: any) => string) | undefined = undefined
|
|
39
|
+
|
|
40
|
+
const fieldMultipleContext: { helptextid: string | undefined } = { helptextid: undefined }
|
|
41
|
+
setContext(DG_DIALOG_FIELD_MULTIPLE, fieldMultipleContext)
|
|
42
|
+
|
|
43
|
+
const reorderupelements: HTMLButtonElement[] = []
|
|
44
|
+
const reorderdownelements: HTMLButtonElement[] = []
|
|
45
|
+
function moveUpAndFocus (onMove: () => void, idx: number) {
|
|
46
46
|
return () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
function moveDownAndFocus(onMove, idx) {
|
|
47
|
+
onMove()
|
|
48
|
+
let newFocus = reorderupelements[idx - 1]
|
|
49
|
+
if (newFocus) {
|
|
50
|
+
if (newFocus.disabled) newFocus = reorderdownelements[idx - 1]
|
|
51
|
+
newFocus.focus()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function moveDownAndFocus (onMove: () => void, idx: number) {
|
|
57
56
|
return () => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
function confirmedDelete(onDelete, item) {
|
|
57
|
+
onMove()
|
|
58
|
+
let newFocus = reorderdownelements[idx + 1]
|
|
59
|
+
if (newFocus) {
|
|
60
|
+
if (newFocus.disabled) newFocus = reorderupelements[idx + 1]
|
|
61
|
+
newFocus.focus()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function confirmedDelete (onDelete: () => void, item: any) {
|
|
68
67
|
return () => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
$: messages = compact ? $messageStore : [];
|
|
68
|
+
if (confirmDelete == null) return onDelete()
|
|
69
|
+
const msg = typeof confirmDelete === 'string' ? confirmDelete : confirmDelete(item)
|
|
70
|
+
if (confirm(msg)) onDelete()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let messages: Feedback[] = []
|
|
77
75
|
</script>
|
|
78
76
|
|
|
79
77
|
<Container {path} {label} {messages} {conditional} {related} {helptext} let:helptextid>
|
|
80
78
|
{noOp(fieldMultipleContext.helptextid = helptextid)}
|
|
81
|
-
<AddMore {path} {initialState} {minLength} {maxLength} {conditional} let:path let:currentLength let:maxLength let:index let:minned let:maxed let:value let:onDelete let:onMoveUp let:onMoveDown>
|
|
79
|
+
<AddMore bind:messages {path} {initialState} {minLength} {maxLength} {conditional} let:path let:currentLength let:maxLength let:index let:minned let:maxed let:value let:onDelete let:onMoveUp let:onMoveDown>
|
|
82
80
|
{@const showDelete = removable && !minned}
|
|
83
81
|
{@const showMove = reorder && currentLength > 1}
|
|
84
82
|
<div class="dialog-multiple" class:has-delete-icon={showDelete} class:has-move-icon={showMove} class:first={index === 0}>
|
|
@@ -138,5 +136,4 @@ $: messages = compact ? $messageStore : [];
|
|
|
138
136
|
.dialog-multiple-buttons button:disabled {
|
|
139
137
|
color: #6d6d6d;
|
|
140
138
|
}
|
|
141
|
-
|
|
142
139
|
</style>
|
|
@@ -6,68 +6,92 @@
|
|
|
6
6
|
controlled by the parent component via the `getOptions` function that will be used as a
|
|
7
7
|
debounced callback on the contents of the text input.
|
|
8
8
|
-->
|
|
9
|
-
<script>
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
`PopupMenuItem[]`
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export let
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export let
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export let
|
|
33
|
-
export let
|
|
34
|
-
|
|
35
|
-
export let
|
|
36
|
-
export let
|
|
37
|
-
|
|
38
|
-
export let
|
|
39
|
-
export let
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import { MultiSelect } from '@txstate-mws/svelte-components'
|
|
11
|
+
import type { PopupMenuItem } from '@txstate-mws/svelte-components'
|
|
12
|
+
import { arraySerialize } from '@txstate-mws/svelte-forms'
|
|
13
|
+
import { Store } from '@txstate-mws/svelte-store'
|
|
14
|
+
import { onMount } from 'svelte'
|
|
15
|
+
import { isNotBlank } from 'txstate-utils'
|
|
16
|
+
import FieldStandard from './FieldStandard.svelte'
|
|
17
|
+
import { getDescribedBy } from './helpers'
|
|
18
|
+
|
|
19
|
+
export let path: string
|
|
20
|
+
/** Function to pass to the component that tells it how to use the text
|
|
21
|
+
in the text input to determine what `PopupMenuItem[]` should be displayed
|
|
22
|
+
in the `<PopupMenu>`. Items already 'selected' from the popup menu will be
|
|
23
|
+
tracked and automatically filtered from the popup if returned as one of the
|
|
24
|
+
`PopupMenuItem[]` by `getOptions`. */
|
|
25
|
+
export let getOptions: (search: string) => Promise<PopupMenuItem[]>
|
|
26
|
+
export let id: string | undefined = undefined
|
|
27
|
+
export let label: string = ''
|
|
28
|
+
/** Text to display in the text input when it's empty. */
|
|
29
|
+
export let placeholder = ''
|
|
30
|
+
/** When there are no items (e.g. it's a filtered search and there were no results), we still display one
|
|
31
|
+
disabled item in the menu to let the user know what is going on. Use this prop to specify the message. */
|
|
32
|
+
export let emptyText: string | undefined = undefined
|
|
33
|
+
export let disabled = false
|
|
34
|
+
export let defaultValue: string[] = []
|
|
35
|
+
export let conditional: boolean | undefined = undefined
|
|
36
|
+
export let required = false
|
|
37
|
+
/** Max number of selections to be allowed before disabling the input - 0 for unlimited. */
|
|
38
|
+
export let maxSelections = 0
|
|
39
|
+
export let lookupByValue: (val: string) => Promise<PopupMenuItem | undefined> = async (val) => { const options = await getOptions(val); return options.find((opt) => opt.value === val) }
|
|
40
|
+
export let related: true | number = 0
|
|
41
|
+
export let extradescid: string | undefined = undefined
|
|
42
|
+
export let helptext: string | undefined = undefined
|
|
43
|
+
export let menuContainerClass = ''
|
|
44
|
+
export let menuClass = ''
|
|
45
|
+
export let menuItemClass = ''
|
|
46
|
+
export let menuItemHilitedClass = ''
|
|
47
|
+
export let menuCategoryClass = ''
|
|
48
|
+
/** Add a Delete All button to clear all selected items */
|
|
49
|
+
export let includeDeleteAll = false
|
|
50
|
+
/** Show a confirmation message before clearing all selections */
|
|
51
|
+
export let confirmDelete: string | undefined = undefined
|
|
52
|
+
|
|
53
|
+
export let selectedItemLabel: ((item: PopupMenuItem) => string) | undefined = (item) => item.label || item.value
|
|
54
|
+
|
|
55
|
+
/** Each time we run getOptions we will save the value -> label mappings that it finds, so that we can display labels on pills. */
|
|
56
|
+
const valueToLabel: Record<string, string> = {}
|
|
57
|
+
const valueToGroup: Record<string, string | undefined> = {}
|
|
58
|
+
|
|
59
|
+
async function wrapGetOptions (search: string) {
|
|
60
|
+
const opts = await getOptions(search)
|
|
44
61
|
/* If no options are returned with the search term, we can end up with an infinite loop the first time reactToValue calls wrapGetOptions */
|
|
45
62
|
// if (opts.length === 0) opts = await getOptions('')
|
|
46
63
|
for (const opt of opts) {
|
|
47
|
-
|
|
64
|
+
valueToLabel[opt.value] = opt.label || opt.value
|
|
65
|
+
valueToGroup[opt.value] = opt.group
|
|
48
66
|
}
|
|
49
|
-
return opts
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
return opts
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const selectedStore = new Store<{ value: string, label: string, group?: string }[]>([])
|
|
71
|
+
|
|
72
|
+
let hasInit = !defaultValue.length
|
|
73
|
+
|
|
74
|
+
let inputelement: HTMLElement
|
|
75
|
+
onMount(async () => {
|
|
76
|
+
await reactToValue(defaultValue)
|
|
77
|
+
hasInit = true
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
If we get a value from the form that we haven't seen in the popup menu yet, we won't have a label for it.
|
|
82
|
+
This function runs getOptions on any selected values for which the label is currently unknown. */
|
|
83
|
+
async function reactToValue (value: string[]) {
|
|
84
|
+
await Promise.all(value.map(async v => {
|
|
85
|
+
if (!valueToLabel[v]) {
|
|
86
|
+
const item = await lookupByValue(v)
|
|
87
|
+
if (item) {
|
|
88
|
+
valueToLabel[item.value] = item.label ?? item.value
|
|
89
|
+
valueToGroup[item.value] = item.group
|
|
67
90
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
91
|
+
}
|
|
92
|
+
}))
|
|
93
|
+
selectedStore.set(value.map(v => ({ value: v, label: valueToLabel[v], group: valueToGroup[v] })).filter(v => isNotBlank(v.label)))
|
|
94
|
+
}
|
|
71
95
|
</script>
|
|
72
96
|
|
|
73
97
|
<div bind:this={inputelement}></div>
|
|
@@ -79,7 +103,11 @@ async function reactToValue(value) {
|
|
|
79
103
|
{disabled} {maxSelections} selected={$selectedStore} {placeholder} {emptyText} getOptions={wrapGetOptions}
|
|
80
104
|
inputClass='multiselect-input'
|
|
81
105
|
on:change={e => setVal(e.detail.map(itm => itm.value))} on:blur={onBlur}
|
|
82
|
-
|
|
106
|
+
{includeDeleteAll} {confirmDelete} {selectedItemLabel}
|
|
107
|
+
{menuClass} {menuContainerClass} {menuItemClass} {menuItemHilitedClass} {menuCategoryClass}
|
|
108
|
+
>
|
|
109
|
+
<slot name="deleteall" slot="deleteall" />
|
|
110
|
+
</MultiSelect>
|
|
83
111
|
</div>
|
|
84
112
|
</FieldStandard>
|
|
85
113
|
{/if}
|
|
@@ -91,5 +119,4 @@ async function reactToValue(value) {
|
|
|
91
119
|
:global(.multiselect-input) {
|
|
92
120
|
width: 100%
|
|
93
121
|
}
|
|
94
|
-
|
|
95
122
|
</style>
|
|
@@ -22,11 +22,23 @@ declare const __propDef: {
|
|
|
22
22
|
related?: true | number;
|
|
23
23
|
extradescid?: string | undefined;
|
|
24
24
|
helptext?: string | undefined;
|
|
25
|
+
menuContainerClass?: string;
|
|
26
|
+
menuClass?: string;
|
|
27
|
+
menuItemClass?: string;
|
|
28
|
+
menuItemHilitedClass?: string;
|
|
29
|
+
menuCategoryClass?: string;
|
|
30
|
+
/** Add a Delete All button to clear all selected items */ includeDeleteAll?: boolean;
|
|
31
|
+
/** Show a confirmation message before clearing all selections */ confirmDelete?: string | undefined;
|
|
32
|
+
selectedItemLabel?: ((item: PopupMenuItem) => string) | undefined;
|
|
25
33
|
};
|
|
26
34
|
events: {
|
|
27
35
|
[evt: string]: CustomEvent<any>;
|
|
28
36
|
};
|
|
29
|
-
slots: {
|
|
37
|
+
slots: {
|
|
38
|
+
deleteall: {
|
|
39
|
+
slot: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
30
42
|
};
|
|
31
43
|
export type FieldMultiselectProps = typeof __propDef.props;
|
|
32
44
|
export type FieldMultiselectEvents = typeof __propDef.events;
|