@tekkare/romulus 0.1.121 → 0.1.123

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
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.16.0"
6
6
  },
7
- "version": "0.1.120",
7
+ "version": "0.1.122",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
@@ -278,7 +278,7 @@ function applyPreset(preset: RmComplexSelectPreset) {
278
278
 
279
279
  let unregister: (() => void) | undefined
280
280
  onMounted(() => {
281
- unregister = rmPanelRegister(close)
281
+ unregister = rmPanelRegister(close, () => isOpen.value)
282
282
  })
283
283
 
284
284
  onUnmounted(() => {
@@ -193,7 +193,7 @@ function onKeydown(event: KeyboardEvent) {
193
193
 
194
194
  let unregister: (() => void) | undefined
195
195
  onMounted(() => {
196
- unregister = rmPanelRegister(close)
196
+ unregister = rmPanelRegister(close, () => isOpen.value)
197
197
  })
198
198
 
199
199
  onUnmounted(() => {
@@ -274,5 +274,5 @@ onUnmounted(() => {
274
274
  </template>
275
275
 
276
276
  <style scoped>
277
- .rm-dsel{align-items:stretch;display:flex;flex-direction:column;gap:var(--space-4);position:relative;width:196px}.rm-dsel__label{color:var(--text-secondary);font-size:var(--font-12,12px);line-height:14px}.rm-dsel__field{align-items:center;background:var(--bg-surface);border:1.5px solid var(--border-default);border-radius:var(--radius-md,8px);box-sizing:border-box;color:var(--text-primary);cursor:pointer;display:flex;font-family:inherit;font-size:var(--font-14,14px);font-weight:700;gap:var(--space-4);line-height:16px;padding:var(--space-12);text-align:left;width:100%}.rm-dsel__field:hover:not(.is-disabled){border-color:rgba(var(--brand-rgb),.3)}.rm-dsel__field.is-open{border-color:var(--color-primary)}.rm-dsel__field.is-disabled{background:var(--bg-surface-2);color:var(--text-muted);cursor:not-allowed}.rm-dsel__field:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.rm-dsel__value-slot{display:block;flex:1;min-width:0}.rm-dsel__value{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rm-dsel__value.is-empty{color:var(--text-muted);font-style:italic;font-weight:400}.rm-dsel__field :deep(svg){color:var(--text-secondary);flex-shrink:0}.rm-dsel__panel--up{bottom:100%;margin-bottom:8px;margin-top:0;top:auto}.rm-dsel__panel{border:1.5px solid var(--border-default);border-radius:var(--radius-lg,12px);box-shadow:var(--r-shadow-lg,0 8px 24px rgba(0,0,0,.12));box-sizing:border-box;flex-direction:column;left:0;margin-top:8px;max-height:500px;max-width:420px;min-width:max(230px,100%);padding:var(--space-16);position:absolute;top:100%;z-index:12}.rm-dsel__panel,.rm-dsel__search{background:var(--bg-surface);display:flex}.rm-dsel__search{align-items:center;border:1px solid var(--border-default);border-radius:var(--radius-md,8px);gap:var(--space-8);padding:var(--space-8) var(--space-12)}.rm-dsel__search :deep(svg){color:var(--text-muted);flex-shrink:0}.rm-dsel__search-input{background:none;border:none;color:var(--text-primary);font-family:inherit;font-size:var(--font-14,14px);width:100%}.rm-dsel__search-input::-moz-placeholder{color:var(--text-muted);font-size:var(--font-12,12px)}.rm-dsel__search-input::placeholder{color:var(--text-muted);font-size:var(--font-12,12px)}.rm-dsel__search-input:focus-visible{outline:none}.rm-dsel__loading{display:flex;flex-direction:column;gap:var(--space-10);padding-top:var(--space-8)}.rm-dsel__sr{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;white-space:nowrap}.rm-dsel__state{color:var(--text-secondary);font-size:var(--font-14,14px);font-style:italic;padding-top:var(--space-16);text-align:center}.rm-dsel__list{display:flex;flex-direction:column;list-style:none;margin:0;min-height:10px;overflow-y:auto;padding:0}.rm-dsel__list:not(:only-child){margin-top:16px}.rm-dsel__option{align-items:center;border-radius:var(--radius-sm,4px);cursor:pointer;display:flex;font-size:var(--font-14,14px);gap:var(--space-8);line-height:160%;margin:4px 0;min-height:24px;padding:var(--space-2) var(--space-4)}.rm-dsel__option :deep(svg){color:var(--text-muted);flex-shrink:0}.rm-dsel__option.is-active{background:rgba(var(--brand-rgb),.05)}.rm-dsel__option.is-selected{font-weight:700}.rm-dsel__option.is-active :deep(svg),.rm-dsel__option.is-selected :deep(svg){color:var(--color-primary)}
277
+ .rm-dsel{align-items:stretch;display:flex;flex-direction:column;gap:var(--space-4);position:relative;width:196px}.rm-dsel__label{color:var(--text-secondary);font-size:var(--font-12,12px);line-height:14px}.rm-dsel__field{align-items:center;background:var(--bg-surface);border:1.5px solid var(--border-default);border-radius:var(--radius-md,8px);box-sizing:border-box;color:var(--text-primary);cursor:pointer;display:flex;font-family:inherit;font-size:var(--font-14,14px);font-weight:700;gap:var(--space-4);line-height:16px;padding:var(--space-12);text-align:left;width:100%}.rm-dsel__field:hover:not(.is-disabled){border-color:rgba(var(--brand-rgb),.3)}.rm-dsel__field.is-open{border-color:var(--color-primary)}.rm-dsel__field.is-disabled{background:var(--bg-surface-2);color:var(--text-muted);cursor:not-allowed}.rm-dsel__field:focus-visible{outline:2px solid var(--color-primary);outline-offset:2px}.rm-dsel__value-slot{display:block;flex:1;min-width:0}.rm-dsel__value{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rm-dsel__value.is-empty{color:var(--text-muted);font-style:italic;font-weight:400}.rm-dsel__field :deep(svg){color:var(--text-secondary);flex-shrink:0}.rm-dsel__panel{background:var(--bg-surface);border:1.5px solid var(--border-default);border-radius:var(--radius-lg,12px);box-shadow:var(--r-shadow-lg,0 8px 24px rgba(0,0,0,.12));box-sizing:border-box;display:flex;flex-direction:column;left:0;margin-top:8px;max-height:500px;max-width:420px;min-width:max(230px,100%);padding:var(--space-16);position:absolute;top:100%;z-index:12}.rm-dsel__panel--up{bottom:100%;margin-bottom:8px;margin-top:0;top:auto}.rm-dsel__search{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-md,8px);display:flex;gap:var(--space-8);padding:var(--space-8) var(--space-12)}.rm-dsel__search :deep(svg){color:var(--text-muted);flex-shrink:0}.rm-dsel__search-input{background:none;border:none;color:var(--text-primary);font-family:inherit;font-size:var(--font-14,14px);width:100%}.rm-dsel__search-input::-moz-placeholder{color:var(--text-muted);font-size:var(--font-12,12px)}.rm-dsel__search-input::placeholder{color:var(--text-muted);font-size:var(--font-12,12px)}.rm-dsel__search-input:focus-visible{outline:none}.rm-dsel__loading{display:flex;flex-direction:column;gap:var(--space-10);padding-top:var(--space-8)}.rm-dsel__sr{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;white-space:nowrap}.rm-dsel__state{color:var(--text-secondary);font-size:var(--font-14,14px);font-style:italic;padding-top:var(--space-16);text-align:center}.rm-dsel__list{display:flex;flex-direction:column;list-style:none;margin:0;min-height:10px;overflow-y:auto;padding:0}.rm-dsel__list:not(:only-child){margin-top:16px}.rm-dsel__option{align-items:center;border-radius:var(--radius-sm,4px);cursor:pointer;display:flex;font-size:var(--font-14,14px);gap:var(--space-8);line-height:160%;margin:4px 0;min-height:24px;padding:var(--space-2) var(--space-4)}.rm-dsel__option :deep(svg){color:var(--text-muted);flex-shrink:0}.rm-dsel__option.is-active{background:rgba(var(--brand-rgb),.05)}.rm-dsel__option.is-selected{font-weight:700}.rm-dsel__option.is-active :deep(svg),.rm-dsel__option.is-selected :deep(svg){color:var(--color-primary)}
278
278
  </style>
@@ -302,7 +302,7 @@ function onKeydown(e: KeyboardEvent) {
302
302
 
303
303
  let unregister: (() => void) | undefined
304
304
  onMounted(() => {
305
- unregister = rmPanelRegister(close)
305
+ unregister = rmPanelRegister(close, () => isOpen.value)
306
306
  document.addEventListener('click', onClickOutside)
307
307
  document.addEventListener('keydown', onKeydown)
308
308
  })
@@ -159,7 +159,7 @@ function onClickOutside(e: Event) {
159
159
 
160
160
  let unregister: (() => void) | undefined
161
161
  onMounted(() => {
162
- unregister = rmPanelRegister(close)
162
+ unregister = rmPanelRegister(close, () => isOpen.value)
163
163
  document.addEventListener('click', onClickOutside)
164
164
  })
165
165
  onUnmounted(() => {
@@ -2,6 +2,7 @@
2
2
  import { computed, ref, toRef } from 'vue'
3
3
  import { PhX } from '@phosphor-icons/vue'
4
4
  import { useRmDialog } from '../composables/useRmDialog'
5
+ import { rmPanelsDismiss } from '../utils/panels'
5
6
 
6
7
  export interface RmModalProps {
7
8
  /** Whether the modal is open */
@@ -37,6 +38,18 @@ function close() {
37
38
  emit('update:modelValue', false)
38
39
  }
39
40
 
41
+ /**
42
+ * Un panneau ouvert prend le clic avant le voile.
43
+ *
44
+ * Une liste deployee dans le formulaire deborde souvent de la boite : le clic
45
+ * qui vise a la refermer tombe sur le voile, et la modale partait avec, tout
46
+ * le formulaire avec elle.
47
+ */
48
+ function onOverlayClick() {
49
+ if (rmPanelsDismiss()) return
50
+ close()
51
+ }
52
+
40
53
  const surface = ref<HTMLElement | null>(null)
41
54
  useRmDialog({ open: toRef(props, 'modelValue'), surface, close })
42
55
 
@@ -62,7 +75,7 @@ const modalStyle = computed(() => {
62
75
  <div
63
76
  v-if="modelValue"
64
77
  class="rm-modal-overlay"
65
- @click.self="close"
78
+ @click.self="onOverlayClick"
66
79
  >
67
80
  <div
68
81
  ref="surface"
@@ -161,7 +161,7 @@ function toggleAll() {
161
161
 
162
162
  let unregister: (() => void) | undefined
163
163
  onMounted(() => {
164
- unregister = rmPanelRegister(close)
164
+ unregister = rmPanelRegister(close, () => isOpen.value)
165
165
  })
166
166
 
167
167
  onUnmounted(() => {
@@ -1,4 +1,5 @@
1
1
  import { nextTick, onBeforeUnmount, watch } from "vue";
2
+ import { rmPanelsDismiss } from "../utils/panels.js";
2
3
  const FOCUSABLE = [
3
4
  "a[href]",
4
5
  "button:not([disabled])",
@@ -15,6 +16,7 @@ export function useRmDialog({ open, surface, close }) {
15
16
  function onKeydown(event) {
16
17
  if (event.key === "Escape") {
17
18
  event.stopPropagation();
19
+ if (rmPanelsDismiss()) return;
18
20
  close();
19
21
  return;
20
22
  }
@@ -2,7 +2,22 @@
2
2
  * Inscrit un panneau. A appeler au montage, cote client seulement : sur le
3
3
  * serveur le registre est partage par toutes les requetes et personne ne le
4
4
  * vide.
5
+ *
6
+ * `isOpen` est facultatif mais vivement conseille : sans lui, le panneau ne
7
+ * protege pas la surface qui le porte du clic qui le referme.
5
8
  */
6
- export declare function rmPanelRegister(close: () => void): () => boolean;
9
+ export declare function rmPanelRegister(close: () => void, isOpen?: () => boolean): () => boolean;
7
10
  /** Ferme tous les panneaux sauf celui qui vient de s'ouvrir. */
8
11
  export declare function rmPanelOpened(self: () => void): void;
12
+ /**
13
+ * Referme les panneaux deployes, et dit s'il y en avait.
14
+ *
15
+ * Une modale se ferme au clic sur son voile et a `Escape`. Un panneau pose
16
+ * dans son formulaire deborde souvent de la boite : le clic qui vise a le
17
+ * refermer tombe alors sur le voile, et l'utilisateur perd tout son
18
+ * formulaire pour avoir voulu replier une liste. Meme geste au clavier.
19
+ *
20
+ * La surface appelle donc ceci d'abord : si un panneau etait ouvert, le geste
21
+ * lui revient et s'arrete la.
22
+ */
23
+ export declare function rmPanelsDismiss(): boolean;
@@ -1,10 +1,16 @@
1
- const closers = /* @__PURE__ */ new Set();
2
- export function rmPanelRegister(close) {
3
- closers.add(close);
4
- return () => closers.delete(close);
1
+ const panels = /* @__PURE__ */ new Set();
2
+ export function rmPanelRegister(close, isOpen) {
3
+ const panel = { close, isOpen };
4
+ panels.add(panel);
5
+ return () => panels.delete(panel);
5
6
  }
6
7
  export function rmPanelOpened(self) {
7
- closers.forEach((close) => {
8
- if (close !== self) close();
8
+ panels.forEach((panel) => {
9
+ if (panel.close !== self) panel.close();
9
10
  });
10
11
  }
12
+ export function rmPanelsDismiss() {
13
+ const open = [...panels].filter((panel) => panel.isOpen?.());
14
+ open.forEach((panel) => panel.close());
15
+ return open.length > 0;
16
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekkare/romulus",
3
- "version": "0.1.121",
3
+ "version": "0.1.123",
4
4
  "description": "Romulus Design System - Nuxt module by Tekkare",
5
5
  "license": "MIT",
6
6
  "type": "module",