@tekkare/romulus 0.1.139 → 0.1.141
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
|
@@ -67,5 +67,5 @@ withDefaults(defineProps<{
|
|
|
67
67
|
</template>
|
|
68
68
|
|
|
69
69
|
<style scoped>
|
|
70
|
-
.rm-product-card{align-self:stretch;background-color:var(--bg-surface);background-position:50%;background-repeat:no-repeat;background-size:cover;border-radius:var(--radius-xl,12px);box-shadow:0 6px 8px 0 rgba(var(--shadow-rgb,30 41 59),.1);display:flex;flex-direction:column;position:relative}.rm-product-card--padded{padding:var(--space-40)}.rm-product-card--healthcare{border:1px solid rgba(var(--healthcare-rgb),.1)}.rm-product-card--discovery{border:1px solid rgba(var(--discovery-rgb),.1)}.rm-product-card--databank{border:1px solid rgba(var(--databank-rgb),.1)}.rm-product-card--pharma{border:1px solid rgba(var(--pharma-rgb),.1)}.rm-product-card__inner{align-items:flex-start;align-self:stretch;display:flex;flex-direction:column;gap:var(--space-24)}.rm-product-card__inner--overlay{backdrop-filter:blur(5px);background:rgba(var(--surface-rgb),.7);border-radius:var(--radius-md,8px);gap:var(--space-16);margin:24px;padding:var(--space-24)
|
|
70
|
+
.rm-product-card{align-self:stretch;background-color:var(--bg-surface);background-position:50%;background-repeat:no-repeat;background-size:cover;border-radius:var(--radius-xl,12px);box-shadow:0 6px 8px 0 rgba(var(--shadow-rgb,30 41 59),.1);display:flex;flex-direction:column;position:relative}.rm-product-card--padded{padding:var(--space-40)}.rm-product-card--healthcare{border:1px solid rgba(var(--healthcare-rgb),.1)}.rm-product-card--discovery{border:1px solid rgba(var(--discovery-rgb),.1)}.rm-product-card--databank{border:1px solid rgba(var(--databank-rgb),.1)}.rm-product-card--pharma{border:1px solid rgba(var(--pharma-rgb),.1)}.rm-product-card__inner{align-items:flex-start;align-self:stretch;display:flex;flex-direction:column;gap:var(--space-24)}.rm-product-card__inner--overlay{backdrop-filter:blur(5px);background:rgba(var(--surface-rgb),.7);border-radius:var(--radius-md,8px);gap:var(--space-16);margin:24px;padding:var(--space-24)}.rm-product-card__title{font-size:26px;font-weight:900;line-height:normal}.rm-product-card--healthcare .rm-product-card__title{color:var(--color-primary)}.rm-product-card--discovery .rm-product-card__title{color:var(--color-discovery)}.rm-product-card--databank .rm-product-card__title{color:var(--color-databank)}.rm-product-card--pharma .rm-product-card__title{color:var(--color-pharma)}.rm-product-card__text{color:var(--text-primary);max-width:550px}.rm-product-card__actions{align-items:center;display:flex;gap:var(--space-16)}
|
|
71
71
|
</style>
|
|
@@ -68,6 +68,22 @@ export interface RmSavedView {
|
|
|
68
68
|
/** La vue s'applique a l'ouverture de la page. */
|
|
69
69
|
isDefault?: boolean
|
|
70
70
|
starred?: boolean
|
|
71
|
+
/**
|
|
72
|
+
* La vue vient de quelqu'un d'autre : son nom. Le lecteur ne peut alors
|
|
73
|
+
* qu'activer la vue ou la dupliquer, le contenu reste a son auteur ;
|
|
74
|
+
* l'etoile et la vue par defaut, elles, restent a sa main.
|
|
75
|
+
*/
|
|
76
|
+
sharedBy?: string
|
|
77
|
+
/** Ma vue, ouverte a d'autres. Le bandeau le rappelle a son auteur. */
|
|
78
|
+
shared?: boolean
|
|
79
|
+
/**
|
|
80
|
+
* Menu propre a cette vue, a la place de celui de la liste. Une liste ou
|
|
81
|
+
* cohabitent mes vues et celles des autres n'a pas un seul menu : le
|
|
82
|
+
* lecteur d'une vue partagee ne peut ni la renommer ni la supprimer.
|
|
83
|
+
*/
|
|
84
|
+
menu?: RmSavedViewMenuItem[]
|
|
85
|
+
/** Renommage sur place, a la place du reglage de la liste. */
|
|
86
|
+
renamable?: boolean
|
|
71
87
|
config?: RmSavedViewConfigRow[]
|
|
72
88
|
/** Pied de carte : auteur, date, compteurs. */
|
|
73
89
|
meta?: RmSavedViewMeta[]
|
|
@@ -99,6 +115,11 @@ const props = withDefaults(
|
|
|
99
115
|
{ menu: () => [], renamable: false, starrable: false, lang: 'fr' },
|
|
100
116
|
)
|
|
101
117
|
|
|
118
|
+
/** Menu de cette carte : celui de la vue s'il y en a un, sinon celui de la liste. */
|
|
119
|
+
const menuItems = computed(() => props.view.menu ?? props.menu)
|
|
120
|
+
/** Idem pour le renommage sur place. */
|
|
121
|
+
const canRename = computed(() => props.view.renamable ?? props.renamable)
|
|
122
|
+
|
|
102
123
|
const emit = defineEmits<{
|
|
103
124
|
activate: [id: string]
|
|
104
125
|
star: [id: string]
|
|
@@ -117,6 +138,9 @@ const T = computed(() => ({
|
|
|
117
138
|
isDefault: en.value
|
|
118
139
|
? 'Default view, applied on opening'
|
|
119
140
|
: "Vue par défaut, s'active à l'ouverture",
|
|
141
|
+
shared: en.value ? 'Shared' : 'Partagée',
|
|
142
|
+
sharedBy: (name: string) =>
|
|
143
|
+
en.value ? `Shared by ${name}` : `Partagée par ${name}`,
|
|
120
144
|
cancel: en.value ? 'Cancel' : 'Annuler',
|
|
121
145
|
confirmPrefix: en.value ? 'Permanently delete' : 'Supprimer définitivement',
|
|
122
146
|
}))
|
|
@@ -140,7 +164,7 @@ function pick(item: RmSavedViewMenuItem) {
|
|
|
140
164
|
pending.value = item
|
|
141
165
|
return
|
|
142
166
|
}
|
|
143
|
-
if (item.key === 'rename' &&
|
|
167
|
+
if (item.key === 'rename' && canRename.value) {
|
|
144
168
|
startRename()
|
|
145
169
|
return
|
|
146
170
|
}
|
|
@@ -220,9 +244,15 @@ function onCardClick() {
|
|
|
220
244
|
:class="{ 'rm-view--active': view.active, 'rm-view--default': view.isDefault }"
|
|
221
245
|
@click="onCardClick"
|
|
222
246
|
>
|
|
223
|
-
<div v-if="view.isDefault" class="rm-view__strip">
|
|
224
|
-
<
|
|
225
|
-
|
|
247
|
+
<div v-if="view.isDefault || view.sharedBy || view.shared" class="rm-view__strip">
|
|
248
|
+
<span v-if="view.isDefault" class="rm-view__strip-mark">
|
|
249
|
+
<RmIcon name="PushPin" :size="14" weight="fill" />
|
|
250
|
+
{{ T.isDefault }}
|
|
251
|
+
</span>
|
|
252
|
+
<span v-if="view.sharedBy || view.shared" class="rm-view__strip-mark">
|
|
253
|
+
<RmIcon name="UsersThree" :size="14" weight="fill" />
|
|
254
|
+
{{ view.sharedBy ? T.sharedBy(view.sharedBy) : T.shared }}
|
|
255
|
+
</span>
|
|
226
256
|
</div>
|
|
227
257
|
|
|
228
258
|
<div class="rm-view__head">
|
|
@@ -258,7 +288,7 @@ function onCardClick() {
|
|
|
258
288
|
@keydown.esc.prevent="renaming = false"
|
|
259
289
|
@blur="commitRename"
|
|
260
290
|
>
|
|
261
|
-
<span v-else class="rm-view__title" @dblclick.stop="
|
|
291
|
+
<span v-else class="rm-view__title" @dblclick.stop="canRename && startRename()">
|
|
262
292
|
{{ view.title }}
|
|
263
293
|
</span>
|
|
264
294
|
|
|
@@ -276,7 +306,7 @@ function onCardClick() {
|
|
|
276
306
|
{{ view.actionLabel || T.activate }}
|
|
277
307
|
</RmButton>
|
|
278
308
|
|
|
279
|
-
<div v-if="
|
|
309
|
+
<div v-if="menuItems.length" class="rm-view__menu-wrap">
|
|
280
310
|
<button
|
|
281
311
|
type="button"
|
|
282
312
|
class="rm-view__more"
|
|
@@ -294,7 +324,7 @@ function onCardClick() {
|
|
|
294
324
|
<div class="rm-view__menu-shade" @click.stop="menuOpen = false" />
|
|
295
325
|
<div class="rm-view__menu" role="menu">
|
|
296
326
|
<button
|
|
297
|
-
v-for="item in
|
|
327
|
+
v-for="item in menuItems"
|
|
298
328
|
:key="item.key"
|
|
299
329
|
type="button"
|
|
300
330
|
class="rm-view__menu-item"
|
|
@@ -366,5 +396,5 @@ function onCardClick() {
|
|
|
366
396
|
</template>
|
|
367
397
|
|
|
368
398
|
<style scoped>
|
|
369
|
-
.rm-view{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-lg);color:var(--text-primary);cursor:pointer;font-family:var(--font-sans);padding:14px 18px;transition:border-color var(--duration-fast) var(--ease-out),box-shadow var(--duration-fast) var(--ease-out),background var(--duration-fast) var(--ease-out)}.rm-view:not(.rm-view--active):hover{background:var(--bg-hover);border-color:var(--color-accent-soft)}.rm-view--active{border-color:var(--color-secondary);box-shadow:0 0 0 3px rgba(var(--brand-rgb),.08);cursor:default}.rm-view__strip{background:var(--bg-hover);border-bottom:1px solid var(--border-soft);border-radius:7px 7px 0 0;color:var(--color-secondary);font-size:var(--font-11);font-weight:var(--weight-bold);gap:var(--space-8);letter-spacing:.06em;margin:-14px -18px var(--space-12);padding:var(--space-8) var(--space-16);text-transform:uppercase}.rm-
|
|
399
|
+
.rm-view{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-lg);color:var(--text-primary);cursor:pointer;font-family:var(--font-sans);padding:14px 18px;transition:border-color var(--duration-fast) var(--ease-out),box-shadow var(--duration-fast) var(--ease-out),background var(--duration-fast) var(--ease-out)}.rm-view:not(.rm-view--active):hover{background:var(--bg-hover);border-color:var(--color-accent-soft)}.rm-view--active{border-color:var(--color-secondary);box-shadow:0 0 0 3px rgba(var(--brand-rgb),.08);cursor:default}.rm-view__strip{align-items:center;background:var(--bg-hover);border-bottom:1px solid var(--border-soft);border-radius:7px 7px 0 0;color:var(--color-secondary);display:flex;font-size:var(--font-11);font-weight:var(--weight-bold);gap:var(--space-8);letter-spacing:.06em;margin:-14px -18px var(--space-12);padding:var(--space-8) var(--space-16);text-transform:uppercase}.rm-view__strip-mark{align-items:center;display:inline-flex;gap:var(--space-6)}.rm-view__strip-mark+.rm-view__strip-mark:before{background:var(--border-soft);content:"";height:10px;margin-right:var(--space-8);width:1px}.rm-view__head{align-items:center;display:flex;gap:var(--space-10)}.rm-view__icon{align-items:center;border-radius:var(--radius-md);display:inline-flex;flex:0 0 auto;height:34px;justify-content:center;width:34px}.rm-view__icon--primary{background:var(--bg-hover);color:var(--color-secondary)}.rm-view__icon--success{background:var(--color-success-bg);color:var(--color-success)}.rm-view__icon--warning{background:var(--color-warning-bg);color:var(--color-warning)}.rm-view__icon--danger{background:var(--color-danger-bg);color:var(--color-danger)}.rm-view__icon--info{background:var(--color-info-bg);color:var(--color-info)}.rm-view__star{align-items:center;background:transparent;border:1px solid transparent;border-radius:var(--radius-md);color:var(--text-muted);cursor:pointer;display:inline-flex;flex:0 0 auto;height:28px;justify-content:center;padding:0;width:28px}.rm-view__star:hover{background:var(--bg-hover)}.rm-view__star--on,.rm-view__star:hover{color:var(--color-warning)}.rm-view__title,.rm-view__title-edit{flex:1;font-size:var(--font-16);font-weight:var(--weight-bold);min-width:0}.rm-view__title-edit{background:var(--bg-surface);border:1px solid var(--color-secondary);border-radius:var(--radius-md);color:var(--text-primary);font-family:inherit;outline:none;padding:var(--space-2) var(--space-8)}.rm-view__title-edit:focus{box-shadow:0 0 0 3px rgba(var(--brand-rgb),.12)}.rm-view__actions{align-items:center;display:flex;gap:var(--space-8)}.rm-view__badge{align-items:center;background:var(--color-success-bg);border-radius:var(--radius-pill);color:var(--color-success);display:inline-flex;font-size:var(--font-10);font-weight:var(--weight-bold);gap:var(--space-4);letter-spacing:.06em;padding:3px var(--space-8);text-transform:uppercase}.rm-view__menu-wrap{position:relative}.rm-view__more{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-muted);cursor:pointer;display:inline-flex;height:28px;justify-content:center;padding:0;width:28px}.rm-view__more:hover{background:var(--bg-hover);color:var(--text-primary)}.rm-view__menu-shade{inset:0;position:fixed;z-index:60}.rm-view__menu{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);display:flex;flex-direction:column;min-width:200px;padding:var(--space-4);position:absolute;right:0;top:calc(100% + 6px);z-index:61}.rm-view__menu-item{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-primary);cursor:pointer;display:flex;font-family:inherit;font-size:var(--font-13);gap:var(--space-8);padding:var(--space-8) var(--space-12);text-align:left;width:100%}.rm-view__menu-item:hover{background:var(--bg-hover);color:var(--color-secondary)}.rm-view__menu-item--danger{color:var(--color-danger)}.rm-view__menu-item--danger:hover{background:var(--color-danger-bg);color:var(--color-danger)}.rm-view__desc{color:var(--text-secondary);font-size:var(--font-13);margin:var(--space-8) 0 var(--space-12)}.rm-view__config{background:var(--bg-surface-2);border:1px solid var(--border-soft);border-radius:var(--radius-md);display:flex;flex-direction:column;gap:var(--space-6);margin:10px 0 var(--space-12);padding:var(--space-10) 14px}.rm-view__row{align-items:flex-start;display:flex;flex-wrap:wrap;font-size:var(--font-12);gap:14px}.rm-view__row-key{color:var(--text-muted);display:inline-flex;flex-shrink:0;font-size:var(--font-10);font-weight:var(--weight-semibold);letter-spacing:.04em;min-width:96px;padding-top:var(--space-4);text-transform:uppercase}.rm-view__row-key,.rm-view__row-val{align-items:center;gap:var(--space-6)}.rm-view__row-val{color:var(--text-primary);display:flex;flex:1 1 200px;flex-wrap:wrap;padding:var(--space-2) 0}.rm-view__pill{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-md);display:inline-flex;font-size:var(--font-12);gap:var(--space-4);padding:5px var(--space-10)}.rm-view__pill-k{color:var(--color-primary);font-weight:var(--weight-semibold)}.rm-view__pill-tip{min-width:0}.rm-view__pill-tip :deep(.rm-tooltip__bubble){max-width:320px}.rm-view__pill-v{display:-webkit-box;-webkit-box-orient:vertical;color:var(--text-primary);font-weight:var(--weight-medium);-webkit-line-clamp:3;overflow:hidden}.rm-view__foot{border-top:1px solid var(--border-soft);color:var(--text-muted);display:flex;flex-wrap:wrap;font-size:var(--font-11);gap:var(--space-16);padding-top:var(--space-10)}.rm-view__foot span{align-items:center;display:inline-flex;gap:var(--space-4)}.rm-view__confirm{align-items:center;background:var(--color-danger-bg);border:1px solid var(--color-danger);border-radius:var(--radius-md);color:var(--color-danger);display:flex;flex-wrap:wrap;font-size:var(--font-13);gap:var(--space-12);margin-top:var(--space-12);padding:var(--space-8) var(--space-12)}.rm-view__confirm>span{align-items:center;display:inline-flex;flex:1 1 220px;gap:var(--space-8)}.rm-view__confirm-actions{display:flex;flex-shrink:0;gap:var(--space-8)}
|
|
370
400
|
</style>
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmShareDialog - a qui une vue sauvegardee est ouverte.
|
|
3
|
+
|
|
4
|
+
Trois portees, exclusives : a personne, a des gens nommes, a tout l'espace
|
|
5
|
+
de travail. « Tout l'espace » n'est pas un raccourci pour « cocher tout le
|
|
6
|
+
monde » : c'est une regle, et elle vaut aussi pour les membres qui arrivent
|
|
7
|
+
apres. Deplier la case en une liste de noms au moment du partage laisserait
|
|
8
|
+
les arrivants dehors, sans que personne s'en apercoive.
|
|
9
|
+
|
|
10
|
+
La liste des noms reste montee quand la portee change : on revient de
|
|
11
|
+
« tout l'espace » a « des personnes » sans avoir reperdu sa selection.
|
|
12
|
+
|
|
13
|
+
Le dialogue ne parle pas a l'API. Il rend une portee et une liste d'ids ;
|
|
14
|
+
l'appelant enregistre. Le package ne sait pas ou vivent les vues.
|
|
15
|
+
-->
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
import { computed, ref, watch } from 'vue'
|
|
18
|
+
|
|
19
|
+
export interface RmShareMember {
|
|
20
|
+
userId: string
|
|
21
|
+
/** Nom affiche. A defaut, l'appelant y met l'adresse. */
|
|
22
|
+
name: string
|
|
23
|
+
email?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type RmShareScope = 'private' | 'people' | 'workspace'
|
|
27
|
+
|
|
28
|
+
const props = withDefaults(
|
|
29
|
+
defineProps<{
|
|
30
|
+
modelValue: boolean
|
|
31
|
+
/** Nom de la vue partagee, rappele dans l'en-tete. */
|
|
32
|
+
title?: string
|
|
33
|
+
/** Membres de l'espace, sans son proprietaire : l'appelant le retire. */
|
|
34
|
+
members?: RmShareMember[]
|
|
35
|
+
/** Portee enregistree. `people` se deduit d'une liste non vide. */
|
|
36
|
+
visibility?: 'private' | 'workspace'
|
|
37
|
+
/** Ids deja destinataires. */
|
|
38
|
+
sharedWith?: string[]
|
|
39
|
+
loading?: boolean
|
|
40
|
+
lang?: 'fr' | 'en'
|
|
41
|
+
}>(),
|
|
42
|
+
{
|
|
43
|
+
title: '',
|
|
44
|
+
members: () => [],
|
|
45
|
+
visibility: 'private',
|
|
46
|
+
sharedWith: () => [],
|
|
47
|
+
loading: false,
|
|
48
|
+
lang: 'fr',
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const emit = defineEmits<{
|
|
53
|
+
'update:modelValue': [value: boolean]
|
|
54
|
+
save: [value: { visibility: 'private' | 'workspace'; sharedWith: string[] }]
|
|
55
|
+
}>()
|
|
56
|
+
|
|
57
|
+
const en = computed(() => props.lang === 'en')
|
|
58
|
+
|
|
59
|
+
const T = computed(() => ({
|
|
60
|
+
heading: en.value ? 'Share the view' : 'Partager la vue',
|
|
61
|
+
private: en.value ? 'Only me' : 'Moi seul',
|
|
62
|
+
privateHint: en.value
|
|
63
|
+
? 'Nobody else sees this view.'
|
|
64
|
+
: 'Personne d\'autre ne voit cette vue.',
|
|
65
|
+
people: en.value ? 'Chosen people' : 'Des personnes choisies',
|
|
66
|
+
peopleHint: en.value
|
|
67
|
+
? 'They can apply the view and mark it, not change it.'
|
|
68
|
+
: 'Elles peuvent activer la vue et la marquer, pas la modifier.',
|
|
69
|
+
workspace: en.value ? 'The whole workspace' : 'Tout l\'espace de travail',
|
|
70
|
+
workspaceHint: en.value
|
|
71
|
+
? 'Members who join later see it too.'
|
|
72
|
+
: 'Les membres qui arrivent ensuite la voient aussi.',
|
|
73
|
+
search: en.value ? 'Search for a person' : 'Chercher une personne',
|
|
74
|
+
empty: en.value ? 'No member to share with.' : 'Aucun membre a qui partager.',
|
|
75
|
+
noMatch: en.value ? 'No match.' : 'Aucun resultat.',
|
|
76
|
+
count: (n: number) =>
|
|
77
|
+
en.value
|
|
78
|
+
? `${n} ${n > 1 ? 'people' : 'person'} selected`
|
|
79
|
+
: `${n} ${n > 1 ? 'personnes' : 'personne'} selectionnee${n > 1 ? 's' : ''}`,
|
|
80
|
+
cancel: en.value ? 'Cancel' : 'Annuler',
|
|
81
|
+
save: en.value ? 'Share' : 'Partager',
|
|
82
|
+
}))
|
|
83
|
+
|
|
84
|
+
const scope = ref<RmShareScope>('private')
|
|
85
|
+
const picked = ref<string[]>([])
|
|
86
|
+
const search = ref('')
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* L'etat ouvert fait foi : le dialogue se recharge a chaque ouverture, jamais
|
|
90
|
+
* pendant, sinon une reponse de l'API en cours de saisie reposerait les cases.
|
|
91
|
+
*/
|
|
92
|
+
watch(
|
|
93
|
+
() => props.modelValue,
|
|
94
|
+
(open) => {
|
|
95
|
+
if (!open) return
|
|
96
|
+
picked.value = [...props.sharedWith]
|
|
97
|
+
scope.value =
|
|
98
|
+
props.visibility === 'workspace'
|
|
99
|
+
? 'workspace'
|
|
100
|
+
: props.sharedWith.length
|
|
101
|
+
? 'people'
|
|
102
|
+
: 'private'
|
|
103
|
+
search.value = ''
|
|
104
|
+
},
|
|
105
|
+
{ immediate: true },
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
const matches = computed(() => {
|
|
109
|
+
const q = search.value.trim().toLowerCase()
|
|
110
|
+
if (!q) return props.members
|
|
111
|
+
return props.members.filter(
|
|
112
|
+
m =>
|
|
113
|
+
m.name.toLowerCase().includes(q) ||
|
|
114
|
+
(m.email ?? '').toLowerCase().includes(q),
|
|
115
|
+
)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
function toggle(userId: string) {
|
|
119
|
+
const next = new Set(picked.value)
|
|
120
|
+
if (next.has(userId)) next.delete(userId)
|
|
121
|
+
else next.add(userId)
|
|
122
|
+
picked.value = [...next]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function close() {
|
|
126
|
+
emit('update:modelValue', false)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Enregistrer efface ce que la portee retenue ne porte pas : passer a « moi
|
|
131
|
+
* seul » retire les destinataires, sinon ils resteraient en base a attendre
|
|
132
|
+
* le prochain partage sans que rien ne le dise a l'ecran.
|
|
133
|
+
*/
|
|
134
|
+
function save() {
|
|
135
|
+
emit('save', {
|
|
136
|
+
visibility: scope.value === 'workspace' ? 'workspace' : 'private',
|
|
137
|
+
sharedWith: scope.value === 'people' ? picked.value : [],
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const scopes = computed<Array<{ key: RmShareScope; icon: string; label: string; hint: string }>>(
|
|
142
|
+
() => [
|
|
143
|
+
{ key: 'private', icon: 'Lock', label: T.value.private, hint: T.value.privateHint },
|
|
144
|
+
{ key: 'people', icon: 'UserPlus', label: T.value.people, hint: T.value.peopleHint },
|
|
145
|
+
{
|
|
146
|
+
key: 'workspace',
|
|
147
|
+
icon: 'UsersThree',
|
|
148
|
+
label: T.value.workspace,
|
|
149
|
+
hint: T.value.workspaceHint,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
)
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<template>
|
|
156
|
+
<RmModal
|
|
157
|
+
:model-value="modelValue"
|
|
158
|
+
size="sm"
|
|
159
|
+
mode="free"
|
|
160
|
+
:label="T.heading"
|
|
161
|
+
@update:model-value="close"
|
|
162
|
+
>
|
|
163
|
+
<div class="rm-share">
|
|
164
|
+
<header class="rm-share__head">
|
|
165
|
+
<h2 class="rm-share__title">{{ T.heading }}</h2>
|
|
166
|
+
<p v-if="title" class="rm-share__subject">{{ title }}</p>
|
|
167
|
+
</header>
|
|
168
|
+
|
|
169
|
+
<!--
|
|
170
|
+
Trois choix exclusifs : des boutons radio natifs, pas trois boutons.
|
|
171
|
+
Le groupe s'annonce alors comme un choix, se parcourt aux fleches et
|
|
172
|
+
dit lequel est retenu, sans qu'on ait a le recoder.
|
|
173
|
+
-->
|
|
174
|
+
<ul class="rm-share__scopes">
|
|
175
|
+
<li v-for="item in scopes" :key="item.key">
|
|
176
|
+
<label class="rm-share__scope" :class="{ 'is-on': scope === item.key }">
|
|
177
|
+
<input
|
|
178
|
+
v-model="scope"
|
|
179
|
+
type="radio"
|
|
180
|
+
name="rm-share-scope"
|
|
181
|
+
class="rm-share__radio"
|
|
182
|
+
:value="item.key"
|
|
183
|
+
:aria-label="item.label"
|
|
184
|
+
>
|
|
185
|
+
<RmIcon :name="item.icon" :size="18" />
|
|
186
|
+
<span class="rm-share__scope-text">
|
|
187
|
+
<span class="rm-share__scope-label">{{ item.label }}</span>
|
|
188
|
+
<span class="rm-share__scope-hint">{{ item.hint }}</span>
|
|
189
|
+
</span>
|
|
190
|
+
<RmIcon
|
|
191
|
+
v-if="scope === item.key"
|
|
192
|
+
name="CheckCircle"
|
|
193
|
+
:size="16"
|
|
194
|
+
weight="fill"
|
|
195
|
+
class="rm-share__scope-mark"
|
|
196
|
+
/>
|
|
197
|
+
</label>
|
|
198
|
+
</li>
|
|
199
|
+
</ul>
|
|
200
|
+
|
|
201
|
+
<div v-show="scope === 'people'" class="rm-share__people">
|
|
202
|
+
<label class="rm-share__search">
|
|
203
|
+
<RmIcon name="MagnifyingGlass" :size="16" />
|
|
204
|
+
<input v-model="search" type="search" :placeholder="T.search" :aria-label="T.search">
|
|
205
|
+
</label>
|
|
206
|
+
|
|
207
|
+
<p v-if="!members.length" class="rm-share__empty">{{ T.empty }}</p>
|
|
208
|
+
<p v-else-if="!matches.length" class="rm-share__empty">{{ T.noMatch }}</p>
|
|
209
|
+
|
|
210
|
+
<ul v-else class="rm-share__list">
|
|
211
|
+
<li v-for="member in matches" :key="member.userId">
|
|
212
|
+
<label class="rm-share__member">
|
|
213
|
+
<input
|
|
214
|
+
type="checkbox"
|
|
215
|
+
class="rm-share__check"
|
|
216
|
+
:checked="picked.includes(member.userId)"
|
|
217
|
+
:aria-label="member.name"
|
|
218
|
+
@change="toggle(member.userId)"
|
|
219
|
+
>
|
|
220
|
+
<RmIcon
|
|
221
|
+
:name="picked.includes(member.userId) ? 'CheckSquare' : 'Square'"
|
|
222
|
+
:weight="picked.includes(member.userId) ? 'fill' : 'regular'"
|
|
223
|
+
:size="18"
|
|
224
|
+
/>
|
|
225
|
+
<span class="rm-share__member-text">
|
|
226
|
+
<span class="rm-share__member-name">{{ member.name }}</span>
|
|
227
|
+
<span v-if="member.email" class="rm-share__member-mail">{{ member.email }}</span>
|
|
228
|
+
</span>
|
|
229
|
+
</label>
|
|
230
|
+
</li>
|
|
231
|
+
</ul>
|
|
232
|
+
|
|
233
|
+
<p v-if="picked.length" class="rm-share__count">{{ T.count(picked.length) }}</p>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
<footer class="rm-share__foot">
|
|
237
|
+
<RmButton variant="ghost" size="sm" @click="close">
|
|
238
|
+
{{ T.cancel }}
|
|
239
|
+
</RmButton>
|
|
240
|
+
<RmButton variant="primary" size="sm" :disabled="loading" @click="save">
|
|
241
|
+
{{ T.save }}
|
|
242
|
+
</RmButton>
|
|
243
|
+
</footer>
|
|
244
|
+
</div>
|
|
245
|
+
</RmModal>
|
|
246
|
+
</template>
|
|
247
|
+
|
|
248
|
+
<style scoped>
|
|
249
|
+
.rm-share{gap:var(--space-16);padding:var(--space-4)}.rm-share,.rm-share__head{display:flex;flex-direction:column}.rm-share__head{gap:var(--space-4)}.rm-share__title{color:var(--color-primary);font-size:var(--font-16);font-weight:var(--weight-bold)}.rm-share__subject{color:var(--color-secondary);font-size:var(--font-13)}.rm-share__scopes{display:flex;flex-direction:column;gap:var(--space-8);list-style:none}.rm-share__scope{align-items:flex-start;background:var(--bg-surface);border:1px solid var(--border-soft);border-radius:var(--radius-lg);color:var(--color-primary);cursor:pointer;display:flex;gap:var(--space-10);padding:var(--space-10) var(--space-12);text-align:left;width:100%}.rm-share__scope:hover{background:var(--bg-hover)}.rm-share__check,.rm-share__radio{border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);white-space:nowrap}.rm-share__scope:has(.rm-share__radio:focus-visible){outline:2px solid var(--color-secondary);outline-offset:2px}.rm-share__scope.is-on{background:var(--bg-hover);border-color:var(--color-secondary)}.rm-share__scope-text{display:flex;flex:1;flex-direction:column;gap:2px}.rm-share__scope-label{font-size:var(--font-13);font-weight:var(--weight-medium)}.rm-share__scope-hint{font-size:var(--font-12)}.rm-share__scope-hint,.rm-share__scope-mark{color:var(--color-secondary)}.rm-share__people{display:flex;flex-direction:column;gap:var(--space-8)}.rm-share__search{align-items:center;background:var(--bg-surface);border:1px solid var(--border-soft);border-radius:var(--radius-pill);color:var(--color-secondary);display:flex;gap:var(--space-8);padding:var(--space-6) var(--space-10)}.rm-share__search input{background:none;border:none;color:var(--color-primary);flex:1;font-size:var(--font-13);outline:none}.rm-share__list{display:flex;flex-direction:column;gap:2px;list-style:none;max-height:220px;overflow-y:auto}.rm-share__member{align-items:center;border-radius:var(--radius-md);cursor:pointer;display:flex;gap:var(--space-8);padding:var(--space-6) var(--space-8)}.rm-share__member:hover{background:var(--bg-hover)}.rm-share__member:has(.rm-share__check:focus-visible){outline:2px solid var(--color-secondary);outline-offset:2px}.rm-share__member-text{display:flex;flex-direction:column}.rm-share__member-name{color:var(--color-primary);font-size:var(--font-13)}.rm-share__member-mail{color:var(--color-secondary);font-size:var(--font-11)}.rm-share__count,.rm-share__empty{color:var(--color-secondary);font-size:var(--font-12)}.rm-share__foot{display:flex;gap:var(--space-8);justify-content:flex-end}
|
|
250
|
+
</style>
|