firstly 0.3.0 → 0.4.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.
Files changed (163) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/esm/SqlDatabase/FF_LogToConsole.js +9 -14
  3. package/esm/carbone/CarboneController.js +2 -1
  4. package/esm/changeLog/index.d.ts +0 -5
  5. package/esm/core/helper.d.ts +2 -0
  6. package/esm/core/helper.js +3 -0
  7. package/esm/core/index.d.ts +0 -0
  8. package/esm/core/index.js +5 -0
  9. package/esm/core/tailwind.d.ts +21 -0
  10. package/esm/core/tailwind.js +22 -0
  11. package/esm/core/tryCatch.d.ts +44 -0
  12. package/esm/core/tryCatch.js +34 -0
  13. package/esm/cron/server/index.js +1 -1
  14. package/esm/feedback/FeedbackController.js +3 -2
  15. package/esm/feedback/index.d.ts +7 -2
  16. package/esm/feedback/index.js +1 -2
  17. package/esm/feedback/server/index.d.ts +0 -5
  18. package/esm/feedback/server/index.js +1 -1
  19. package/esm/formats/strings.js +2 -2
  20. package/esm/index.d.ts +16 -0
  21. package/esm/index.js +13 -0
  22. package/esm/svelte/FF_Repo.svelte.d.ts +0 -2
  23. package/esm/svelte/FF_Repo.svelte.js +1 -17
  24. package/esm/svelte/helpers/debounce.js +1 -1
  25. package/esm/svelte/index.d.ts +2 -24
  26. package/esm/svelte/index.js +2 -22
  27. package/esm/{ui → svelte/ui}/Icon.svelte +1 -1
  28. package/esm/virtual/StateDemoEnum.d.ts +3 -3
  29. package/esm/virtual/StateDemoEnum.js +3 -3
  30. package/esm/virtual/UIEntity.js +1 -2
  31. package/package.json +6 -24
  32. package/esm/bin/cmd.d.ts +0 -1
  33. package/esm/bin/cmd.js +0 -638
  34. package/esm/feedback/ui/DialogIssue.svelte +0 -149
  35. package/esm/feedback/ui/DialogIssue.svelte.d.ts +0 -22
  36. package/esm/feedback/ui/DialogIssues.svelte +0 -114
  37. package/esm/feedback/ui/DialogIssues.svelte.d.ts +0 -22
  38. package/esm/feedback/ui/DialogMilestones.svelte +0 -43
  39. package/esm/feedback/ui/DialogMilestones.svelte.d.ts +0 -20
  40. package/esm/feedback/ui/Feedback.svelte +0 -16
  41. package/esm/feedback/ui/Feedback.svelte.d.ts +0 -18
  42. package/esm/internals/FF_Entity.d.ts +0 -2
  43. package/esm/internals/FF_Fields.d.ts +0 -11
  44. package/esm/internals/FF_Fields.js +0 -144
  45. package/esm/internals/cellsBuildor.d.ts +0 -47
  46. package/esm/internals/cellsBuildor.js +0 -141
  47. package/esm/internals/helper.d.ts +0 -49
  48. package/esm/internals/helper.js +0 -162
  49. package/esm/internals/index.d.ts +0 -78
  50. package/esm/internals/index.js +0 -45
  51. package/esm/internals/storeItem.d.ts +0 -19
  52. package/esm/internals/storeItem.js +0 -190
  53. package/esm/internals/storeList.d.ts +0 -34
  54. package/esm/internals/storeList.js +0 -108
  55. package/esm/internals/theme.d.ts +0 -4
  56. package/esm/internals/theme.js +0 -4
  57. package/esm/server/index.d.ts +0 -52
  58. package/esm/server/index.js +0 -87
  59. package/esm/svelte/FF_Cell.svelte +0 -103
  60. package/esm/svelte/FF_Cell.svelte.d.ts +0 -33
  61. package/esm/svelte/FF_Cell_Caption.svelte +0 -20
  62. package/esm/svelte/FF_Cell_Caption.svelte.d.ts +0 -31
  63. package/esm/svelte/FF_Cell_Display.svelte +0 -61
  64. package/esm/svelte/FF_Cell_Display.svelte.d.ts +0 -29
  65. package/esm/svelte/FF_Cell_Edit.svelte +0 -104
  66. package/esm/svelte/FF_Cell_Edit.svelte.d.ts +0 -32
  67. package/esm/svelte/FF_Cell_Error.svelte +0 -20
  68. package/esm/svelte/FF_Cell_Error.svelte.d.ts +0 -31
  69. package/esm/svelte/FF_Cell_Hint.svelte +0 -20
  70. package/esm/svelte/FF_Cell_Hint.svelte.d.ts +0 -31
  71. package/esm/svelte/FF_Config.svelte +0 -29
  72. package/esm/svelte/FF_Config.svelte.d.ts +0 -9
  73. package/esm/svelte/FF_Form.svelte +0 -155
  74. package/esm/svelte/FF_Form.svelte.d.ts +0 -37
  75. package/esm/svelte/FF_Grid.svelte +0 -257
  76. package/esm/svelte/FF_Grid.svelte.d.ts +0 -37
  77. package/esm/svelte/FF_Layout.svelte +0 -62
  78. package/esm/svelte/FF_Layout.svelte.d.ts +0 -31
  79. package/esm/svelte/actions/intersection.d.ts +0 -6
  80. package/esm/svelte/actions/intersection.js +0 -17
  81. package/esm/svelte/customField.d.ts +0 -69
  82. package/esm/svelte/customField.js +0 -4
  83. package/esm/svelte/dialog/DialogManagement.svelte +0 -98
  84. package/esm/svelte/dialog/DialogManagement.svelte.d.ts +0 -18
  85. package/esm/svelte/dialog/DialogPrimitive.svelte +0 -156
  86. package/esm/svelte/dialog/DialogPrimitive.svelte.d.ts +0 -38
  87. package/esm/svelte/dialog/dialog.d.ts +0 -58
  88. package/esm/svelte/dialog/dialog.js +0 -130
  89. package/esm/svelte/ff_Config.svelte.d.ts +0 -91
  90. package/esm/svelte/ff_Config.svelte.js +0 -111
  91. package/esm/svelte/firstly.css +0 -14
  92. package/esm/svelte/helpers.d.ts +0 -30
  93. package/esm/svelte/helpers.js +0 -38
  94. package/esm/svelte/tryCatch.d.ts +0 -12
  95. package/esm/svelte/tryCatch.js +0 -18
  96. package/esm/sveltekit/server/index.d.ts +0 -5
  97. package/esm/sveltekit/server/index.js +0 -24
  98. package/esm/ui/Button.svelte +0 -90
  99. package/esm/ui/Button.svelte.d.ts +0 -11
  100. package/esm/ui/Clipboardable.svelte +0 -25
  101. package/esm/ui/Clipboardable.svelte.d.ts +0 -12
  102. package/esm/ui/Field.svelte +0 -391
  103. package/esm/ui/Field.svelte.d.ts +0 -40
  104. package/esm/ui/FieldGroup.svelte +0 -117
  105. package/esm/ui/FieldGroup.svelte.d.ts +0 -44
  106. package/esm/ui/Grid.svelte +0 -262
  107. package/esm/ui/Grid.svelte.d.ts +0 -57
  108. package/esm/ui/Grid2.svelte +0 -290
  109. package/esm/ui/Grid2.svelte.d.ts +0 -57
  110. package/esm/ui/GridLoading.svelte +0 -58
  111. package/esm/ui/GridLoading.svelte.d.ts +0 -23
  112. package/esm/ui/GridPaginate.svelte +0 -69
  113. package/esm/ui/GridPaginate.svelte.d.ts +0 -23
  114. package/esm/ui/GridPaginate2.svelte +0 -25
  115. package/esm/ui/GridPaginate2.svelte.d.ts +0 -7
  116. package/esm/ui/Loading.svelte +0 -16
  117. package/esm/ui/Loading.svelte.d.ts +0 -31
  118. package/esm/ui/Tooltip.svelte +0 -45
  119. package/esm/ui/Tooltip.svelte.d.ts +0 -32
  120. package/esm/ui/dialog/DialogForm.svelte +0 -76
  121. package/esm/ui/dialog/DialogForm.svelte.d.ts +0 -21
  122. package/esm/ui/dialog/DialogManagement.svelte +0 -96
  123. package/esm/ui/dialog/DialogManagement.svelte.d.ts +0 -26
  124. package/esm/ui/dialog/DialogPrimitive.svelte +0 -90
  125. package/esm/ui/dialog/DialogPrimitive.svelte.d.ts +0 -38
  126. package/esm/ui/dialog/FormEditAction.svelte +0 -62
  127. package/esm/ui/dialog/FormEditAction.svelte.d.ts +0 -31
  128. package/esm/ui/dialog/dialog.d.ts +0 -60
  129. package/esm/ui/dialog/dialog.js +0 -100
  130. package/esm/ui/index.d.ts +0 -6
  131. package/esm/ui/index.js +0 -20
  132. package/esm/ui/internals/FieldContainer.svelte +0 -39
  133. package/esm/ui/internals/FieldContainer.svelte.d.ts +0 -18
  134. package/esm/ui/internals/Input.svelte +0 -143
  135. package/esm/ui/internals/Input.svelte.d.ts +0 -37
  136. package/esm/ui/internals/Textarea.svelte +0 -66
  137. package/esm/ui/internals/Textarea.svelte.d.ts +0 -33
  138. package/esm/ui/internals/select/MultiSelectMelt.svelte +0 -260
  139. package/esm/ui/internals/select/MultiSelectMelt.svelte.d.ts +0 -32
  140. package/esm/ui/internals/select/Select2.svelte +0 -88
  141. package/esm/ui/internals/select/Select2.svelte.d.ts +0 -12
  142. package/esm/ui/internals/select/SelectMelt.svelte +0 -289
  143. package/esm/ui/internals/select/SelectMelt.svelte.d.ts +0 -40
  144. package/esm/ui/internals/select/SelectRadio.svelte +0 -43
  145. package/esm/ui/internals/select/SelectRadio.svelte.d.ts +0 -27
  146. package/esm/ui/link/Link.svelte +0 -33
  147. package/esm/ui/link/Link.svelte.d.ts +0 -33
  148. package/esm/ui/link/LinkPlus.svelte +0 -63
  149. package/esm/ui/link/LinkPlus.svelte.d.ts +0 -9
  150. package/esm/utils/tailwind.d.ts +0 -2
  151. package/esm/utils/tailwind.js +0 -3
  152. package/esm/utils/transition.d.ts +0 -9
  153. package/esm/utils/transition.js +0 -33
  154. /package/esm/{internals → core}/BaseEnum.d.ts +0 -0
  155. /package/esm/{internals → core}/BaseEnum.js +0 -0
  156. /package/esm/{internals → core}/FF_Entity.js +0 -0
  157. /package/esm/{internals → core}/common.d.ts +0 -0
  158. /package/esm/{internals → core}/common.js +0 -0
  159. /package/esm/{utils → core}/types.d.ts +0 -0
  160. /package/esm/{utils → core}/types.js +0 -0
  161. /package/esm/{ui → svelte/ui}/Icon.svelte.d.ts +0 -0
  162. /package/esm/{ui → svelte/ui}/LibIcon.d.ts +0 -0
  163. /package/esm/{ui → svelte/ui}/LibIcon.js +0 -0
