@tekkare/romulus 0.1.223 → 0.1.224
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/module.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
3
|
-
import { PhCaretDown, PhCheck, PhMagnifyingGlass } from '@phosphor-icons/vue'
|
|
3
|
+
import { PhCaretDown, PhCheck, PhMagnifyingGlass, PhPlus } from '@phosphor-icons/vue'
|
|
4
4
|
import type { Component } from 'vue'
|
|
5
5
|
import { rmPanelOpened, rmPanelRegister } from '../utils/panels'
|
|
6
6
|
|
|
@@ -37,8 +37,16 @@ const props = withDefaults(defineProps<{
|
|
|
37
37
|
searchPlaceholder?: string
|
|
38
38
|
/** Langue des textes propres au composant (tout selectionner, aucun resultat). */
|
|
39
39
|
lang?: 'fr' | 'en'
|
|
40
|
+
/**
|
|
41
|
+
* La saisie de recherche peut devenir une valeur nouvelle (des tags) : une
|
|
42
|
+
* ligne « Creer « … » » apparait quand aucune option ne porte exactement ce
|
|
43
|
+
* libelle. La valeur est ajoutee a la selection et `create` previent
|
|
44
|
+
* l'appelant, qui l'ajoute a ses options. Implique la recherche.
|
|
45
|
+
*/
|
|
46
|
+
creatable?: boolean
|
|
40
47
|
}>(), {
|
|
41
48
|
icon: undefined,
|
|
49
|
+
creatable: false,
|
|
42
50
|
multiple: true,
|
|
43
51
|
showSelectAll: false,
|
|
44
52
|
searchable: 'auto',
|
|
@@ -47,8 +55,10 @@ const props = withDefaults(defineProps<{
|
|
|
47
55
|
})
|
|
48
56
|
|
|
49
57
|
const texts = computed(() => props.lang === 'en'
|
|
50
|
-
? { selectAll: 'Select all', deselectAll: 'Deselect all', noMatch: 'No match' }
|
|
51
|
-
: { selectAll: 'Tout sélectionner', deselectAll: 'Tout désélectionner', noMatch: 'Aucun résultat' })
|
|
58
|
+
? { selectAll: 'Select all', deselectAll: 'Deselect all', noMatch: 'No match', create: (q: string) => `Create « ${q} »` }
|
|
59
|
+
: { selectAll: 'Tout sélectionner', deselectAll: 'Tout désélectionner', noMatch: 'Aucun résultat', create: (q: string) => `Créer « ${q} »` })
|
|
60
|
+
|
|
61
|
+
const emit = defineEmits<{ create: [value: string] }>()
|
|
52
62
|
|
|
53
63
|
const selected = defineModel<string[]>({ default: () => [] })
|
|
54
64
|
|
|
@@ -80,9 +90,27 @@ const displayLabel = computed(() => {
|
|
|
80
90
|
})
|
|
81
91
|
|
|
82
92
|
const hasSearch = computed(() =>
|
|
83
|
-
props.searchable === 'auto' ? props.options.length > SEARCH_THRESHOLD : props.searchable,
|
|
93
|
+
props.creatable || (props.searchable === 'auto' ? props.options.length > SEARCH_THRESHOLD : props.searchable),
|
|
84
94
|
)
|
|
85
95
|
|
|
96
|
+
/** La saisie, quand elle peut devenir une option nouvelle. */
|
|
97
|
+
const creatableValue = computed(() => {
|
|
98
|
+
const q = search.value.trim()
|
|
99
|
+
if (!props.creatable || !q) return ''
|
|
100
|
+
const exists = props.options.some(o => o.label.toLowerCase() === q.toLowerCase())
|
|
101
|
+
return exists ? '' : q
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
function createOption() {
|
|
105
|
+
const value = creatableValue.value
|
|
106
|
+
if (!value) return
|
|
107
|
+
hasUserChanged.value = true
|
|
108
|
+
emit('create', value)
|
|
109
|
+
selected.value = props.multiple ? [...selected.value, value] : [value]
|
|
110
|
+
search.value = ''
|
|
111
|
+
if (!props.multiple) isOpen.value = false
|
|
112
|
+
}
|
|
113
|
+
|
|
86
114
|
const filteredOptions = computed(() => {
|
|
87
115
|
if (!hasSearch.value || !search.value.trim()) return props.options
|
|
88
116
|
const needle = search.value.trim().toLowerCase()
|
|
@@ -204,6 +232,7 @@ onUnmounted(() => {
|
|
|
204
232
|
type="search"
|
|
205
233
|
:placeholder="searchPlaceholder"
|
|
206
234
|
@click.stop
|
|
235
|
+
@keydown.enter.prevent="createOption"
|
|
207
236
|
/>
|
|
208
237
|
</label>
|
|
209
238
|
<button
|
|
@@ -217,7 +246,16 @@ onUnmounted(() => {
|
|
|
217
246
|
<span>{{ allSelected ? texts.deselectAll : texts.selectAll }}</span>
|
|
218
247
|
</button>
|
|
219
248
|
<div :class="{ 'filter-chip__options-group': showSelectAll && multiple && options.length > 0 }">
|
|
220
|
-
<
|
|
249
|
+
<button
|
|
250
|
+
v-if="creatableValue"
|
|
251
|
+
type="button"
|
|
252
|
+
class="filter-chip__option filter-chip__create"
|
|
253
|
+
@click.stop="createOption"
|
|
254
|
+
>
|
|
255
|
+
<PhPlus :size="12" weight="bold" />
|
|
256
|
+
<span>{{ texts.create(creatableValue) }}</span>
|
|
257
|
+
</button>
|
|
258
|
+
<p v-if="hasSearch && filteredOptions.length === 0 && !creatableValue" class="filter-chip__no-match">
|
|
221
259
|
{{ texts.noMatch }}
|
|
222
260
|
</p>
|
|
223
261
|
<template v-for="row in rows" :key="row.key">
|
|
@@ -248,5 +286,5 @@ onUnmounted(() => {
|
|
|
248
286
|
</template>
|
|
249
287
|
|
|
250
288
|
<style scoped>
|
|
251
|
-
.filter-chip-wrapper{position:relative}.filter-chip{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;box-sizing:border-box;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);font-weight:var(--weight-medium);gap:var(--space-8);height:32px;padding:var(--space-8);transition:all var(--duration-fast) var(--ease-in-out);white-space:nowrap;width:193px}.filter-chip:hover{border-color:var(--border-default)}.filter-chip__icon{color:var(--color-healthcare);flex-shrink:0}.filter-chip--active{border-color:var(--color-healthcare);box-shadow:0 0 8px 0 rgba(var(--healthcare-rgb),.1)}.filter-chip__label{flex:1;min-width:0;overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap}.filter-chip__badge{align-items:center;background-color:var(--color-healthcare);border-radius:100px;color:var(--color-text-inverse);display:inline-flex;flex-shrink:0;font-size:var(--font-11);font-weight:600;height:18px;justify-content:center;line-height:1;min-width:18px;padding:0 5px}.filter-chip__caret{color:var(--text-muted);flex-shrink:0;transition:transform var(--duration-fast) var(--ease-in-out)}.filter-chip__caret--open{transform:rotate(180deg)}.filter-chip__dropdown{background-color:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;box-shadow:0 4px 12px rgba(var(--shadow-rgb),.08),0 2px 4px rgba(var(--shadow-rgb),.04);left:0;max-height:300px;max-width:420px;min-width:380px;overflow-y:auto;padding:var(--space-4);position:absolute;top:calc(100% + 4px);width:-moz-max-content;width:max-content;z-index:50}.filter-chip__search{align-items:center;background-color:var(--bg-surface);border-bottom:1px solid var(--border-soft);color:var(--text-muted);display:flex;gap:var(--space-6);margin:-4px -4px 4px;padding:var(--space-8);position:sticky;top:-4px;z-index:1}.filter-chip__search input{background:none;border:none;color:var(--text-primary);flex:1;font-family:var(--font-sans);font-size:var(--font-14);min-width:0;outline:none}.filter-chip__search input::-webkit-search-cancel-button{cursor:pointer}.filter-chip__no-match{font-size:var(--font-14);margin:0;padding:var(--space-10) var(--space-8)}.filter-chip__no-match,.filter-chip__section{color:var(--text-muted);font-family:var(--font-sans)}.filter-chip__section{border-top:1px solid var(--border-default);font-size:var(--font-12);font-weight:var(--weight-bold);letter-spacing:.04em;margin:8px 0 2px;padding:var(--space-8) var(--space-8) 0;text-transform:uppercase}.filter-chip__section:first-child{border-top:none;margin-top:0;padding-top:0}.filter-chip__option{align-items:center;background:none;border:none;border-radius:4px;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);gap:var(--space-8);padding:var(--space-6) var(--space-8);text-align:left;transition:background-color var(--duration-fast) var(--ease-in-out);width:100%}.filter-chip__option:hover{background-color:var(--bg-surface-2)}.filter-chip__option--selected{color:var(--text-primary);font-weight:var(--weight-medium)}.filter-chip__checkbox{align-items:center;border:1.5px solid var(--border-default);border-radius:3px;display:flex;flex-shrink:0;height:16px;justify-content:center;transition:all var(--duration-fast) var(--ease-in-out);width:16px}.filter-chip__checkbox--checked{background-color:var(--color-healthcare);border-color:var(--color-healthcare);color:var(--color-text-inverse)}.filter-chip__radio{align-items:center;border:1.5px solid var(--border-default);border-radius:100%;display:flex;flex-shrink:0;height:16px;justify-content:center;transition:all var(--duration-fast) var(--ease-in-out);width:16px}.filter-chip__radio--checked{border-color:var(--color-healthcare)}.filter-chip__radio--checked:after{background-color:var(--color-healthcare);border-radius:100%;content:"";height:8px;width:8px}.filter-chip__select-all{align-items:center;background:none;border:none;border-radius:4px;color:var(--color-healthcare);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);font-weight:500;gap:var(--space-8);padding:var(--space-6) var(--space-8);text-align:left;width:100%}.filter-chip__select-all:hover{background-color:rgba(var(--healthcare-rgb),.04)}.filter-chip__options-group{border-left:1px solid var(--border-default);margin-left:14px;padding-left:var(--space-6)}.dropdown-enter-active,.dropdown-leave-active{transition:opacity var(--duration-fast) var(--ease-in-out),transform var(--duration-fast) var(--ease-in-out)}.dropdown-enter-from,.dropdown-leave-to{opacity:0;transform:translateY(-4px)}
|
|
289
|
+
.filter-chip-wrapper{position:relative}.filter-chip{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;box-sizing:border-box;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);font-weight:var(--weight-medium);gap:var(--space-8);height:32px;padding:var(--space-8);transition:all var(--duration-fast) var(--ease-in-out);white-space:nowrap;width:193px}.filter-chip:hover{border-color:var(--border-default)}.filter-chip__icon{color:var(--color-healthcare);flex-shrink:0}.filter-chip--active{border-color:var(--color-healthcare);box-shadow:0 0 8px 0 rgba(var(--healthcare-rgb),.1)}.filter-chip__label{flex:1;min-width:0;overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap}.filter-chip__badge{align-items:center;background-color:var(--color-healthcare);border-radius:100px;color:var(--color-text-inverse);display:inline-flex;flex-shrink:0;font-size:var(--font-11);font-weight:600;height:18px;justify-content:center;line-height:1;min-width:18px;padding:0 5px}.filter-chip__caret{color:var(--text-muted);flex-shrink:0;transition:transform var(--duration-fast) var(--ease-in-out)}.filter-chip__caret--open{transform:rotate(180deg)}.filter-chip__dropdown{background-color:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;box-shadow:0 4px 12px rgba(var(--shadow-rgb),.08),0 2px 4px rgba(var(--shadow-rgb),.04);left:0;max-height:300px;max-width:420px;min-width:380px;overflow-y:auto;padding:var(--space-4);position:absolute;top:calc(100% + 4px);width:-moz-max-content;width:max-content;z-index:50}.filter-chip__search{align-items:center;background-color:var(--bg-surface);border-bottom:1px solid var(--border-soft);color:var(--text-muted);display:flex;gap:var(--space-6);margin:-4px -4px 4px;padding:var(--space-8);position:sticky;top:-4px;z-index:1}.filter-chip__search input{background:none;border:none;color:var(--text-primary);flex:1;font-family:var(--font-sans);font-size:var(--font-14);min-width:0;outline:none}.filter-chip__search input::-webkit-search-cancel-button{cursor:pointer}.filter-chip__no-match{font-size:var(--font-14);margin:0;padding:var(--space-10) var(--space-8)}.filter-chip__no-match,.filter-chip__section{color:var(--text-muted);font-family:var(--font-sans)}.filter-chip__section{border-top:1px solid var(--border-default);font-size:var(--font-12);font-weight:var(--weight-bold);letter-spacing:.04em;margin:8px 0 2px;padding:var(--space-8) var(--space-8) 0;text-transform:uppercase}.filter-chip__section:first-child{border-top:none;margin-top:0;padding-top:0}.filter-chip__create{color:var(--color-primary);font-weight:var(--weight-medium)}.filter-chip__option{align-items:center;background:none;border:none;border-radius:4px;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);gap:var(--space-8);padding:var(--space-6) var(--space-8);text-align:left;transition:background-color var(--duration-fast) var(--ease-in-out);width:100%}.filter-chip__option:hover{background-color:var(--bg-surface-2)}.filter-chip__option--selected{color:var(--text-primary);font-weight:var(--weight-medium)}.filter-chip__checkbox{align-items:center;border:1.5px solid var(--border-default);border-radius:3px;display:flex;flex-shrink:0;height:16px;justify-content:center;transition:all var(--duration-fast) var(--ease-in-out);width:16px}.filter-chip__checkbox--checked{background-color:var(--color-healthcare);border-color:var(--color-healthcare);color:var(--color-text-inverse)}.filter-chip__radio{align-items:center;border:1.5px solid var(--border-default);border-radius:100%;display:flex;flex-shrink:0;height:16px;justify-content:center;transition:all var(--duration-fast) var(--ease-in-out);width:16px}.filter-chip__radio--checked{border-color:var(--color-healthcare)}.filter-chip__radio--checked:after{background-color:var(--color-healthcare);border-radius:100%;content:"";height:8px;width:8px}.filter-chip__select-all{align-items:center;background:none;border:none;border-radius:4px;color:var(--color-healthcare);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);font-weight:500;gap:var(--space-8);padding:var(--space-6) var(--space-8);text-align:left;width:100%}.filter-chip__select-all:hover{background-color:rgba(var(--healthcare-rgb),.04)}.filter-chip__options-group{border-left:1px solid var(--border-default);margin-left:14px;padding-left:var(--space-6)}.dropdown-enter-active,.dropdown-leave-active{transition:opacity var(--duration-fast) var(--ease-in-out),transform var(--duration-fast) var(--ease-in-out)}.dropdown-enter-from,.dropdown-leave-to{opacity:0;transform:translateY(-4px)}
|
|
252
290
|
</style>
|