@tekkare/romulus 0.1.51 → 0.1.52

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.50",
7
+ "version": "0.1.51",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { defineNuxtModule, createResolver, addComponentsDir, addImportsDir } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addPlugin, addComponentsDir, addImportsDir } from '@nuxt/kit';
2
2
  import { fileURLToPath } from 'node:url';
3
3
 
4
4
  const module = defineNuxtModule({
@@ -25,6 +25,7 @@ const module = defineNuxtModule({
25
25
  nuxt.options.css.push(resolve(runtimeDir, "assets/styles/shell.css"));
26
26
  if (options.utilities)
27
27
  nuxt.options.css.push(resolve(runtimeDir, "assets/styles/utilities.css"));
28
+ addPlugin(resolve(runtimeDir, "plugins/a11yFilters.client"));
28
29
  await addComponentsDir({
29
30
  path: resolve(runtimeDir, "components"),
30
31
  prefix: options.prefix
@@ -1 +1 @@
1
- .grid-2{grid-template-columns:repeat(2,1fr)}.grid-2,.grid-3{display:grid;gap:20px}.grid-3{grid-template-columns:repeat(3,1fr)}.grid-4{display:grid;gap:16px;grid-template-columns:repeat(4,1fr)}.grid-2-1{display:grid;gap:20px;grid-template-columns:2fr 1fr}.pane-title{color:var(--text-primary);font-family:var(--font-serif);font-size:32px;font-weight:700;letter-spacing:-.02em;line-height:1.1;margin:0 0 16px}.pane-sub{color:var(--text-muted);font-size:13px;line-height:1.55;margin:0 0 24px;max-width:78ch}.chart-section{margin-inline:auto;max-width:var(--content-max-w,1440px);padding:0 0 40px;width:100%}.section-label{align-items:center;color:var(--text-secondary);display:flex;font-size:11px;font-weight:700;gap:8px;letter-spacing:.12em;margin:40px 0 18px;text-transform:uppercase}body.text-large .pane-title{font-size:38px}body.text-xl .pane-title{font-size:46px}body.text-large .section-label{font-size:13px}body.text-xl .section-label{font-size:14px}.chart-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:10px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;transition:box-shadow .18s,border-color .18s}.chart-card:hover{border-color:var(--color-violet-pale);box-shadow:0 6px 16px rgba(107,63,255,.1),0 1px 3px rgba(30,41,59,.04)}.chart-card-head{padding:22px 64px 14px 24px;position:relative}.chart-card-title{color:var(--text-primary);font-size:15px;font-weight:700;letter-spacing:-.01em;line-height:1.3;margin:0 0 5px}.chart-card-context{color:var(--text-muted);font-size:12px;line-height:1.55;margin:0}.chart-card-actions{position:absolute;right:12px;top:12px}.cc-export{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;color:var(--text-secondary);cursor:pointer;display:flex;font-size:14px;height:28px;justify-content:center;transition:background .15s,color .15s,border-color .15s;width:28px}.cc-export:hover{color:var(--color-secondary)}.cc-export:hover,.chart-card-actions.open .cc-export{background:var(--bg-hover);border-color:var(--color-violet-pale)}.chart-card-actions.open .cc-export{color:var(--color-primary)}.cc-menu{animation:cc-menu-in .12s cubic-bezier(.2,0,0,1);background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:var(--shadow-lg);display:none;min-width:200px;padding:6px 0;position:absolute;right:0;top:34px;z-index:var(--z-modal)}.chart-card-actions.open .cc-menu{display:block}.cc-menu-section{color:var(--text-muted);font-size:10px;font-weight:700;letter-spacing:.08em;padding:8px 14px 4px;text-transform:uppercase}.cc-menu-item{align-items:center;background:none;border:0;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:13px;gap:10px;padding:7px 14px;text-align:left;width:100%}.cc-menu-item i{color:var(--text-muted);font-size:14px}.cc-menu-item:hover{background:var(--bg-surface-3);color:var(--text-primary)}.cc-menu-item:hover i{color:var(--text-secondary)}.cc-menu-divider{background:var(--bg-surface-3);height:1px;margin:4px 0}@keyframes cc-menu-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.chart-card-body{flex:1;padding:4px 12px 8px}.chart-card-body>.rm-empty{box-sizing:border-box;gap:6px;justify-content:center;max-width:none;min-height:240px;padding:36px 24px}.chart-card-foot{align-items:center;background:var(--bg-surface-2);border-radius:0 0 10px 10px;border-top:1px solid var(--border-soft);color:var(--text-muted);display:flex;flex-wrap:wrap;font-size:11px;gap:10px;padding:12px 24px}.chart-card-foot .src{align-items:center;display:flex;gap:4px}.chart-card-foot .src b{color:var(--text-secondary);font-weight:600}.chart-card-foot .dot{background:var(--border-default);border-radius:50%;height:3px;width:3px}.chart-card-foot .updated{color:var(--text-secondary);font-weight:500}.kpi-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:0 2px 8px rgba(30,41,59,.06);padding:16px 18px}.kpi-label{color:var(--text-muted);font-size:11px;font-weight:600;letter-spacing:.1em;margin:0 0 6px;text-transform:uppercase}.kpi-value{color:var(--color-primary);font-family:var(--font-serif);font-size:28px;font-weight:700;line-height:1;margin:0}.kpi-value.healthcare{color:var(--color-healthcare)}.kpi-value.discovery{color:var(--color-discovery)}.kpi-value.pharma{color:var(--color-pharma)}.kpi-value.databank{color:var(--color-databank)}.kpi-trend{align-items:center;display:inline-flex;font-size:12px;font-weight:600;gap:4px;margin-top:6px}.kpi-trend i{font-size:11px}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-danger)}.kpi-foot,.kpi-trend--flat{color:var(--text-muted)}.kpi-foot{font-size:11px;margin-top:4px}.kpi-extra{margin-top:12px}.badge{border-radius:4px;display:inline-block;font-size:10px;font-weight:600;padding:2px 8px}.badge-active{background:var(--color-success-bg);color:var(--color-success)}.badge-pending{background:var(--color-warning-bg);color:var(--color-warning)}.badge-error{background:var(--color-danger-bg);color:var(--color-danger)}.badge-info{background:var(--bg-hover);color:var(--color-primary)}.badge-grey{background:var(--bg-surface-3);color:var(--text-secondary)}table.tk{border-collapse:collapse;font-size:13px;width:100%}.sr-only,table.tk>caption.sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;white-space:nowrap}table.tk caption{caption-side:top;color:var(--text-primary);font-size:var(--font-13);font-weight:var(--weight-bold);padding:0 0 var(--space-8);text-align:left}table.tk th{background:var(--bg-surface-2);border-bottom:1px solid var(--border-default);color:var(--text-secondary);font-size:11px;font-weight:700;letter-spacing:.08em;padding:12px 14px;text-align:left;text-transform:uppercase}table.tk tbody th[scope=row]{background:transparent;font-size:var(--font-13);font-weight:var(--weight-bold);letter-spacing:0;text-transform:none}table.tk tbody th[scope=row],table.tk td{border-bottom:1px solid var(--border-soft);color:var(--text-primary)}table.tk td{padding:12px 14px}table.tk tbody tr:nth-child(2n) td,table.tk tbody tr:nth-child(2n) th[scope=row]{background:var(--bg-surface-2)}table.tk tbody tr:hover td,table.tk tbody tr:hover th[scope=row]{background:var(--bg-hover)}table.tk th[aria-sort]{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}table.tk th[aria-sort]:hover{color:var(--text-primary)}.tk-wrap{overflow-x:auto}.tk-num{font-variant-numeric:tabular-nums;text-align:right}.tk-skeleton{height:14px;width:80%}.tk-empty{color:var(--text-muted);text-align:center}.nav-group{display:flex;flex-direction:column}.nav-group-header{align-items:center;background:none;border:0;color:var(--text-muted);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:11px;font-weight:700;gap:8px;letter-spacing:.08em;padding:8px 12px;text-align:left;text-transform:uppercase;transition:color var(--duration-fast) var(--ease-out);width:100%}.nav-group-header:hover{color:var(--text-secondary)}.nav-group-header .caret{display:inline-flex;margin-left:auto;transition:transform var(--duration-fast) var(--ease-out)}.nav-group[data-open=true]>.nav-group-header{color:var(--color-primary)}.nav-group[data-open=true]>.nav-group-header .caret{transform:rotate(180deg)}.nav-group-items{display:flex;flex-direction:column;gap:2px;padding:2px 0 8px}.cc-export:focus-visible,.cc-menu-item:focus-visible,.chart-card :focus-visible,.nav-group-header:focus-visible,table.tk th[aria-sort]:focus-visible{box-shadow:var(--shadow-focus);outline:2px solid var(--color-secondary);outline-offset:2px}.cc-menu-item,.nav-group-header{min-height:40px}body.text-large .kpi-label{font-size:13px}body.text-large .kpi-value{font-size:34px}body.text-large .kpi-trend{font-size:14px}body.text-large .kpi-foot{font-size:13px}body.text-large .chart-card-title{font-size:17px}body.text-large .chart-card-context{font-size:14px}body.text-large .chart-card-foot{font-size:12px}body.text-large .badge{font-size:12px;padding:3px 9px}body.text-large table.tk{font-size:15px}body.text-large table.tk th{font-size:13px}body.text-xl .kpi-label{font-size:14px}body.text-xl .kpi-value{font-size:40px}body.text-xl .kpi-trend{font-size:16px}body.text-xl .kpi-foot{font-size:14px}body.text-xl .chart-card-title{font-size:20px}body.text-xl .chart-card-context{font-size:16px}body.text-xl .chart-card-foot{font-size:14px}body.text-xl .badge{font-size:14px;padding:3px 10px}body.text-xl table.tk{font-size:16px}body.text-xl table.tk th{font-size:14px}body.high-contrast .cc-export,body.high-contrast .cc-menu,body.high-contrast .chart-card,body.high-contrast .kpi-card{border-width:2px}body.high-contrast .chart-card:hover{border-color:var(--border-default)}body.rm-switching,body.rm-switching *,body.rm-switching :after,body.rm-switching :before,body.theme-switching,body.theme-switching *,body.theme-switching :after,body.theme-switching :before{transition:none!important}body.high-contrast *{box-shadow:none!important}.rm-skeleton{animation:rm-skeleton-shimmer 1.5s ease-in-out infinite;background:linear-gradient(90deg,var(--bg-hover) 0,var(--bg-surface-2) 50%,var(--bg-hover) 100%);background-size:200% 100%;border-radius:var(--radius-md)}@keyframes rm-skeleton-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.rm-skeleton{animation:none;background:var(--bg-hover)}}.rm-state-loading{display:flex;flex-direction:column;gap:10px;justify-content:center;min-height:240px;padding:8px 4px}.rm-spinner{animation:rm-spinner-turn .8s linear infinite;border:3px solid var(--border-default);border-radius:50%;border-top-color:var(--color-secondary);height:32px;width:32px}@keyframes rm-spinner-turn{to{transform:rotate(1turn)}}@media (prefers-reduced-motion:reduce){.rm-spinner{animation-duration:3s}}.rm-state{color:var(--text-secondary);flex-direction:column;font-family:var(--font-sans);gap:6px;min-height:240px;padding:36px 24px;text-align:center}.rm-state,.rm-state__icon{align-items:center;display:flex;justify-content:center}.rm-state__icon{background:var(--bg-surface-3);border-radius:50%;color:var(--text-muted);height:56px;margin-bottom:6px;width:56px}.rm-state__title{color:var(--text-primary);font-size:var(--font-14);font-weight:var(--weight-semibold);margin:0}.rm-state__text{color:var(--text-muted);font-size:var(--font-12);line-height:1.5;margin:0;max-width:280px}.rm-state__action{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-md);color:var(--text-secondary);cursor:pointer;display:inline-flex;font-family:inherit;font-size:var(--font-12);font-weight:var(--weight-semibold);gap:6px;margin-top:10px;padding:6px 14px}.rm-state__action:hover{background:var(--bg-surface-3);color:var(--text-primary)}.rm-state--error .rm-state__icon{background:var(--color-danger-bg);color:var(--color-danger)}.rm-state--error .rm-state__action{border-color:var(--color-danger);color:var(--color-danger)}.rm-state--error .rm-state__action:hover{background:var(--color-danger-bg)}
1
+ .grid-2{grid-template-columns:repeat(2,1fr)}.grid-2,.grid-3{display:grid;gap:20px}.grid-3{grid-template-columns:repeat(3,1fr)}.grid-4{display:grid;gap:16px;grid-template-columns:repeat(4,1fr)}.grid-2-1{display:grid;gap:20px;grid-template-columns:2fr 1fr}.pane-title{color:var(--text-primary);font-family:var(--font-serif);font-size:32px;font-weight:700;letter-spacing:-.02em;line-height:1.1;margin:0 0 16px}.pane-sub{color:var(--text-muted);font-size:13px;line-height:1.55;margin:0 0 24px;max-width:78ch}.chart-section{margin-inline:auto;max-width:var(--content-max-w,1440px);padding:0 0 40px;width:100%}.section-label{align-items:center;color:var(--text-secondary);display:flex;font-size:11px;font-weight:700;gap:8px;letter-spacing:.12em;margin:40px 0 18px;text-transform:uppercase}body.text-large .pane-title{font-size:38px}body.text-xl .pane-title{font-size:46px}body.text-large .section-label{font-size:13px}body.text-xl .section-label{font-size:14px}.chart-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:10px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;transition:box-shadow .18s,border-color .18s}.chart-card:hover{border-color:var(--color-violet-pale);box-shadow:0 6px 16px rgba(107,63,255,.1),0 1px 3px rgba(30,41,59,.04)}.chart-card-head{padding:22px 64px 14px 24px;position:relative}.chart-card-title{color:var(--text-primary);font-size:15px;font-weight:700;letter-spacing:-.01em;line-height:1.3;margin:0 0 5px}.chart-card-context{color:var(--text-muted);font-size:12px;line-height:1.55;margin:0}.rm-card-actions{position:relative}.chart-card-head>.chart-card-actions{position:absolute;right:12px;top:12px}.cc-export{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;color:var(--text-secondary);cursor:pointer;display:flex;font-size:14px;height:28px;justify-content:center;transition:background .15s,color .15s,border-color .15s;width:28px}.cc-export:hover{color:var(--color-secondary)}.cc-export:hover,.rm-card-actions.open .cc-export{background:var(--bg-hover);border-color:var(--color-violet-pale)}.rm-card-actions.open .cc-export{color:var(--color-primary)}.cc-menu{animation:cc-menu-in .12s cubic-bezier(.2,0,0,1);background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:var(--shadow-lg);display:none;min-width:200px;padding:6px 0;position:absolute;right:0;top:34px;z-index:var(--z-modal)}.rm-card-actions.open .cc-menu{display:block}.cc-menu-section{color:var(--text-muted);font-size:10px;font-weight:700;letter-spacing:.08em;padding:8px 14px 4px;text-transform:uppercase}.cc-menu-item{align-items:center;background:none;border:0;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:13px;gap:10px;padding:7px 14px;text-align:left;width:100%}.cc-menu-item i{color:var(--text-muted);font-size:14px}.cc-menu-item:hover{background:var(--bg-surface-3);color:var(--text-primary)}.cc-menu-item:hover i{color:var(--text-secondary)}.cc-menu-divider{background:var(--bg-surface-3);height:1px;margin:4px 0}.cc-menu-note{border-top:1px solid var(--bg-surface-3);color:var(--text-secondary);font-size:12px;margin:4px 0 0;padding:8px 14px 4px}.cc-menu-item[disabled]{cursor:default;opacity:.55}.cc-menu-item[disabled]:hover{background:none;color:var(--text-secondary)}.chart-card:fullscreen,.rm-rcard:fullscreen,.rm-table-card:fullscreen{background:var(--bg-surface);border-radius:0;box-sizing:border-box;height:100vh;overflow:auto;padding:16px;width:100vw}.chart-card:fullscreen .chart-card-body{display:flex;flex-direction:column}.chart-card:fullscreen .rm-chart{flex:1 1 auto}.chart-card:fullscreen .echart{height:72vh!important}@keyframes cc-menu-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.chart-card-body{flex:1;padding:4px 12px 8px}.chart-card-body>.rm-empty{box-sizing:border-box;gap:6px;justify-content:center;max-width:none;min-height:240px;padding:36px 24px}.chart-card-foot{align-items:center;background:var(--bg-surface-2);border-radius:0 0 10px 10px;border-top:1px solid var(--border-soft);color:var(--text-muted);display:flex;flex-wrap:wrap;font-size:11px;gap:10px;padding:12px 24px}.chart-card-foot .src{align-items:center;display:flex;gap:4px}.chart-card-foot .src b{color:var(--text-secondary);font-weight:600}.chart-card-foot .dot{background:var(--border-default);border-radius:50%;height:3px;width:3px}.chart-card-foot .updated{color:var(--text-secondary);font-weight:500}.kpi-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:0 2px 8px rgba(30,41,59,.06);padding:16px 18px}.kpi-label{color:var(--text-muted);font-size:11px;font-weight:600;letter-spacing:.1em;margin:0 0 6px;text-transform:uppercase}.kpi-value{color:var(--color-primary);font-family:var(--font-serif);font-size:28px;font-weight:700;line-height:1;margin:0}.kpi-value.healthcare{color:var(--color-healthcare)}.kpi-value.discovery{color:var(--color-discovery)}.kpi-value.pharma{color:var(--color-pharma)}.kpi-value.databank{color:var(--color-databank)}.kpi-trend{align-items:center;display:inline-flex;font-size:12px;font-weight:600;gap:4px;margin-top:6px}.kpi-trend i{font-size:11px}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-danger)}.kpi-foot,.kpi-trend--flat{color:var(--text-muted)}.kpi-foot{font-size:11px;margin-top:4px}.kpi-extra{margin-top:12px}.badge{border-radius:4px;display:inline-block;font-size:10px;font-weight:600;padding:2px 8px}.badge-active{background:var(--color-success-bg);color:var(--color-success)}.badge-pending{background:var(--color-warning-bg);color:var(--color-warning)}.badge-error{background:var(--color-danger-bg);color:var(--color-danger)}.badge-info{background:var(--bg-hover);color:var(--color-primary)}.badge-grey{background:var(--bg-surface-3);color:var(--text-secondary)}table.tk{border-collapse:collapse;font-size:13px;width:100%}.sr-only,table.tk>caption.sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;white-space:nowrap}table.tk caption{caption-side:top;color:var(--text-primary);font-size:var(--font-13);font-weight:var(--weight-bold);padding:0 0 var(--space-8);text-align:left}table.tk th{background:var(--bg-surface-2);border-bottom:1px solid var(--border-default);color:var(--text-secondary);font-size:11px;font-weight:700;letter-spacing:.08em;padding:12px 14px;text-align:left;text-transform:uppercase}table.tk tbody th[scope=row]{background:transparent;font-size:var(--font-13);font-weight:var(--weight-bold);letter-spacing:0;text-transform:none}table.tk tbody th[scope=row],table.tk td{border-bottom:1px solid var(--border-soft);color:var(--text-primary)}table.tk td{padding:12px 14px}table.tk tbody tr:nth-child(2n) td,table.tk tbody tr:nth-child(2n) th[scope=row]{background:var(--bg-surface-2)}table.tk tbody tr:hover td,table.tk tbody tr:hover th[scope=row]{background:var(--bg-hover)}table.tk th[aria-sort]{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}table.tk th[aria-sort]:hover{color:var(--text-primary)}.tk-wrap{overflow-x:auto}.tk-num{font-variant-numeric:tabular-nums;text-align:right}.tk-skeleton{height:14px;width:80%}.tk-empty{color:var(--text-muted);text-align:center}.nav-group{display:flex;flex-direction:column}.nav-group-header{align-items:center;background:none;border:0;color:var(--text-muted);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:11px;font-weight:700;gap:8px;letter-spacing:.08em;padding:8px 12px;text-align:left;text-transform:uppercase;transition:color var(--duration-fast) var(--ease-out);width:100%}.nav-group-header:hover{color:var(--text-secondary)}.nav-group-header .caret{display:inline-flex;margin-left:auto;transition:transform var(--duration-fast) var(--ease-out)}.nav-group[data-open=true]>.nav-group-header{color:var(--color-primary)}.nav-group[data-open=true]>.nav-group-header .caret{transform:rotate(180deg)}.nav-group-items{display:flex;flex-direction:column;gap:2px;padding:2px 0 8px}.cc-export:focus-visible,.cc-menu-item:focus-visible,.chart-card :focus-visible,.nav-group-header:focus-visible,table.tk th[aria-sort]:focus-visible{box-shadow:var(--shadow-focus);outline:2px solid var(--color-secondary);outline-offset:2px}.cc-menu-item,.nav-group-header{min-height:40px}body.text-large .kpi-label{font-size:13px}body.text-large .kpi-value{font-size:34px}body.text-large .kpi-trend{font-size:14px}body.text-large .kpi-foot{font-size:13px}body.text-large .chart-card-title{font-size:17px}body.text-large .chart-card-context{font-size:14px}body.text-large .chart-card-foot{font-size:12px}body.text-large .badge{font-size:12px;padding:3px 9px}body.text-large table.tk{font-size:15px}body.text-large table.tk th{font-size:13px}body.text-xl .kpi-label{font-size:14px}body.text-xl .kpi-value{font-size:40px}body.text-xl .kpi-trend{font-size:16px}body.text-xl .kpi-foot{font-size:14px}body.text-xl .chart-card-title{font-size:20px}body.text-xl .chart-card-context{font-size:16px}body.text-xl .chart-card-foot{font-size:14px}body.text-xl .badge{font-size:14px;padding:3px 10px}body.text-xl table.tk{font-size:16px}body.text-xl table.tk th{font-size:14px}body.high-contrast .cc-export,body.high-contrast .cc-menu,body.high-contrast .chart-card,body.high-contrast .kpi-card{border-width:2px}body.high-contrast .chart-card:hover{border-color:var(--border-default)}body.rm-switching,body.rm-switching *,body.rm-switching :after,body.rm-switching :before,body.theme-switching,body.theme-switching *,body.theme-switching :after,body.theme-switching :before{transition:none!important}body.high-contrast *{box-shadow:none!important}.rm-skeleton{animation:rm-skeleton-shimmer 1.5s ease-in-out infinite;background:linear-gradient(90deg,var(--bg-hover) 0,var(--bg-surface-2) 50%,var(--bg-hover) 100%);background-size:200% 100%;border-radius:var(--radius-md)}@keyframes rm-skeleton-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.rm-skeleton{animation:none;background:var(--bg-hover)}}.rm-state-loading{display:flex;flex-direction:column;gap:10px;justify-content:center;min-height:240px;padding:8px 4px}.rm-spinner{animation:rm-spinner-turn .8s linear infinite;border:3px solid var(--border-default);border-radius:50%;border-top-color:var(--color-secondary);height:32px;width:32px}@keyframes rm-spinner-turn{to{transform:rotate(1turn)}}@media (prefers-reduced-motion:reduce){.rm-spinner{animation-duration:3s}}.rm-state{color:var(--text-secondary);flex-direction:column;font-family:var(--font-sans);gap:6px;min-height:240px;padding:36px 24px;text-align:center}.rm-state,.rm-state__icon{align-items:center;display:flex;justify-content:center}.rm-state__icon{background:var(--bg-surface-3);border-radius:50%;color:var(--text-muted);height:56px;margin-bottom:6px;width:56px}.rm-state__title{color:var(--text-primary);font-size:var(--font-14);font-weight:var(--weight-semibold);margin:0}.rm-state__text{color:var(--text-muted);font-size:var(--font-12);line-height:1.5;margin:0;max-width:280px}.rm-state__action{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-md);color:var(--text-secondary);cursor:pointer;display:inline-flex;font-family:inherit;font-size:var(--font-12);font-weight:var(--weight-semibold);gap:6px;margin-top:10px;padding:6px 14px}.rm-state__action:hover{background:var(--bg-surface-3);color:var(--text-primary)}.rm-state--error .rm-state__icon{background:var(--color-danger-bg);color:var(--color-danger)}.rm-state--error .rm-state__action{border-color:var(--color-danger);color:var(--color-danger)}.rm-state--error .rm-state__action:hover{background:var(--color-danger-bg)}
@@ -1 +1 @@
1
- :root{--sidebar-w:220px;--sidebar-collapsed-w:56px;--rsidebar-w:280px;--rsidebar-collapsed-w:76px;--topbar-h:56px;--topbar-merged-h:0px;--tabs-h:46px;--analysis-header-h:124px;--sourcebar-h:38px;--filterbar-h:56px;--color-primary:#2100ad;--color-primary-hover:#1a0088;--color-secondary:#6b3fff;--color-accent-soft:#b485ff;--color-violet-pale:#d6ccff;--color-healthcare:#4164ee;--color-discovery:#07816f;--color-pharma:#f7520e;--color-databank:#e04380;--color-healthcare-active:#3451d4;--color-discovery-active:#066b5c;--color-pharma-active:#d9470c;--color-databank-active:#c73a6f;--color-pharma-soft:#ff7942;--healthcare-rgb:65,100,238;--discovery-rgb:7,129,111;--pharma-rgb:247,82,14;--databank-rgb:224,67,128;--surface-rgb:255,255,255;--shadow-rgb:30,41,59;--color-accent-teal:#00d4a8;--color-notification:#e59;--color-text-inverse:#fff;--color-success:#16a34a;--color-success-bg:#dcfce7;--color-warning:#d97706;--color-warning-bg:#fef3c7;--color-danger:#dc2626;--color-danger-bg:#fee2e2;--color-info:#4164ee;--color-info-bg:#dbeafe;--bg-app:#eef1f5;--bg-surface:#fff;--bg-surface-2:#fbfbfd;--bg-surface-3:#f5f5f9;--bg-hover:#eef1fe;--border-default:#dbdef0;--border-soft:#f0f1f7;--text-primary:#1e293b;--text-secondary:#525670;--text-muted:#878ca6;--brand-rgb:33,0,173;--chart-cat-1:#2100ad;--chart-cat-2:#00a38e;--chart-cat-3:#e67e22;--chart-cat-4:#d63384;--chart-cat-5:#4164ee;--chart-cat-6:#16a34a;--space-2:2px;--space-4:4px;--space-6:6px;--space-8:8px;--space-12:12px;--space-16:16px;--space-20:20px;--space-24:24px;--space-32:32px;--space-40:40px;--space-48:48px;--radius-sm:4px;--radius-md:6px;--radius-lg:8px;--radius-xl:10px;--radius-2xl:12px;--radius-pill:999px;--font-10:10px;--font-11:11px;--font-12:12px;--font-13:13px;--font-14:14px;--font-16:16px;--font-18:18px;--font-22:22px;--font-24:24px;--font-32:32px;--weight-normal:400;--weight-medium:500;--weight-semibold:600;--weight-bold:700;--font-serif:"Caudex",Georgia,serif;--font-sans:"Figtree",system-ui,sans-serif;--font-mono:"SF Mono",Menlo,monospace;--shadow-xs:0 1px 2px rgba(30,41,59,.04);--shadow-sm:0 2px 6px rgba(30,41,59,.06),0 1px 2px rgba(30,41,59,.03);--shadow-md:0 4px 12px rgba(30,41,59,.06),0 1px 3px rgba(30,41,59,.04);--shadow-lg:0 8px 24px rgba(30,41,59,.12);--shadow-focus:0 0 0 3px rgba(107,63,255,.14);--duration-instant:100ms;--duration-fast:150ms;--duration-base:240ms;--duration-slow:400ms;--duration-slowest:600ms;--ease-out:cubic-bezier(.2,0,0,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--ease-spring:cubic-bezier(.5,1.6,.4,1);--z-base:1;--z-sticky:20;--z-header:30;--z-drawer:40;--z-modal:50;--z-toast:60;--z-help:70}body.night{--color-primary:#9b7cff;--color-secondary:#b485ff;--color-accent-soft:#d6ccff;--color-healthcare:#7aa0ff;--color-discovery:#2ecc71;--color-pharma:#ff8c00;--color-databank:#f472b6;--color-healthcare-active:#4164ee;--color-discovery-active:#07816f;--color-pharma-active:#f7520e;--color-databank-active:#e04380;--healthcare-rgb:122,160,255;--discovery-rgb:46,204,113;--pharma-rgb:255,140,0;--databank-rgb:244,114,182;--surface-rgb:26,26,36;--shadow-rgb:0,0,0;--bg-app:#0e0e14;--bg-surface:#1a1a24;--bg-surface-2:#232330;--bg-surface-3:#2a2a38;--bg-hover:#2d2a42;--border-default:#45455c;--border-soft:#32323f;--text-primary:#f5f6fa;--text-secondary:#cfd3e5;--text-muted:#8a90a8;--brand-rgb:155,124,255;--color-success:#6fe8c5;--color-success-bg:rgba(0,212,168,.18);--color-warning:#fbbf24;--color-warning-bg:rgba(245,158,11,.18);--color-danger:#fca5a5;--color-danger-bg:rgba(239,68,68,.18);--color-info:#7aa0ff;--color-info-bg:rgba(65,100,238,.18);--chart-cat-1:#9b7cff;--chart-cat-2:#6fe8c5;--chart-cat-3:#fbbf24;--chart-cat-4:#f472b6;--chart-cat-5:#7b9ef5;--chart-cat-6:#bef264;background:var(--bg-app);color:var(--text-primary)}body.high-contrast:not(.night){--text-primary:#000;--text-secondary:#1a1a24;--text-muted:#2f2f40;--border-default:#000;--border-soft:#525670;--bg-app:#fff;--bg-surface:#fff;--bg-surface-2:#fff;--bg-surface-3:#f0f1f7;--bg-hover:#ffe56b;--color-primary:#1100a0;--color-primary-hover:#0a0070;--color-secondary:#5028d8;--color-success:#006b2c;--color-warning:#8c4500;--color-danger:#a60000;--color-info:#0040c8}body.night.high-contrast{--text-primary:#fff;--text-secondary:#f5f6fa;--text-muted:#cfd3e5;--border-default:#fff;--border-soft:#b8bcd0;--bg-app:#000;--bg-surface:#000;--bg-surface-2:#000;--bg-surface-3:#000;--bg-hover:#ffe56b;--color-primary:#d6ccff;--color-primary-hover:#fff;--color-secondary:#e5daff;--color-accent-soft:#fff;--color-success:#6fe8c5;--color-warning:#fbbf24;--color-danger:#fca5a5;--color-info:#7aa0ff;--brand-rgb:214,204,255}body.reduce-motion *,body.reduce-motion :after,body.reduce-motion :before{animation:none!important;scroll-behavior:auto!important;transition:none!important}:root{--r-color-primary-50:#f0f4ff;--r-color-primary-100:#dce5fd;--r-color-primary-200:#b9cbfb;--r-color-primary-300:#96b0f9;--r-color-primary-400:#7395f5;--r-color-primary-500:#5a7ef2;--r-color-primary-600:var(--color-healthcare);--r-color-primary-700:#3451d4;--r-color-primary-800:#283fb0;--r-color-primary-900:#1c2d8a;--r-color-gray-50:#f9fafb;--r-color-gray-100:#f3f4f6;--r-color-gray-200:#e5e7eb;--r-color-gray-300:#d1d5db;--r-color-gray-400:#9ca3af;--r-color-gray-500:#6b7280;--r-color-gray-600:#4b5563;--r-color-gray-700:#374151;--r-color-gray-800:#1f2937;--r-color-gray-900:#111827;--r-color-gray-950:#030712;--r-color-success-500:var(--color-success);--r-color-success-600:#16a34a;--r-color-warning-500:#f59e0b;--r-color-warning-600:var(--color-warning);--r-color-danger-500:#ef4444;--r-color-danger-600:var(--color-danger);--r-color-white:#fff;--r-color-black:#000;--r-font-family:var(--font-sans);--r-font-mono:var(--font-mono);--r-text-xs:0.75rem;--r-text-sm:0.875rem;--r-text-base:1rem;--r-text-lg:1.125rem;--r-text-xl:1.25rem;--r-text-2xl:1.5rem;--r-text-3xl:1.875rem;--r-text-4xl:2.25rem;--r-font-weight-normal:var(--weight-normal);--r-font-weight-medium:var(--weight-medium);--r-font-weight-semibold:var(--weight-semibold);--r-font-weight-bold:var(--weight-bold);--r-leading-tight:1.25;--r-leading-normal:1.5;--r-leading-relaxed:1.75;--r-space-0:0;--r-space-1:var(--space-4);--r-space-2:var(--space-8);--r-space-3:var(--space-12);--r-space-4:var(--space-16);--r-space-5:var(--space-20);--r-space-6:var(--space-24);--r-space-8:var(--space-32);--r-space-10:var(--space-40);--r-space-12:var(--space-48);--r-radius-none:0;--r-radius-sm:var(--radius-sm);--r-radius-md:var(--radius-md);--r-radius-lg:var(--radius-lg);--r-radius-xl:0.75rem;--r-radius-2xl:1rem;--r-radius-full:var(--radius-pill);--r-shadow-xs:0 1px 2px 0 rgba(0,0,0,.05);--r-shadow-sm:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--r-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--r-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--r-shadow-xl:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--r-transition-fast:var(--duration-fast) var(--ease-in-out);--r-transition-base:200ms var(--ease-in-out);--r-transition-slow:var(--duration-slow) var(--ease-in-out)}
1
+ :root{--sidebar-w:220px;--sidebar-collapsed-w:56px;--rsidebar-w:280px;--rsidebar-collapsed-w:76px;--topbar-h:56px;--topbar-merged-h:0px;--tabs-h:46px;--analysis-header-h:124px;--sourcebar-h:38px;--filterbar-h:56px;--color-primary:#2100ad;--color-primary-hover:#1a0088;--color-secondary:#6b3fff;--color-accent-soft:#b485ff;--color-violet-pale:#d6ccff;--color-healthcare:#4164ee;--color-discovery:#07816f;--color-pharma:#f7520e;--color-databank:#e04380;--color-healthcare-active:#3451d4;--color-discovery-active:#066b5c;--color-pharma-active:#d9470c;--color-databank-active:#c73a6f;--color-pharma-soft:#ff7942;--healthcare-rgb:65,100,238;--discovery-rgb:7,129,111;--pharma-rgb:247,82,14;--databank-rgb:224,67,128;--surface-rgb:255,255,255;--shadow-rgb:30,41,59;--color-accent-teal:#00d4a8;--color-notification:#e59;--color-text-inverse:#fff;--color-success:#16a34a;--color-success-bg:#dcfce7;--color-warning:#d97706;--color-warning-bg:#fef3c7;--color-danger:#dc2626;--color-danger-bg:#fee2e2;--color-info:#4164ee;--color-info-bg:#dbeafe;--bg-app:#eef1f5;--bg-surface:#fff;--bg-surface-2:#fbfbfd;--bg-surface-3:#f5f5f9;--bg-hover:#eef1fe;--border-default:#dbdef0;--border-soft:#f0f1f7;--text-primary:#1e293b;--text-secondary:#525670;--text-muted:#878ca6;--brand-rgb:33,0,173;--chart-cat-1:#2100ad;--chart-cat-2:#00a38e;--chart-cat-3:#e67e22;--chart-cat-4:#d63384;--chart-cat-5:#4164ee;--chart-cat-6:#16a34a;--space-2:2px;--space-4:4px;--space-6:6px;--space-8:8px;--space-12:12px;--space-16:16px;--space-20:20px;--space-24:24px;--space-32:32px;--space-40:40px;--space-48:48px;--radius-sm:4px;--radius-md:6px;--radius-lg:8px;--radius-xl:10px;--radius-2xl:12px;--radius-pill:999px;--font-10:10px;--font-11:11px;--font-12:12px;--font-13:13px;--font-14:14px;--font-16:16px;--font-18:18px;--font-22:22px;--font-24:24px;--font-32:32px;--weight-normal:400;--weight-medium:500;--weight-semibold:600;--weight-bold:700;--font-serif:"Caudex",Georgia,serif;--font-sans:"Figtree",system-ui,sans-serif;--font-mono:"SF Mono",Menlo,monospace;--shadow-xs:0 1px 2px rgba(30,41,59,.04);--shadow-sm:0 2px 6px rgba(30,41,59,.06),0 1px 2px rgba(30,41,59,.03);--shadow-md:0 4px 12px rgba(30,41,59,.06),0 1px 3px rgba(30,41,59,.04);--shadow-lg:0 8px 24px rgba(30,41,59,.12);--shadow-focus:0 0 0 3px rgba(107,63,255,.14);--duration-instant:100ms;--duration-fast:150ms;--duration-base:240ms;--duration-slow:400ms;--duration-slowest:600ms;--ease-out:cubic-bezier(.2,0,0,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--ease-spring:cubic-bezier(.5,1.6,.4,1);--z-base:1;--z-sticky:20;--z-header:30;--z-drawer:40;--z-modal:50;--z-toast:60;--z-help:70}body.night{--color-primary:#9b7cff;--color-secondary:#b485ff;--color-accent-soft:#d6ccff;--color-healthcare:#7aa0ff;--color-discovery:#2ecc71;--color-pharma:#ff8c00;--color-databank:#f472b6;--color-healthcare-active:#4164ee;--color-discovery-active:#07816f;--color-pharma-active:#f7520e;--color-databank-active:#e04380;--healthcare-rgb:122,160,255;--discovery-rgb:46,204,113;--pharma-rgb:255,140,0;--databank-rgb:244,114,182;--surface-rgb:26,26,36;--shadow-rgb:0,0,0;--bg-app:#0e0e14;--bg-surface:#1a1a24;--bg-surface-2:#232330;--bg-surface-3:#2a2a38;--bg-hover:#2d2a42;--border-default:#45455c;--border-soft:#32323f;--text-primary:#f5f6fa;--text-secondary:#cfd3e5;--text-muted:#8a90a8;--brand-rgb:155,124,255;--color-success:#6fe8c5;--color-success-bg:rgba(0,212,168,.18);--color-warning:#fbbf24;--color-warning-bg:rgba(245,158,11,.18);--color-danger:#fca5a5;--color-danger-bg:rgba(239,68,68,.18);--color-info:#7aa0ff;--color-info-bg:rgba(65,100,238,.18);--chart-cat-1:#9b7cff;--chart-cat-2:#6fe8c5;--chart-cat-3:#fbbf24;--chart-cat-4:#f472b6;--chart-cat-5:#7b9ef5;--chart-cat-6:#bef264;background:var(--bg-app);color:var(--text-primary)}body.high-contrast:not(.night){--text-primary:#000;--text-secondary:#1a1a24;--text-muted:#2f2f40;--border-default:#000;--border-soft:#525670;--bg-app:#fff;--bg-surface:#fff;--bg-surface-2:#fff;--bg-surface-3:#f0f1f7;--bg-hover:#ffe56b;--color-primary:#1100a0;--color-primary-hover:#0a0070;--color-secondary:#5028d8;--color-success:#006b2c;--color-warning:#8c4500;--color-danger:#a60000;--color-info:#0040c8}body.night.high-contrast{--text-primary:#fff;--text-secondary:#f5f6fa;--text-muted:#cfd3e5;--border-default:#fff;--border-soft:#b8bcd0;--bg-app:#000;--bg-surface:#000;--bg-surface-2:#000;--bg-surface-3:#000;--bg-hover:#ffe56b;--color-primary:#d6ccff;--color-primary-hover:#fff;--color-secondary:#e5daff;--color-accent-soft:#fff;--color-success:#6fe8c5;--color-warning:#fbbf24;--color-danger:#fca5a5;--color-info:#7aa0ff;--brand-rgb:214,204,255}body.reduce-motion *,body.reduce-motion :after,body.reduce-motion :before{animation:none!important;scroll-behavior:auto!important;transition:none!important}body.cvd-deutera .pane,body.cvd-deutera .rm-cvd-target,body.cvd-deutera .rm-shell,body.cvd-deutera .rsidebar,body.cvd-deutera .sidebar,body.cvd-deutera>.rm-action-bar__overlay,body.cvd-deutera>.rm-drawer,body.cvd-deutera>.rm-drawer-overlay,body.cvd-deutera>.rm-modal-overlay{filter:url(#a11y-deutera)}body.cvd-protan .pane,body.cvd-protan .rm-cvd-target,body.cvd-protan .rm-shell,body.cvd-protan .rsidebar,body.cvd-protan .sidebar,body.cvd-protan>.rm-action-bar__overlay,body.cvd-protan>.rm-drawer,body.cvd-protan>.rm-drawer-overlay,body.cvd-protan>.rm-modal-overlay{filter:url(#a11y-protan)}body.cvd-tritan .pane,body.cvd-tritan .rm-cvd-target,body.cvd-tritan .rm-shell,body.cvd-tritan .rsidebar,body.cvd-tritan .sidebar,body.cvd-tritan>.rm-action-bar__overlay,body.cvd-tritan>.rm-drawer,body.cvd-tritan>.rm-drawer-overlay,body.cvd-tritan>.rm-modal-overlay{filter:url(#a11y-tritan)}body.cvd-mono .pane,body.cvd-mono .rm-cvd-target,body.cvd-mono .rm-shell,body.cvd-mono .rsidebar,body.cvd-mono .sidebar,body.cvd-mono>.rm-action-bar__overlay,body.cvd-mono>.rm-drawer,body.cvd-mono>.rm-drawer-overlay,body.cvd-mono>.rm-modal-overlay{filter:url(#a11y-mono)}:root{--r-color-primary-50:#f0f4ff;--r-color-primary-100:#dce5fd;--r-color-primary-200:#b9cbfb;--r-color-primary-300:#96b0f9;--r-color-primary-400:#7395f5;--r-color-primary-500:#5a7ef2;--r-color-primary-600:var(--color-healthcare);--r-color-primary-700:#3451d4;--r-color-primary-800:#283fb0;--r-color-primary-900:#1c2d8a;--r-color-gray-50:#f9fafb;--r-color-gray-100:#f3f4f6;--r-color-gray-200:#e5e7eb;--r-color-gray-300:#d1d5db;--r-color-gray-400:#9ca3af;--r-color-gray-500:#6b7280;--r-color-gray-600:#4b5563;--r-color-gray-700:#374151;--r-color-gray-800:#1f2937;--r-color-gray-900:#111827;--r-color-gray-950:#030712;--r-color-success-500:var(--color-success);--r-color-success-600:#16a34a;--r-color-warning-500:#f59e0b;--r-color-warning-600:var(--color-warning);--r-color-danger-500:#ef4444;--r-color-danger-600:var(--color-danger);--r-color-white:#fff;--r-color-black:#000;--r-font-family:var(--font-sans);--r-font-mono:var(--font-mono);--r-text-xs:0.75rem;--r-text-sm:0.875rem;--r-text-base:1rem;--r-text-lg:1.125rem;--r-text-xl:1.25rem;--r-text-2xl:1.5rem;--r-text-3xl:1.875rem;--r-text-4xl:2.25rem;--r-font-weight-normal:var(--weight-normal);--r-font-weight-medium:var(--weight-medium);--r-font-weight-semibold:var(--weight-semibold);--r-font-weight-bold:var(--weight-bold);--r-leading-tight:1.25;--r-leading-normal:1.5;--r-leading-relaxed:1.75;--r-space-0:0;--r-space-1:var(--space-4);--r-space-2:var(--space-8);--r-space-3:var(--space-12);--r-space-4:var(--space-16);--r-space-5:var(--space-20);--r-space-6:var(--space-24);--r-space-8:var(--space-32);--r-space-10:var(--space-40);--r-space-12:var(--space-48);--r-radius-none:0;--r-radius-sm:var(--radius-sm);--r-radius-md:var(--radius-md);--r-radius-lg:var(--radius-lg);--r-radius-xl:0.75rem;--r-radius-2xl:1rem;--r-radius-full:var(--radius-pill);--r-shadow-xs:0 1px 2px 0 rgba(0,0,0,.05);--r-shadow-sm:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--r-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--r-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--r-shadow-xl:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--r-transition-fast:var(--duration-fast) var(--ease-in-out);--r-transition-base:200ms var(--ease-in-out);--r-transition-slow:var(--duration-slow) var(--ease-in-out)}
@@ -0,0 +1,229 @@
1
+ <!--
2
+ RmCardActions - le menu en haut a droite d'une carte : exporter, partager.
3
+
4
+ Il est le meme partout (carte de graphique, de tableau, de resultat) parce
5
+ qu'un utilisateur ne doit pas chercher ou est parti l'export selon l'ecran.
6
+ D'ou son autonomie : il trouve seul la carte qui le porte, seul la donnee
7
+ qu'elle affiche (`rmCardSheets`) et seul le graphe qu'elle dessine. Poser
8
+ `<RmCardActions />` suffit, la page n'a rien a brancher.
9
+
10
+ Les items visuels (PNG, SVG, plein ecran) n'apparaissent que si la carte
11
+ porte vraiment un graphe : « Vectoriel SVG » sur un tableau sortirait ses
12
+ fleches de tri. La detection est refaite a chaque ouverture, la donnee
13
+ arrivant apres le premier rendu.
14
+
15
+ Les evenements sont emis EN PLUS de l'action, jamais a sa place : ils
16
+ servent au suivi (PostHog), pas a implementer l'export dans la page.
17
+ -->
18
+ <script setup lang="ts">
19
+ import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
20
+ import type { RmExportSheet } from '../utils/export'
21
+ import { rmCardCharts, rmCardOf } from '../utils/cardData'
22
+ import { useRmCardActions } from '../composables/useRmCardActions'
23
+
24
+ const props = withDefaults(defineProps<{
25
+ /** Langue du menu. Le reste de la carte garde la sienne. */
26
+ lang?: 'fr' | 'en'
27
+ /** Lien copie par « Copier le lien direct ». A defaut, l'URL courante. */
28
+ link?: string
29
+ /** Nom du fichier et de l'onglet. A defaut, le titre lu dans la carte. */
30
+ name?: string
31
+ /**
32
+ * Onglets fournis par la page, quand elle a mieux que le rendu : la donnee
33
+ * complete, ou les nombres bruts derriere des cellules mises en forme.
34
+ */
35
+ sheets?: RmExportSheet[]
36
+ /**
37
+ * Force les items visuels. Non renseigne : ils suivent la presence d'un
38
+ * graphe dans la carte, ce qui est le bon defaut dans les deux sens.
39
+ */
40
+ visual?: boolean
41
+ }>(), {
42
+ lang: 'fr',
43
+ link: undefined,
44
+ name: undefined,
45
+ sheets: undefined,
46
+ visual: undefined,
47
+ })
48
+
49
+ const emit = defineEmits<{
50
+ exportPng: []
51
+ exportSvg: []
52
+ exportCsv: []
53
+ exportXlsx: []
54
+ copyLink: []
55
+ fullscreen: []
56
+ }>()
57
+
58
+ const WORDS = {
59
+ fr: {
60
+ actions: 'Actions sur cette carte',
61
+ exportSection: 'Exporter',
62
+ png: 'Image PNG',
63
+ svg: 'Vectoriel SVG',
64
+ csv: 'Données CSV',
65
+ xlsx: 'Excel (XLSX)',
66
+ shareSection: 'Partager',
67
+ copyLink: 'Copier le lien direct',
68
+ fullscreen: 'Plein écran',
69
+ working: 'Préparation…',
70
+ copied: 'Lien copié',
71
+ failed: "L'export a échoué",
72
+ },
73
+ en: {
74
+ actions: 'Card actions',
75
+ exportSection: 'Export',
76
+ png: 'PNG image',
77
+ svg: 'SVG vector',
78
+ csv: 'CSV data',
79
+ xlsx: 'Excel (XLSX)',
80
+ shareSection: 'Share',
81
+ copyLink: 'Copy direct link',
82
+ fullscreen: 'Fullscreen',
83
+ working: 'Preparing…',
84
+ copied: 'Link copied',
85
+ failed: 'Export failed',
86
+ },
87
+ } as const
88
+
89
+ const t = computed(() => WORDS[props.lang])
90
+
91
+ const { runCardAction, toggleCardFullscreen } = useRmCardActions()
92
+
93
+ const root = ref<HTMLElement | null>(null)
94
+ const menuOpen = ref(false)
95
+ const busy = ref(false)
96
+ const note = ref('')
97
+ const hasChart = ref(false)
98
+
99
+ /** Items visuels : la consigne de la page, sinon ce que la carte dessine. */
100
+ const showVisual = computed(() => props.visual ?? hasChart.value)
101
+
102
+ function cardEl(): Element | null {
103
+ return rmCardOf(root.value)
104
+ }
105
+
106
+ function toggleMenu() {
107
+ const card = cardEl()
108
+ hasChart.value = card ? rmCardCharts(card).length > 0 : false
109
+ note.value = ''
110
+ menuOpen.value = !menuOpen.value
111
+ }
112
+
113
+ /**
114
+ * Lance l'action et rend compte dans le menu. Un export qui echoue doit le
115
+ * dire la ou il a ete demande : un menu qui se referme sans fichier laisse
116
+ * croire a un telechargement bloque par le navigateur.
117
+ */
118
+ async function act(action: 'png' | 'svg' | 'csv' | 'xlsx' | 'link') {
119
+ const card = cardEl()
120
+ if (!card || busy.value) return
121
+
122
+ busy.value = true
123
+ note.value = t.value.working
124
+ try {
125
+ await runCardAction(action, {
126
+ card,
127
+ lang: props.lang,
128
+ name: props.name,
129
+ link: props.link,
130
+ sheets: props.sheets,
131
+ })
132
+ if (action === 'link') {
133
+ // Le presse-papier ne se voit pas : sans un mot, rien ne distingue un
134
+ // lien copie d'un clic perdu. Le menu reste le temps de le lire.
135
+ note.value = t.value.copied
136
+ setTimeout(() => {
137
+ note.value = ''
138
+ menuOpen.value = false
139
+ }, 1400)
140
+ }
141
+ else {
142
+ note.value = ''
143
+ menuOpen.value = false
144
+ }
145
+ }
146
+ catch {
147
+ note.value = t.value.failed
148
+ }
149
+ finally {
150
+ busy.value = false
151
+ }
152
+
153
+ const events = {
154
+ png: 'exportPng',
155
+ svg: 'exportSvg',
156
+ csv: 'exportCsv',
157
+ xlsx: 'exportXlsx',
158
+ link: 'copyLink',
159
+ } as const
160
+ emit(events[action])
161
+ }
162
+
163
+ async function fullscreen() {
164
+ const card = cardEl()
165
+ menuOpen.value = false
166
+ if (card) await toggleCardFullscreen(card)
167
+ emit('fullscreen')
168
+ }
169
+
170
+ function onDocumentClick(event: MouseEvent) {
171
+ if (!menuOpen.value) return
172
+ if (root.value && !root.value.contains(event.target as Node)) menuOpen.value = false
173
+ }
174
+ function onEscape(event: KeyboardEvent) {
175
+ if (event.key === 'Escape') menuOpen.value = false
176
+ }
177
+
178
+ onMounted(() => {
179
+ document.addEventListener('click', onDocumentClick)
180
+ document.addEventListener('keydown', onEscape)
181
+ })
182
+ onBeforeUnmount(() => {
183
+ document.removeEventListener('click', onDocumentClick)
184
+ document.removeEventListener('keydown', onEscape)
185
+ })
186
+ </script>
187
+
188
+ <template>
189
+ <!-- Le clic est arrete ici : la carte de resultat entiere est un lien, et
190
+ ouvrir le menu ne doit pas ouvrir la fiche. -->
191
+ <div ref="root" class="rm-card-actions" :class="{ open: menuOpen }" @click.stop>
192
+ <button
193
+ class="cc-export"
194
+ type="button"
195
+ :aria-label="t.actions"
196
+ :aria-expanded="menuOpen"
197
+ @click="toggleMenu()"
198
+ >
199
+ <RmIcon name="Gear" :size="14" />
200
+ </button>
201
+
202
+ <div class="cc-menu" role="menu">
203
+ <div class="cc-menu-section">{{ t.exportSection }}</div>
204
+ <button v-if="showVisual" class="cc-menu-item" type="button" role="menuitem" :disabled="busy" @click="act('png')">
205
+ <RmIcon name="Image" :size="14" /> {{ t.png }}
206
+ </button>
207
+ <button v-if="showVisual" class="cc-menu-item" type="button" role="menuitem" :disabled="busy" @click="act('svg')">
208
+ <RmIcon name="VectorThree" :size="14" /> {{ t.svg }}
209
+ </button>
210
+ <button class="cc-menu-item" type="button" role="menuitem" :disabled="busy" @click="act('csv')">
211
+ <RmIcon name="FileCsv" :size="14" /> {{ t.csv }}
212
+ </button>
213
+ <button class="cc-menu-item" type="button" role="menuitem" :disabled="busy" @click="act('xlsx')">
214
+ <RmIcon name="FileXls" :size="14" /> {{ t.xlsx }}
215
+ </button>
216
+
217
+ <div class="cc-menu-divider" />
218
+ <div class="cc-menu-section">{{ t.shareSection }}</div>
219
+ <button class="cc-menu-item" type="button" role="menuitem" :disabled="busy" @click="act('link')">
220
+ <RmIcon name="Link" :size="14" /> {{ t.copyLink }}
221
+ </button>
222
+ <button v-if="showVisual" class="cc-menu-item" type="button" role="menuitem" @click="fullscreen()">
223
+ <RmIcon name="ArrowsOut" :size="14" /> {{ t.fullscreen }}
224
+ </button>
225
+
226
+ <p v-if="note" class="cc-menu-note" role="status">{{ note }}</p>
227
+ </div>
228
+ </div>
229
+ </template>
@@ -10,7 +10,7 @@
10
10
  le tableau, elle ne se confond jamais avec la date de verification.
11
11
  -->
12
12
  <script setup lang="ts">
13
- import { computed, ref, onMounted, onBeforeUnmount } from 'vue'
13
+ import { computed } from 'vue'
14
14
  import { rmDate } from '../utils/format'
15
15
 
16
16
  const props = withDefaults(defineProps<{
@@ -32,6 +32,11 @@ const props = withDefaults(defineProps<{
32
32
  extra?: string
33
33
  /** Masque le menu d'actions quand la carte n'a rien a exporter. */
34
34
  actions?: boolean
35
+ /**
36
+ * Lien copie par « Copier le lien direct ». A defaut, l'URL courante, ce
37
+ * qui est le bon lien pour une carte d'analyse : elle vit dans sa page.
38
+ */
39
+ link?: string
35
40
  /**
36
41
  * Etat de la donnee. Les trois variantes se posent dans le CORPS ; la tete
37
42
  * et le pied restent stables, pour que le titre et la source restent sous
@@ -50,11 +55,16 @@ const props = withDefaults(defineProps<{
50
55
  lang: 'fr',
51
56
  extra: undefined,
52
57
  actions: true,
58
+ link: undefined,
53
59
  state: 'ready',
54
60
  retryLabel: '',
55
61
  emptyActionLabel: '',
56
62
  })
57
63
 
64
+ /**
65
+ * Les evenements d'export SUIVENT l'action, ils ne la remplacent pas :
66
+ * `RmCardActions` produit deja le fichier. Ils servent au suivi d'usage.
67
+ */
58
68
  const emit = defineEmits<{
59
69
  exportPng: []
60
70
  exportSvg: []
@@ -68,21 +78,9 @@ const emit = defineEmits<{
68
78
  emptyAction: []
69
79
  }>()
70
80
 
71
- const menuOpen = ref(false)
72
- const root = ref<HTMLElement | null>(null)
73
-
74
81
  /** Le chrome de la carte, dans les deux langues du portail. */
75
82
  const WORDS = {
76
83
  fr: {
77
- actions: 'Actions sur cette carte',
78
- exportSection: 'Exporter',
79
- png: 'Image PNG',
80
- svg: 'Vectoriel SVG',
81
- csv: 'Données CSV',
82
- xlsx: 'Excel (XLSX)',
83
- shareSection: 'Partager',
84
- copyLink: 'Copier le lien direct',
85
- fullscreen: 'Plein écran',
86
84
  source: 'Source :',
87
85
  verified: 'Vérifié le',
88
86
  emptyTitle: 'Aucun résultat',
@@ -91,15 +89,6 @@ const WORDS = {
91
89
  errorText: "La donnée de cette carte n'a pas pu être chargée.",
92
90
  },
93
91
  en: {
94
- actions: 'Card actions',
95
- exportSection: 'Export',
96
- png: 'PNG image',
97
- svg: 'SVG vector',
98
- csv: 'CSV data',
99
- xlsx: 'Excel (XLSX)',
100
- shareSection: 'Share',
101
- copyLink: 'Copy direct link',
102
- fullscreen: 'Fullscreen',
103
92
  source: 'Source:',
104
93
  verified: 'Verified on',
105
94
  emptyTitle: 'No results',
@@ -129,27 +118,6 @@ const verifiedText = computed(() => {
129
118
  : rmDate(parsed)
130
119
  })
131
120
 
132
- function onDocumentClick(event: MouseEvent) {
133
- if (!menuOpen.value) return
134
- if (root.value && !root.value.contains(event.target as Node)) menuOpen.value = false
135
- }
136
- function onEscape(event: KeyboardEvent) {
137
- if (event.key === 'Escape') menuOpen.value = false
138
- }
139
-
140
- onMounted(() => {
141
- document.addEventListener('click', onDocumentClick)
142
- document.addEventListener('keydown', onEscape)
143
- })
144
- onBeforeUnmount(() => {
145
- document.removeEventListener('click', onDocumentClick)
146
- document.removeEventListener('keydown', onEscape)
147
- })
148
-
149
- function run(action: 'exportPng' | 'exportSvg' | 'exportCsv' | 'exportXlsx' | 'copyLink' | 'fullscreen') {
150
- menuOpen.value = false
151
- emit(action)
152
- }
153
121
  </script>
154
122
 
155
123
  <template>
@@ -158,40 +126,22 @@ function run(action: 'exportPng' | 'exportSvg' | 'exportCsv' | 'exportXlsx' | 'c
158
126
  <p class="chart-card-title">{{ title }}</p>
159
127
  <p v-if="context" class="chart-card-context">{{ context }}</p>
160
128
 
161
- <div v-if="actions" ref="root" class="chart-card-actions" :class="{ open: menuOpen }">
162
- <button
163
- class="cc-export"
164
- type="button"
165
- :aria-label="t.actions"
166
- :aria-expanded="menuOpen"
167
- @click.stop="menuOpen = !menuOpen"
168
- >
169
- <RmIcon name="Gear" :size="14" />
170
- </button>
171
- <div class="cc-menu" role="menu">
172
- <div class="cc-menu-section">{{ t.exportSection }}</div>
173
- <button class="cc-menu-item" type="button" role="menuitem" @click="run('exportPng')">
174
- <RmIcon name="Image" :size="14" /> {{ t.png }}
175
- </button>
176
- <button class="cc-menu-item" type="button" role="menuitem" @click="run('exportSvg')">
177
- <RmIcon name="VectorThree" :size="14" /> {{ t.svg }}
178
- </button>
179
- <button class="cc-menu-item" type="button" role="menuitem" @click="run('exportCsv')">
180
- <RmIcon name="FileCsv" :size="14" /> {{ t.csv }}
181
- </button>
182
- <button class="cc-menu-item" type="button" role="menuitem" @click="run('exportXlsx')">
183
- <RmIcon name="FileXls" :size="14" /> {{ t.xlsx }}
184
- </button>
185
- <div class="cc-menu-divider" />
186
- <div class="cc-menu-section">{{ t.shareSection }}</div>
187
- <button class="cc-menu-item" type="button" role="menuitem" @click="run('copyLink')">
188
- <RmIcon name="Link" :size="14" /> {{ t.copyLink }}
189
- </button>
190
- <button class="cc-menu-item" type="button" role="menuitem" @click="run('fullscreen')">
191
- <RmIcon name="ArrowsOut" :size="14" /> {{ t.fullscreen }}
192
- </button>
193
- </div>
194
- </div>
129
+ <!-- Les six actions vivent dans RmCardActions : elles doivent etre les
130
+ memes sur une carte de resultat que sur une carte de graphique. Les
131
+ evenements sont reemis pour les pages qui suivent les exports. -->
132
+ <RmCardActions
133
+ v-if="actions"
134
+ class="chart-card-actions"
135
+ :lang="lang"
136
+ :link="link"
137
+ :name="context ? `${title} (${context})` : title"
138
+ @export-png="emit('exportPng')"
139
+ @export-svg="emit('exportSvg')"
140
+ @export-csv="emit('exportCsv')"
141
+ @export-xlsx="emit('exportXlsx')"
142
+ @copy-link="emit('copyLink')"
143
+ @fullscreen="emit('fullscreen')"
144
+ />
195
145
  </div>
196
146
 
197
147
  <!-- Un corps vide n'est jamais laisse blanc : chaque etat a son rendu par
@@ -1,5 +1,7 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue'
2
+ import { computed, ref, toRef } from 'vue'
3
+ import { useRmDialog } from '../composables/useRmDialog'
4
+
3
5
  export interface RmDrawerProps {
4
6
  /** Whether the drawer is open */
5
7
  modelValue: boolean
@@ -11,6 +13,15 @@ export interface RmDrawerProps {
11
13
  overlay?: boolean
12
14
  /** Offset from the edge (e.g. '280px' to keep a panel visible) */
13
15
  offset?: string
16
+ /**
17
+ * Nom accessible du tiroir. A renseigner des que l'en-tete est purement
18
+ * visuel : sans lui un lecteur d'ecran annonce « dialogue » sans dire lequel.
19
+ */
20
+ label?: string
21
+ /** Id de l'element qui porte le titre visible, quand l'en-tete en a un. */
22
+ labelledBy?: string
23
+ /** Libelle du bouton de fermeture, pour les lecteurs d'ecran. */
24
+ closeLabel?: string
14
25
  }
15
26
 
16
27
  const props = withDefaults(defineProps<RmDrawerProps>(), {
@@ -18,6 +29,9 @@ const props = withDefaults(defineProps<RmDrawerProps>(), {
18
29
  side: 'right',
19
30
  overlay: true,
20
31
  offset: '0px',
32
+ label: undefined,
33
+ labelledBy: undefined,
34
+ closeLabel: 'Fermer',
21
35
  })
22
36
 
23
37
  const emit = defineEmits<{
@@ -28,6 +42,9 @@ function close() {
28
42
  emit('update:modelValue', false)
29
43
  }
30
44
 
45
+ const surface = ref<HTMLElement | null>(null)
46
+ useRmDialog({ open: toRef(props, 'modelValue'), surface, close })
47
+
31
48
  const sizeMap: Record<string, string> = {
32
49
  sm: '360px',
33
50
  md: '480px',
@@ -52,13 +69,19 @@ const drawerWidth = computed(() => sizeMap[props.size] || props.size)
52
69
  <Transition :name="`rm-drawer-slide-${side}`">
53
70
  <div
54
71
  v-if="modelValue"
72
+ ref="surface"
55
73
  class="rm-drawer"
56
74
  :class="[`rm-drawer--${side}`]"
57
75
  :style="{ width: drawerWidth, [side]: offset }"
76
+ role="dialog"
77
+ aria-modal="true"
78
+ :aria-label="labelledBy ? undefined : label"
79
+ :aria-labelledby="labelledBy"
80
+ tabindex="-1"
58
81
  >
59
82
  <div class="rm-drawer__header">
60
83
  <slot name="header" />
61
- <button class="rm-drawer__close" @click="close">
84
+ <button class="rm-drawer__close" type="button" :aria-label="closeLabel" @click="close">
62
85
  <RmIcon name="X" :size="20" />
63
86
  </button>
64
87
  </div>