@@ -1,260 +0,0 @@
1
- <script lang="ts">
2
- import { createCombobox, createSync, type ComboboxOptionProps } from '@melt-ui/svelte'
3
- import { createEventDispatcher, onMount } from 'svelte'
4
- import { fly } from 'svelte/transition'
5
-
6
- import {
7
- LibIcon_Check,
8
- LibIcon_Cross,
9
- LibIcon_MultiCheck,
10
- LibIcon_Search,
11
- tw,
12
- type BaseItem,
13
- type FF_Icon,
14
- } from '../../../internals'
15
- import Icon from '../../Icon.svelte'
16
-
17
- export let id: string
18
- export let disabled: boolean = false
19
- export let placeholder: string = ''
20
- export let items: BaseItem[] = []
21
- let totalCount: number | undefined = undefined
22
-
23
- export let loadOptions:
24
- | ((str: string) => Promise<{ items: BaseItem[]; totalCount: number }>)
25
- | undefined = undefined
26
- export let values: (string | null)[] | undefined = undefined
27
- export let clearable = false
28
-
29
- const dispatch = createEventDispatcher()
30
-
31
- function dispatchSelectedValues(_data: BaseItem[] | undefined) {
32
- values = _data?.map((_data) => _data.id)
33
- dispatch('selected', _data)
34
- }
35
-
36
- onMount(async () => {
37
- if (loadOptions) {
38
- const lo = await loadOptions('')
39
- items = lo.items
40
- totalCount = lo.totalCount
41
- filteredItems = items
42
- }
43
- })
44
-
45
- const getDefaultValues = (_selectedValue: (string | null)[] | undefined) => {
46
- if (!items) {
47
- return []
48
- }
49
-
50
- const f = items.filter((c) => (_selectedValue ?? []).includes(String(c.id)))
51
- if (f) {
52
- return f.map((c) => toOption(c))
53
- }
54
-
55
- return []
56
- }
57
-
58
- const toOption = (
59
- item: BaseItem,
60
- ): ComboboxOptionProps<BaseItem> & {
61
- icon?: FF_Icon
62
- } => ({
63
- value: item,
64
- label: item.caption,
65
- // icon: item.icon,
66
- // disabled: item.disabled,
67
- })
68
-
69
- const {
70
- elements: { menu, input, option },
71
- states: { open, inputValue, touchedInput, selected: localSelected },
72
- helpers: { isSelected },
73
- } = createCombobox<BaseItem, true>({
74
- forceVisible: true,
75
- multiple: true,
76
- disabled,
77
- ids: { label: id },
78
- })
79
-
80
- const clearSelection = () => {
81
- sync.selected(undefined)
82
- }
83
-
84
- let debounceTimer: ReturnType<typeof setTimeout>
85
- const debounce = (callback: () => void) => {
86
- clearTimeout(debounceTimer)
87
- debounceTimer = setTimeout(
88
- callback,
89
- // debounce only if we have a load option
90
- loadOptions ? 444 : 0,
91
- )
92
- }
93
-
94
- const sync = createSync({ selected: localSelected })
95
- $: items &&
96
- sync.selected(getDefaultValues(values), (v) => {
97
- const list = (v ?? []).map((c) => c.value.id)
98
-
99
- // Create a map to count occurrences of each element
100
- // TODO: switch to: Use SvelteMap instead svelte/prefer-svelte-reactivity
101
- // eslint-disable-next-line
102
- const countMap: Map<string | null, number> = new Map()
103
-
104
- list.forEach((item) => {
105
- countMap.set(item, (countMap.get(item) || 0) + 1)
106
- })
107
-
108
- // Filter the list to include only elements that occur exactly once
109
- const uniqueList: (string | null)[] = list.filter((item) => countMap.get(item) === 1)
110
-
111
- const newIds = uniqueList.sort().join(',')
112
- const oldSelectedValues = (values ?? []).sort().join(',')
113
-
114
- if (newIds !== oldSelectedValues) {
115
- dispatchSelectedValues(
116
- v === undefined
117
- ? undefined
118
- : v.filter((c) => uniqueList.includes(c.value.id)).map((c) => c.value),
119
- )
120
- }
121
- })
122
-
123
- const labelToDisplayInInput = (_localSelected: typeof $localSelected) => {
124
- if (_localSelected === undefined || _localSelected.length === 0) {
125
- return ''
126
- }
127
- if (_localSelected.length === 1) {
128
- return _localSelected[0].label ?? ''
129
- }
130
- return `${_localSelected.length} éléments`
131
- }
132
- $: $inputValue = labelToDisplayInInput($localSelected)
133
-
134
- const iconToDisplayInInput = (_localSelected: typeof $localSelected): FF_Icon => {
135
- if (_localSelected === undefined || _localSelected.length === 0) {
136
- return { data: LibIcon_Search }
137
- }
138
- if (_localSelected.length === 1) {
139
- return _localSelected[0].value.icon ?? { data: LibIcon_Search }
140
- }
141
- return { data: LibIcon_MultiCheck }
142
- }
143
-
144
- const isChecked = (_localSelected: typeof $localSelected, _item: BaseItem) => {
145
- const f = (_localSelected ?? []).filter((c) => c.value?.id === _item.id)
146
- if (f.length > 0) {
147
- return true
148
- }
149
- return false
150
- }
151
-
152
- let filteredItems = items
153
- const calcFilteredItems = (touched: boolean, str: string, values: any) => {
154
- if (touched && !str.endsWith(' éléments')) {
155
- debounce(async () => {
156
- const normalizedInput = str.toLowerCase()
157
-
158
- // In a Multi select we can't filter to the server.
159
- // If we do I don't knwo what to set to $inputValue. and and list gets shorter... So what do we do about items that are selected but not in the list anymore (because of the filter) ?
160
- // if (loadOptions) {
161
- // const lo = await loadOptions(normalizedInput)
162
- // items = lo.items
163
- // totalCount = lo.totalCount
164
- // filteredItems = items
165
- // } else {
166
- filteredItems = items.filter((item) => {
167
- return item.caption?.toLowerCase().includes(normalizedInput)
168
- })
169
- // }
170
- })
171
- } else {
172
- filteredItems = items
173
- }
174
- }
175
-
176
- $: calcFilteredItems($touchedInput, $inputValue, values)
177
- </script>
178
-
179
- <div class="input flex min-w-0 items-center">
180
- <div class="relative">
181
- {#if iconToDisplayInInput($localSelected)}
182
- {@const ico = iconToDisplayInInput($localSelected)}
183
- <Icon data={ico?.data} class={tw(['relative', ico?.class])} style={ico?.style} size={ico?.size}
184
- ></Icon>
185
- {/if}
186
- </div>
187
- <!-- {id} -->
188
- <input
189
- {...$input}
190
- use:$input.action
191
- class="-mr-5 -ml-8 h-full min-w-0 flex-grow bg-transparent px-10"
192
- {placeholder}
193
- />
194
- <div class="pointer-events-none relative right-0 flex gap-2">
195
- {#if clearable && $localSelected && $localSelected.length > 0}
196
- <button on:click={clearSelection} class="pointer-events-auto">
197
- <Icon data={LibIcon_Cross}></Icon>
198
- </button>
199
- {/if}
200
- <!-- {#if $open}
201
- <Icon data={LibIcon_ChevronUp}></Icon>
202
- {:else}
203
- <Icon data={LibIcon_ChevronDown}></Icon>
204
- {/if} -->
205
- </div>
206
- </div>
207
-
208
- {#if $open}
209
- <ul
210
- class="z-50 flex max-h-[300px] flex-col overflow-hidden rounded-lg border border-base-content/20"
211
- {...$menu}
212
- use:$menu.action
213
- transition:fly={{ duration: 150, y: -5 }}
214
- >
215
- <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
216
- <div class="flex max-h-full flex-col gap-0 overflow-y-auto bg-base-100 py-2" tabindex="0">
217
- {#each filteredItems as item, index (index)}
218
- <li
219
- {...$option(toOption(item))}
220
- use:$option.action
221
- class="relative flex cursor-pointer scroll-my-2 items-center rounded-md px-1
222
- py-2
223
- data-[disabled]:opacity-50
224
- data-[highlighted]:bg-primary
225
- data-[highlighted]:text-primary-content"
226
- >
227
- {#if isChecked($localSelected, item)}
228
- <Icon data={LibIcon_Check} class="w-6"></Icon>
229
- {:else}
230
- <!-- just to book the place -->
231
- <span class="w-6"></span>
232
- {/if}
233
- {#if item.icon?.data}
234
- <Icon
235
- data={item.icon.data}
236
- class={tw(['flex-shrink-0', item.icon.class])}
237
- style={item.icon.style}
238
- size={item.icon.size}
239
- ></Icon>
240
- {/if}
241
- <div class="pl-2 {item.class}">
242
- <span class="font-medium">{item.caption}</span>
243
- </div>
244
- </li>
245
- {:else}
246
- <li class="relative cursor-pointer rounded-md py-1 pl-8 pr-4">Aucun résultat</li>
247
- {/each}
248
- </div>
249
- {#if totalCount}
250
- <div class="z-50 bg-base-300 text-center text-xs">
251
- {#if items.length < totalCount}
252
- ({items.length} / {totalCount})
253
- {:else}
254
- <!-- yes, items.length can be bigger if the selected item is not in the limit -->
255
- ({items.length})
256
- {/if}
257
- </div>
258
- {/if}
259
- </ul>
260
- {/if}
@@ -1,32 +0,0 @@
1
- import { type BaseItem } from '../../../internals';
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: Props & {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
14
- }
15
- declare const MultiSelectMelt: $$__sveltets_2_IsomorphicComponent<{
16
- id: string;
17
- disabled?: boolean;
18
- placeholder?: string;
19
- items?: BaseItem[];
20
- loadOptions?: ((str: string) => Promise<{
21
- items: BaseItem[];
22
- totalCount: number;
23
- }>) | undefined;
24
- values?: (string | null)[] | undefined;
25
- clearable?: boolean;
26
- }, {
27
- selected: CustomEvent<any>;
28
- } & {
29
- [evt: string]: CustomEvent<any>;
30
- }, {}, {}, string>;
31
- type MultiSelectMelt = InstanceType<typeof MultiSelectMelt>;
32
- export default MultiSelectMelt;
@@ -1,88 +0,0 @@
1
- <script lang="ts">
2
- import Svelecte from 'svelecte'
3
-
4
- import { LibIcon_Search, type BaseItem } from '../../../internals'
5
- import Icon from '../../Icon.svelte'
6
-
7
- interface Props {
8
- value?: string | undefined
9
- clearable?: boolean
10
- items?: BaseItem[] | undefined
11
- placeholder?: string | undefined
12
-
13
- onChange?: (value: string | undefined) => void
14
-
15
- multiple?: boolean
16
- }
17
-
18
- let {
19
- value = $bindable(undefined),
20
- clearable = false,
21
- items = [],
22
- placeholder = '',
23
- multiple = false,
24
- onChange,
25
- }: Props = $props()
26
- </script>
27
-
28
- <Svelecte
29
- i18n={{
30
- nomatch: 'Aucun résultat',
31
- }}
32
- options={items}
33
- bind:value
34
- {clearable}
35
- {placeholder}
36
- {multiple}
37
- {onChange}
38
- >
39
- {#snippet prepend()}
40
- {value}
41
- <Icon data={LibIcon_Search} class="mr-2 ml-3"></Icon>
42
- {/snippet}
43
-
44
- {#snippet option(opt, inputValue)}
45
- {@const item = opt as BaseItem}
46
- <div class="flex items-center">
47
- <Icon data={item.icon?.data} class="mr-2"></Icon>
48
- {item.caption}
49
- </div>
50
- {/snippet}
51
- </Svelecte>
52
-
53
- <style>
54
- :global(.svelecte) {
55
- --sv-min-height: 3rem;
56
- --sv-bg: var(--color-base-100, #fff);
57
- --sv-disabled-bg: #eee;
58
- --sv-border: 1px solid #414a54;
59
- --sv-border-radius: var(--radius-field, 4px);
60
- --sv-general-padding: 4px;
61
- --sv-control-bg: var(--sv-bg);
62
- --sv-item-wrap-padding: 3px 3px 3px 6px;
63
- --sv-item-selected-bg: var(--color-base-200, #efefef);
64
- --sv-item-btn-color: var(--color-base-content, #000);
65
- --sv-item-btn-color-hover: #777; /* same as icon-color-hover in default theme */
66
- --sv-item-btn-bg: var(--color-base-100, #efefef);
67
- --sv-item-btn-bg-hover: var(--color-base-200, #ddd);
68
- --sv-icon-color: var(--color-base-content, #efefef);
69
- --sv-icon-color-hover: #777;
70
- --sv-icon-bg: transparent;
71
- --sv-icon-size: 20px;
72
- --sv-separator-bg: var(--color-neutral-content, #ccc);
73
- --sv-btn-border: 0;
74
- --sv-placeholder-color: #ccccd6;
75
- --sv-dropdown-bg: var(--sv-bg);
76
- --sv-dropdown-offset: 1px;
77
- --sv-dropdown-border: 1px solid rgba(0, 0, 0, 0.15);
78
- --sv-dropdown-width: auto;
79
- --sv-dropdown-shadow: 0 6px 12px #0000002d;
80
- --sv-dropdown-height: 320px;
81
- --sv-dropdown-active-bg: var(--color-primary, #f2f5f8);
82
- --sv-dropdown-selected-bg: var(--color-neutral);
83
- --sv-create-kbd-border: var(--border, 1px) solid var(--color-base-200, #efefef);
84
- --sv-create-kbd-bg: var(--color-base-100, #fff);
85
- --sv-create-disabled-bg: var(--color-error, #fcbaba);
86
- --sv-loader-border: var(--border, 2px) solid var(--color-base-200, #ccc);
87
- }
88
- </style>
@@ -1,12 +0,0 @@
1
- import { type BaseItem } from '../../../internals';
2
- interface Props {
3
- value?: string | undefined;
4
- clearable?: boolean;
5
- items?: BaseItem[] | undefined;
6
- placeholder?: string | undefined;
7
- onChange?: (value: string | undefined) => void;
8
- multiple?: boolean;
9
- }
10
- declare const Select2: import("svelte").Component<Props, {}, "value">;
11
- type Select2 = ReturnType<typeof Select2>;
12
- export default Select2;
@@ -1,289 +0,0 @@
1
- <script lang="ts">
2
- import { createCombobox, createSync, type ComboboxOptionProps } from '@melt-ui/svelte'
3
- import { createEventDispatcher, onMount, tick } from 'svelte'
4
- import { fly } from 'svelte/transition'
5
-
6
- import {
7
- Button,
8
- LibIcon_Add,
9
- LibIcon_Check,
10
- LibIcon_Cross,
11
- LibIcon_Search,
12
- tw,
13
- type BaseItem,
14
- type FF_Icon,
15
- } from '../../../internals'
16
- import Icon from '../../Icon.svelte'
17
-
18
- export let id: string
19
- export let disabled: boolean = false
20
- export let placeholder: string = ''
21
- export let items: BaseItem[] = []
22
- let totalCount: number | undefined = undefined
23
-
24
- export let focus: boolean = false
25
- const focusNow = (node: any) => {
26
- if (focus) {
27
- tick().then(() => {
28
- node.focus()
29
- })
30
- }
31
- }
32
-
33
- export let loadOptions:
34
- | ((str: string) => Promise<{ items: BaseItem[]; totalCount: number }>)
35
- | undefined = undefined
36
- export let value: string | null | undefined = undefined
37
- export let clearable = false
38
- export let createOptionWhenNoResult = false
39
- export let default_select_if_one_item = false
40
- export let createRequest: ((args: { input: string; id: string }) => void) | undefined = undefined
41
-
42
- const dispatch = createEventDispatcher()
43
-
44
- function dispatchSelectedValue(_data: BaseItem | undefined) {
45
- dispatch('selected', _data)
46
- }
47
-
48
- function dispatchIssue(msg: 'VALUE_NOT_IN_ITEMS') {
49
- dispatch('issue', msg)
50
- }
51
-
52
- let lastSearch: string | undefined = undefined
53
- const localLoadOptions = async (str: string) => {
54
- if (str === lastSearch) {
55
- return
56
- }
57
- lastSearch = str
58
- if (loadOptions) {
59
- const lo = await loadOptions(str)
60
- items = lo.items
61
- totalCount = lo.totalCount
62
- filteredItems = items
63
- }
64
- }
65
-
66
- onMount(async () => {
67
- localLoadOptions('')
68
-
69
- // after we load items
70
- sync.selected(getDefaultValue(value))
71
- })
72
-
73
- const getDefaultValue = (_selectedValue: string | null | undefined) => {
74
- if (!items) {
75
- return
76
- }
77
- const found = items.find((c) => String(c?.id) === String(_selectedValue))
78
- if (found) {
79
- return toOption(found)
80
- } else {
81
- if (value !== null && value !== undefined && items.length > 0) {
82
- dispatchIssue('VALUE_NOT_IN_ITEMS')
83
- }
84
- }
85
- }
86
-
87
- const toOption = (
88
- item: BaseItem,
89
- ): ComboboxOptionProps<BaseItem> & {
90
- icon?: FF_Icon
91
- } => ({
92
- value: item,
93
- label: item.caption,
94
- // icon: item.icon,
95
- // disabled: item.disabled,
96
- })
97
-
98
- const {
99
- elements: { menu, input, option },
100
- states: { open, inputValue, touchedInput, selected: localSelected },
101
- // helpers: { isSelected },
102
- } = createCombobox<BaseItem>({
103
- forceVisible: true,
104
- disabled,
105
- ids: { label: id },
106
- })
107
-
108
- const clearSelection = () => {
109
- sync.selected(undefined)
110
- }
111
-
112
- let debounceTimer: ReturnType<typeof setTimeout>
113
- const debounce = (callback: () => void) => {
114
- clearTimeout(debounceTimer)
115
- debounceTimer = setTimeout(
116
- callback,
117
- // debounce only if we have a load option
118
- loadOptions ? 444 : 0,
119
- )
120
- }
121
-
122
- const sync = createSync({ selected: localSelected })
123
- $: items &&
124
- sync.selected(getDefaultValue(value), (v) => {
125
- // Only if different
126
- if (v?.value?.id !== value) {
127
- dispatchSelectedValue(v?.value)
128
- }
129
- value = v?.value?.id
130
- })
131
-
132
- // Helper to strip HTML tags from caption for display in input
133
- const stripHtml = (html: string): string => {
134
- const tmp = document.createElement('div')
135
- tmp.innerHTML = html
136
- return tmp.textContent || tmp.innerText || ''
137
- }
138
-
139
- $: if (!$open) {
140
- $inputValue = $localSelected?.label ? stripHtml($localSelected.label) : ''
141
- }
142
-
143
- $: filteredItems = items
144
-
145
- $: {
146
- if (items.length === 1 && default_select_if_one_item) {
147
- sync.selected(toOption(items[0]))
148
- }
149
- }
150
-
151
- const calcFilteredItems = (touched: boolean, str: string, value: any) => {
152
- if (touched) {
153
- debounce(async () => {
154
- const normalizedInput = str.toLowerCase()
155
- updateFilteredItems(normalizedInput)
156
- })
157
- } else {
158
- updateFilteredItems('')
159
- }
160
- }
161
-
162
- const updateFilteredItems = async (normalizedInput: string) => {
163
- if (loadOptions) {
164
- await localLoadOptions(normalizedInput)
165
- } else {
166
- filteredItems = items.filter((item) => {
167
- return item.caption?.toLowerCase().includes(normalizedInput)
168
- })
169
- }
170
- }
171
-
172
- $: calcFilteredItems($touchedInput, $inputValue, value)
173
- </script>
174
-
175
- <div class="input flex w-full min-w-0 items-center {disabled && 'opacity-40'}">
176
- <div class="relative">
177
- {#if $localSelected?.value?.icon?.data}
178
- <Icon
179
- data={$localSelected.value.icon.data}
180
- class={tw(['relative', $localSelected.value.icon.class])}
181
- style={$localSelected.value.icon.style}
182
- size={$localSelected.value.icon.size}
183
- ></Icon>
184
- {:else}
185
- <Icon data={LibIcon_Search} class="relative"></Icon>
186
- {/if}
187
- </div>
188
- <!-- {id} -->
189
- <input
190
- {...$input}
191
- use:$input.action
192
- class="-mr-5 -ml-8 h-full min-w-0 flex-grow bg-transparent px-10"
193
- {placeholder}
194
- use:focusNow
195
- />
196
- <div class="pointer-events-none relative right-0 flex gap-2">
197
- {#if clearable && $localSelected}
198
- <button on:click={clearSelection} class="pointer-events-auto">
199
- <Icon data={LibIcon_Cross}></Icon>
200
- </button>
201
- {/if}
202
- <!-- {#if $open}
203
- <Icon data={LibIcon_ChevronUp}></Icon>
204
- {:else}
205
- <Icon data={LibIcon_ChevronDown}></Icon>
206
- {/if} -->
207
- </div>
208
- </div>
209
-
210
- {#if $open}
211
- <ul
212
- class="z-50 flex max-h-[300px] flex-col overflow-hidden rounded-lg border border-base-content/20"
213
- {...$menu}
214
- use:$menu.action
215
- transition:fly={{ duration: 150, y: -5 }}
216
- >
217
- <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
218
- <div class="flex max-h-full flex-col gap-0 overflow-y-auto bg-base-100 py-2" tabindex="0">
219
- {#each filteredItems as item, index (index)}
220
- <li
221
- {...$option(toOption(item))}
222
- use:$option.action
223
- class="relative flex cursor-pointer scroll-my-2 items-start rounded-md px-1
224
- py-2
225
- data-[disabled]:opacity-50
226
- data-[highlighted]:bg-primary
227
- data-[highlighted]:text-primary-content"
228
- >
229
- <div class="flex items-center">
230
- {#if $localSelected?.value?.id === item.id}
231
- <Icon data={LibIcon_Check} class="w-6"></Icon>
232
- {:else}
233
- <!-- just to book the place -->
234
- <span class="w-6"></span>
235
- {/if}
236
- {#if item.icon?.data}
237
- <Icon
238
- data={item.icon.data}
239
- class={tw(['flex-shrink-0', item.icon.class])}
240
- style={item.icon.style}
241
- size={item.icon.size}
242
- ></Icon>
243
- {/if}
244
- </div>
245
- <div class="pl-2">
246
- <span class="font-medium">{@html item.caption}</span>
247
- </div>
248
- </li>
249
- {:else}
250
- {#if createOptionWhenNoResult}
251
- {#if $inputValue}
252
- <div class="p-4">
253
- <Button
254
- class="w-full"
255
- onclick={async () => {
256
- createRequest?.({ input: $inputValue, id })
257
- $open = false
258
- }}
259
- >
260
- <div class="flex items-center gap-2">
261
- <Icon data={LibIcon_Add}></Icon>
262
- {#if $inputValue}
263
- Créer "{$inputValue}"
264
- {/if}
265
- </div>
266
- </Button>
267
- </div>
268
- {:else}
269
- <li class="relative cursor-pointer rounded-md py-1 pl-8 pr-4 text-sm">
270
- Il faut tenter de rechercher avant de créer un nouvel élément
271
- </li>
272
- {/if}
273
- {:else}
274
- <li class="relative cursor-pointer rounded-md py-1 pl-8 pr-4">Aucun résultat</li>
275
- {/if}
276
- {/each}
277
- </div>
278
- {#if totalCount}
279
- <div class="z-50 bg-base-300 text-center text-xs">
280
- {#if items.length < totalCount}
281
- ({items.length} / {totalCount})
282
- {:else}
283
- <!-- yes, items.length can be bigger if the selected item is not in the limit -->
284
- ({items.length})
285
- {/if}
286
- </div>
287
- {/if}
288
- </ul>
289
- {/if}