@tekkare/romulus 0.1.0 → 0.1.7
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 +1 -1
- package/dist/module.mjs +13 -1
- package/dist/runtime/assets/styles/components.css +1 -0
- package/dist/runtime/assets/styles/tokens.css +1 -1
- package/dist/runtime/assets/styles/utilities.css +1 -0
- package/dist/runtime/components/ActionBar.vue +95 -0
- package/dist/runtime/components/AdvancedSearch.vue +110 -0
- package/dist/runtime/components/Badge.vue +15 -0
- package/dist/runtime/components/Breadcrumb.vue +46 -0
- package/dist/runtime/components/Button.vue +17 -9
- package/dist/runtime/components/ChartCard.vue +129 -0
- package/dist/runtime/components/DataTable.vue +92 -0
- package/dist/runtime/components/Drawer.vue +79 -0
- package/dist/runtime/components/FilterCheckbox.vue +26 -0
- package/dist/runtime/components/FilterComplex.vue +205 -0
- package/dist/runtime/components/FilterDropdown.vue +119 -0
- package/dist/runtime/components/Icon.vue +43 -0
- package/dist/runtime/components/Kpi.vue +63 -0
- package/dist/runtime/components/NavGroup.vue +37 -0
- package/dist/runtime/components/NavLink.vue +60 -0
- package/dist/runtime/components/Pill.vue +37 -0
- package/dist/runtime/components/PillTabs.vue +34 -0
- package/dist/runtime/components/SearchBar.vue +38 -0
- package/dist/runtime/components/Sidebar.vue +143 -0
- package/dist/runtime/components/Tabs.vue +105 -0
- package/dist/runtime/components/ToolbarFilter.vue +134 -0
- package/dist/runtime/utils/format.d.ts +27 -0
- package/dist/runtime/utils/format.js +7 -0
- package/package.json +4 -3
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -10,18 +10,30 @@ const module = defineNuxtModule({
|
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
defaults: {
|
|
13
|
-
prefix: "
|
|
13
|
+
prefix: "Rm"
|
|
14
14
|
},
|
|
15
15
|
async setup(options, nuxt) {
|
|
16
16
|
const { resolve } = createResolver(import.meta.url);
|
|
17
17
|
const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
|
|
18
18
|
nuxt.options.css.push(resolve(runtimeDir, "assets/styles/tokens.css"));
|
|
19
19
|
nuxt.options.css.push(resolve(runtimeDir, "assets/styles/base.css"));
|
|
20
|
+
nuxt.options.css.push(resolve(runtimeDir, "assets/styles/components.css"));
|
|
21
|
+
nuxt.options.css.push(resolve(runtimeDir, "assets/styles/utilities.css"));
|
|
20
22
|
await addComponentsDir({
|
|
21
23
|
path: resolve(runtimeDir, "components"),
|
|
22
24
|
prefix: options.prefix
|
|
23
25
|
});
|
|
24
26
|
addImportsDir(resolve(runtimeDir, "composables"));
|
|
27
|
+
addImportsDir(resolve(runtimeDir, "utils"));
|
|
28
|
+
nuxt.options.app.head.link = nuxt.options.app.head.link || [];
|
|
29
|
+
const fontsHref = "https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&family=Caudex:wght@400;700&display=swap";
|
|
30
|
+
if (!nuxt.options.app.head.link.some((l) => l.href === fontsHref)) {
|
|
31
|
+
nuxt.options.app.head.link.push(
|
|
32
|
+
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
|
|
33
|
+
{ rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: "" },
|
|
34
|
+
{ rel: "stylesheet", href: fontsHref }
|
|
35
|
+
);
|
|
36
|
+
}
|
|
25
37
|
}
|
|
26
38
|
});
|
|
27
39
|
|
|
@@ -0,0 +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}.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-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}.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}.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)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--r-color-primary-50:#
|
|
1
|
+
:root{--sidebar-w:220px;--sidebar-collapsed-w:56px;--rsidebar-w:280px;--rsidebar-collapsed-w:76px;--topbar-h:56px;--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:#1a65e0;--color-discovery-active:#066b5c;--color-pharma-active:#d9470c;--color-databank-active:#c73a6f;--healthcare-rgb:65,100,238;--discovery-rgb:7,129,111;--pharma-rgb:247,82,14;--databank-rgb:224,67,128;--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-databank:#f472b6;--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)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.row{display:flex;flex-direction:row}.column{display:flex;flex-direction:column}.row-reverse{display:flex;flex-direction:row-reverse}.column-reverse{display:flex;flex-direction:column-reverse}.x-start{justify-content:flex-start}.x-center{justify-content:center}.x-end{justify-content:flex-end}.x-between{justify-content:space-between}.x-around{justify-content:space-around}.x-evenly{justify-content:space-evenly}.y-start{align-items:flex-start}.y-center{align-items:center}.y-end{align-items:flex-end}.y-between{align-content:space-between}.y-stretch{align-items:stretch}.y-baseline{align-items:baseline}.wrap{flex-wrap:wrap}.nowrap{flex-wrap:nowrap}.grow{flex-grow:1}.grow-0{flex-grow:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.self-start{align-self:flex-start}.self-center{align-self:center}.self-end{align-self:flex-end}.self-stretch{align-self:stretch}.w-full{width:100%}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.min-w-0{min-width:0}.h-full{height:100%}.h-auto{height:auto}.h-screen{height:100vh}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.block{display:block}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-14{gap:14px}.gap-16{gap:16px}.gap-18{gap:18px}.gap-20{gap:20px}.gap-22{gap:22px}.gap-24{gap:24px}.gap-26{gap:26px}.gap-28{gap:28px}.gap-30{gap:30px}.gap-32{gap:32px}.gap-34{gap:34px}.gap-36{gap:36px}.gap-38{gap:38px}.gap-40{gap:40px}.gap-42{gap:42px}.gap-44{gap:44px}.gap-46{gap:46px}.gap-48{gap:48px}.gap-50{gap:50px}.gap-52{gap:52px}.gap-54{gap:54px}.gap-56{gap:56px}.gap-58{gap:58px}.gap-60{gap:60px}.gap-62{gap:62px}.gap-64{gap:64px}.gap-66{gap:66px}.gap-68{gap:68px}.gap-70{gap:70px}.gap-72{gap:72px}.gap-74{gap:74px}.gap-76{gap:76px}.gap-78{gap:78px}.gap-80{gap:80px}.p-0{padding:0}.p-4{padding:4px}.p-8{padding:8px}.p-12{padding:12px}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.p-32{padding:32px}.p-40{padding:40px}.p-48{padding:48px}.px-4{padding-left:4px;padding-right:4px}.px-8{padding-left:8px;padding-right:8px}.px-12{padding-left:12px;padding-right:12px}.px-16{padding-left:16px;padding-right:16px}.px-20{padding-left:20px;padding-right:20px}.px-24{padding-left:24px;padding-right:24px}.px-32{padding-left:32px;padding-right:32px}.py-4{padding-bottom:4px;padding-top:4px}.py-8{padding-bottom:8px;padding-top:8px}.py-12{padding-bottom:12px;padding-top:12px}.py-16{padding-bottom:16px;padding-top:16px}.py-20{padding-bottom:20px;padding-top:20px}.py-24{padding-bottom:24px;padding-top:24px}.py-32{padding-bottom:32px;padding-top:32px}.m-0{margin:0}.m-auto{margin:auto}.mx-auto{margin-right:auto}.ml-auto,.mx-auto{margin-left:auto}.mr-auto{margin-right:auto}.mt-auto{margin-top:auto}.mb-auto{margin-bottom:auto}.border{border:1px solid var(--r-color-gray-200)}.border-t{border-top:1px solid var(--r-color-gray-200)}.border-b{border-bottom:1px solid var(--r-color-gray-200)}.border-l{border-left:1px solid var(--r-color-gray-200)}.border-r{border-right:1px solid var(--r-color-gray-200)}.border-none{border:none}.rounded-sm{border-radius:var(--r-radius-sm)}.rounded-md{border-radius:var(--r-radius-md)}.rounded-lg{border-radius:var(--r-radius-lg)}.rounded-xl{border-radius:var(--r-radius-xl)}.rounded-full{border-radius:var(--r-radius-full)}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
export interface RmActionBarProps {
|
|
3
|
+
/** Width of the action panel in px */
|
|
4
|
+
panelWidth?: number
|
|
5
|
+
/** Width of the drawer when open in px */
|
|
6
|
+
drawerWidth?: number
|
|
7
|
+
/** Whether the drawer is open */
|
|
8
|
+
drawerOpen?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<RmActionBarProps>(), {
|
|
12
|
+
panelWidth: 280,
|
|
13
|
+
drawerWidth: 640,
|
|
14
|
+
drawerOpen: false,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const emit = defineEmits<{
|
|
18
|
+
'update:drawerOpen': [value: boolean]
|
|
19
|
+
'closeDrawer': []
|
|
20
|
+
}>()
|
|
21
|
+
|
|
22
|
+
const totalWidth = computed(() => {
|
|
23
|
+
return props.drawerOpen
|
|
24
|
+
? props.panelWidth + props.drawerWidth
|
|
25
|
+
: props.panelWidth
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
function closeDrawer() {
|
|
29
|
+
emit('update:drawerOpen', false)
|
|
30
|
+
emit('closeDrawer')
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<div
|
|
37
|
+
class="rm-action-bar"
|
|
38
|
+
:style="{ width: totalWidth + 'px' }"
|
|
39
|
+
>
|
|
40
|
+
<!-- Panel -->
|
|
41
|
+
<div class="rm-action-bar__panel" :style="{ width: panelWidth + 'px' }">
|
|
42
|
+
<div class="rm-action-bar__header">
|
|
43
|
+
<slot name="header" />
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div v-if="$slots.tabs" class="rm-action-bar__tabs">
|
|
47
|
+
<slot name="tabs" />
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="rm-action-bar__content">
|
|
51
|
+
<slot name="actions" />
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div v-if="$slots.footer" class="rm-action-bar__footer">
|
|
55
|
+
<slot name="footer" />
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<!-- Overlay -->
|
|
60
|
+
<Teleport to="body">
|
|
61
|
+
<Transition name="rm-action-bar-overlay">
|
|
62
|
+
<div
|
|
63
|
+
v-if="drawerOpen"
|
|
64
|
+
class="rm-action-bar__overlay"
|
|
65
|
+
@click="closeDrawer"
|
|
66
|
+
/>
|
|
67
|
+
</Transition>
|
|
68
|
+
</Teleport>
|
|
69
|
+
|
|
70
|
+
<!-- Drawer -->
|
|
71
|
+
<Transition name="rm-action-bar-drawer">
|
|
72
|
+
<div
|
|
73
|
+
v-if="drawerOpen"
|
|
74
|
+
class="rm-action-bar__drawer"
|
|
75
|
+
>
|
|
76
|
+
<div class="rm-action-bar__drawer-header">
|
|
77
|
+
<slot name="drawer-header" />
|
|
78
|
+
<button class="rm-action-bar__drawer-close" @click="closeDrawer">
|
|
79
|
+
<RmIcon name="X" :size="20" />
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="rm-action-bar__drawer-body">
|
|
83
|
+
<slot name="drawer" />
|
|
84
|
+
</div>
|
|
85
|
+
<div v-if="$slots['drawer-footer']" class="rm-action-bar__drawer-footer">
|
|
86
|
+
<slot name="drawer-footer" />
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</Transition>
|
|
90
|
+
</div>
|
|
91
|
+
</template>
|
|
92
|
+
|
|
93
|
+
<style scoped>
|
|
94
|
+
.rm-action-bar{bottom:0;display:flex;position:fixed;right:0;top:0;transition:width .3s cubic-bezier(.4,0,.2,1);z-index:61}.rm-action-bar__panel{background-color:var(--r-color-white);border-left:1px solid var(--r-color-gray-200);display:flex;flex-direction:column;flex-shrink:0;height:100%}.rm-action-bar__header{align-items:center;display:flex;gap:12px;justify-content:space-between;min-height:64px;padding:0 16px}.rm-action-bar__tabs{border-bottom:1px solid var(--r-color-gray-200);padding:8px 16px 12px}.rm-action-bar__content{display:flex;flex:1;flex-direction:column;overflow-y:auto}.rm-action-bar__footer{border-top:1px solid var(--r-color-gray-200);padding:16px}.rm-action-bar__overlay{backdrop-filter:blur(2px);background-color:rgba(0,0,0,.2);inset:0;position:fixed;z-index:60}.rm-action-bar-overlay-enter-active,.rm-action-bar-overlay-leave-active{transition:opacity .25s ease}.rm-action-bar-overlay-enter-from,.rm-action-bar-overlay-leave-to{opacity:0}.rm-action-bar__drawer{background-color:var(--r-color-white);border-left:1px solid var(--r-color-gray-200);display:flex;flex:1;flex-direction:column;min-width:0;overflow:hidden}.rm-action-bar__drawer-header{align-items:center;border-bottom:1px solid var(--r-color-gray-200);display:flex;justify-content:space-between;min-height:64px;padding:0 24px}.rm-action-bar__drawer-close{align-items:center;background:none;border:none;border-radius:var(--r-radius-md);color:var(--r-color-gray-500);cursor:pointer;display:flex;flex-shrink:0;height:32px;justify-content:center;transition:all var(--r-transition-fast);width:32px}.rm-action-bar__drawer-close:hover{background-color:var(--r-color-gray-100);color:var(--r-color-gray-700)}.rm-action-bar__drawer-body{flex:1;overflow-y:auto;padding:24px}.rm-action-bar__drawer-footer{border-top:1px solid var(--r-color-gray-200);padding:16px 24px}.rm-action-bar-drawer-enter-active,.rm-action-bar-drawer-leave-active{transition:opacity .25s ease}.rm-action-bar-drawer-enter-from,.rm-action-bar-drawer-leave-to{opacity:0}
|
|
95
|
+
</style>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
PhTrash,
|
|
4
|
+
PhPlus,
|
|
5
|
+
PhLineVertical,
|
|
6
|
+
PhMinus,
|
|
7
|
+
} from '@phosphor-icons/vue'
|
|
8
|
+
|
|
9
|
+
interface SearchRow {
|
|
10
|
+
id: number
|
|
11
|
+
type: 'and' | 'or' | 'not'
|
|
12
|
+
value: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
defineProps<{
|
|
16
|
+
visible: boolean
|
|
17
|
+
}>()
|
|
18
|
+
|
|
19
|
+
defineEmits<{
|
|
20
|
+
close: []
|
|
21
|
+
saveSearch: []
|
|
22
|
+
}>()
|
|
23
|
+
|
|
24
|
+
let nextId = 1
|
|
25
|
+
|
|
26
|
+
const rows = ref<SearchRow[]>([
|
|
27
|
+
{ id: nextId++, type: 'and', value: '' },
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
function addRow(type: 'and' | 'or' | 'not') {
|
|
31
|
+
rows.value.push({ id: nextId++, type, value: '' })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function removeRow(id: number) {
|
|
35
|
+
rows.value = rows.value.filter(r => r.id !== id)
|
|
36
|
+
if (rows.value.length === 0) {
|
|
37
|
+
rows.value.push({ id: nextId++, type: 'and', value: '' })
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function clearAll() {
|
|
42
|
+
rows.value = [{ id: nextId++, type: 'and', value: '' }]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const typeColors: Record<string, string> = {
|
|
46
|
+
and: '#4164EE',
|
|
47
|
+
or: '#07816F',
|
|
48
|
+
not: '#F7520E',
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template>
|
|
53
|
+
<div v-if="visible" class="advanced-search">
|
|
54
|
+
<div class="advanced-search__header">
|
|
55
|
+
<h2 class="advanced-search__title">Advanced search</h2>
|
|
56
|
+
<RmButton variant="outline" size="sm" @click="$emit('saveSearch')">
|
|
57
|
+
Save search
|
|
58
|
+
<PhLineVertical :size="14" />
|
|
59
|
+
</RmButton>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div class="advanced-search__rows">
|
|
63
|
+
<div
|
|
64
|
+
v-for="row in rows"
|
|
65
|
+
:key="row.id"
|
|
66
|
+
class="advanced-search__row"
|
|
67
|
+
>
|
|
68
|
+
<span
|
|
69
|
+
class="advanced-search__type"
|
|
70
|
+
:style="{ color: typeColors[row.type] }"
|
|
71
|
+
>
|
|
72
|
+
{{ row.type.charAt(0).toUpperCase() + row.type.slice(1) }}
|
|
73
|
+
</span>
|
|
74
|
+
<input
|
|
75
|
+
v-model="row.value"
|
|
76
|
+
type="text"
|
|
77
|
+
:placeholder="row.value ? '' : 'Add a keyword'"
|
|
78
|
+
class="advanced-search__input"
|
|
79
|
+
:class="{ 'advanced-search__input--not': row.type === 'not' }"
|
|
80
|
+
>
|
|
81
|
+
<button class="advanced-search__delete" @click="removeRow(row.id)">
|
|
82
|
+
<PhTrash :size="16" />
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="advanced-search__actions">
|
|
88
|
+
<button class="advanced-search__add" @click="addRow('and')">
|
|
89
|
+
<PhPlus :size="12" />
|
|
90
|
+
and
|
|
91
|
+
</button>
|
|
92
|
+
<button class="advanced-search__add" @click="addRow('or')">
|
|
93
|
+
<PhLineVertical :size="12" />
|
|
94
|
+
or
|
|
95
|
+
</button>
|
|
96
|
+
<button class="advanced-search__add" @click="addRow('not')">
|
|
97
|
+
<PhMinus :size="12" />
|
|
98
|
+
not
|
|
99
|
+
</button>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<button class="advanced-search__clear" @click="clearAll">
|
|
103
|
+
Clear All
|
|
104
|
+
</button>
|
|
105
|
+
</div>
|
|
106
|
+
</template>
|
|
107
|
+
|
|
108
|
+
<style scoped>
|
|
109
|
+
.advanced-search{background-color:var(--r-color-white);border-bottom:1px solid var(--r-color-gray-200);padding:20px 24px}.advanced-search__header{align-items:center;display:flex;justify-content:space-between;margin-bottom:20px}.advanced-search__title{color:var(--r-color-gray-900);font-family:var(--r-font-family);font-size:16px;font-weight:700;margin:0}.advanced-search__rows{display:flex;flex-direction:column;gap:12px;margin-bottom:16px}.advanced-search__row{align-items:center;display:flex;gap:12px}.advanced-search__type{flex-shrink:0;font-weight:600;min-width:32px}.advanced-search__input,.advanced-search__type{font-family:var(--r-font-family);font-size:var(--r-text-sm)}.advanced-search__input{background:#fff;border:1px solid #dbdef0;border-radius:6px;color:var(--r-color-gray-900);flex:1;height:36px;outline:none;padding:0 12px;transition:border-color var(--r-transition-fast)}.advanced-search__input:focus{border-color:#4164ee}.advanced-search__input--not,.advanced-search__input--not:focus{border-color:#f7520e}.advanced-search__input::-moz-placeholder{color:var(--r-color-gray-400)}.advanced-search__input::placeholder{color:var(--r-color-gray-400)}.advanced-search__delete{align-items:center;background:none;border:none;border-radius:4px;color:var(--r-color-gray-400);cursor:pointer;display:flex;justify-content:center;padding:4px;transition:color var(--r-transition-fast)}.advanced-search__delete:hover{color:var(--r-color-danger-500)}.advanced-search__actions{display:flex;gap:8px;margin-bottom:16px}.advanced-search__add{align-items:center;background:#fff;border:1px solid #dbdef0;border-radius:6px;color:var(--r-color-gray-600);cursor:pointer;display:inline-flex;font-family:var(--r-font-family);font-size:var(--r-text-sm);font-weight:var(--r-font-weight-medium);gap:4px;height:32px;padding:4px 12px;transition:all var(--r-transition-fast)}.advanced-search__add:hover{border-color:var(--r-color-gray-300);box-shadow:0 1px 2px 0 rgba(30,41,59,.1)}.advanced-search__clear{background:none;border:1px solid var(--r-color-danger-500);border-radius:6px;color:var(--r-color-danger-500);cursor:pointer;font-family:var(--r-font-family);font-size:var(--r-text-sm);font-weight:var(--r-font-weight-medium);padding:4px 12px;transition:all var(--r-transition-fast)}.advanced-search__clear:hover{background-color:var(--r-color-danger-500);color:#fff}
|
|
110
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmBadge - label de statut STATIQUE (non cliquable).
|
|
3
|
+
Style : components.css (.badge). Jamais la couleur seule : le badge porte
|
|
4
|
+
toujours son texte. Pour un filtre cliquable, ce n'est pas un badge.
|
|
5
|
+
-->
|
|
6
|
+
<script setup lang="ts">
|
|
7
|
+
withDefaults(defineProps<{
|
|
8
|
+
/** active = favorable/inscrit/valide · pending = en cours/restreint · error = radie/refus · info · grey = neutre */
|
|
9
|
+
variant?: 'active' | 'pending' | 'error' | 'info' | 'grey'
|
|
10
|
+
}>(), { variant: 'grey' })
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<span class="badge" :class="`badge-${variant}`"><slot /></span>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
export interface RmBreadcrumbItem {
|
|
3
|
+
/** Display label */
|
|
4
|
+
label: string
|
|
5
|
+
/** Optional icon name (Phosphor) — only shown on first item by default */
|
|
6
|
+
icon?: string
|
|
7
|
+
/** Navigation path */
|
|
8
|
+
to?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface RmBreadcrumbProps {
|
|
12
|
+
/** Breadcrumb items */
|
|
13
|
+
items: RmBreadcrumbItem[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
defineProps<RmBreadcrumbProps>()
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<nav class="rm-breadcrumb">
|
|
21
|
+
<template v-for="(item, idx) in items" :key="idx">
|
|
22
|
+
<span v-if="idx > 0" class="rm-breadcrumb__sep">/</span>
|
|
23
|
+
<NuxtLink
|
|
24
|
+
v-if="item.to"
|
|
25
|
+
:to="item.to"
|
|
26
|
+
class="rm-breadcrumb__item"
|
|
27
|
+
:class="{ 'rm-breadcrumb__item--first': idx === 0 }"
|
|
28
|
+
>
|
|
29
|
+
<RmIcon v-if="item.icon" :name="item.icon" :size="16" weight="duotone" class="rm-breadcrumb__icon" />
|
|
30
|
+
{{ item.label }}
|
|
31
|
+
</NuxtLink>
|
|
32
|
+
<span
|
|
33
|
+
v-else
|
|
34
|
+
class="rm-breadcrumb__item"
|
|
35
|
+
:class="{ 'rm-breadcrumb__item--first': idx === 0 }"
|
|
36
|
+
>
|
|
37
|
+
<RmIcon v-if="item.icon" :name="item.icon" :size="16" weight="duotone" class="rm-breadcrumb__icon" />
|
|
38
|
+
{{ item.label }}
|
|
39
|
+
</span>
|
|
40
|
+
</template>
|
|
41
|
+
</nav>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<style scoped>
|
|
45
|
+
.rm-breadcrumb{align-items:center;color:var(--r-color-gray-400);display:flex;font-family:var(--r-font-family);font-size:14px;gap:8px}.rm-breadcrumb__sep{color:var(--r-color-gray-300)}.rm-breadcrumb__item{align-items:center;color:var(--r-color-gray-400);display:inline-flex;gap:6px;text-decoration:none;transition:color var(--r-transition-fast)}.rm-breadcrumb__item--first{color:var(--r-color-gray-700);font-weight:500}a.rm-breadcrumb__item:hover{color:var(--r-color-primary-600)}.rm-breadcrumb__icon{color:var(--r-color-primary-500)}
|
|
46
|
+
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
export interface
|
|
2
|
+
export interface RmButtonProps {
|
|
3
3
|
/** Visual style variant */
|
|
4
|
-
variant?: 'primary' | 'secondary' | 'outline' | 'ghost'
|
|
4
|
+
variant?: 'primary-fill' | 'primary-stroke' | 'discovery-fill' | 'discovery-stroke' | 'databank-fill' | 'databank-stroke' | 'pharma-fill' | 'pharma-stroke' | 'secondary' | 'outline' | 'ghost'
|
|
5
5
|
/** Size of the button */
|
|
6
6
|
size?: 'sm' | 'md' | 'lg'
|
|
7
7
|
/** Whether the button is disabled */
|
|
@@ -10,14 +10,20 @@ export interface RButtonProps {
|
|
|
10
10
|
block?: boolean
|
|
11
11
|
/** HTML button type attribute */
|
|
12
12
|
type?: 'button' | 'submit' | 'reset'
|
|
13
|
+
/** Phosphor icon name before the label */
|
|
14
|
+
prefixIcon?: string
|
|
15
|
+
/** Phosphor icon name after the label */
|
|
16
|
+
suffixIcon?: string
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
withDefaults(defineProps<
|
|
16
|
-
variant: 'primary',
|
|
19
|
+
withDefaults(defineProps<RmButtonProps>(), {
|
|
20
|
+
variant: 'primary-fill',
|
|
17
21
|
size: 'md',
|
|
18
22
|
disabled: false,
|
|
19
23
|
block: false,
|
|
20
24
|
type: 'button',
|
|
25
|
+
prefixIcon: undefined,
|
|
26
|
+
suffixIcon: undefined,
|
|
21
27
|
})
|
|
22
28
|
|
|
23
29
|
defineEmits<{
|
|
@@ -30,17 +36,19 @@ defineEmits<{
|
|
|
30
36
|
:type="type"
|
|
31
37
|
:disabled="disabled"
|
|
32
38
|
:class="[
|
|
33
|
-
'
|
|
34
|
-
`
|
|
35
|
-
`
|
|
36
|
-
{ '
|
|
39
|
+
'rm-button',
|
|
40
|
+
`rm-button--${variant}`,
|
|
41
|
+
`rm-button--${size}`,
|
|
42
|
+
{ 'rm-button--block': block },
|
|
37
43
|
]"
|
|
38
44
|
@click="$emit('click', $event)"
|
|
39
45
|
>
|
|
46
|
+
<RmIcon v-if="prefixIcon" :name="prefixIcon" :size="size === 'sm' ? 14 : 16" />
|
|
40
47
|
<slot />
|
|
48
|
+
<RmIcon v-if="suffixIcon" :name="suffixIcon" :size="size === 'sm' ? 14 : 16" />
|
|
41
49
|
</button>
|
|
42
50
|
</template>
|
|
43
51
|
|
|
44
52
|
<style scoped>
|
|
45
|
-
.
|
|
53
|
+
.rm-button{align-items:center;border:1px solid transparent;border-radius:6px;cursor:pointer;display:inline-flex;font-family:var(--r-font-family);font-weight:500;gap:8px;justify-content:center;transition:all var(--r-transition-fast);-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.rm-button:focus-visible{outline:2px solid var(--r-color-primary-500);outline-offset:2px}.rm-button:disabled{cursor:not-allowed;opacity:.5}.rm-button--sm{font-size:13px;height:32px;padding:6px 16px}.rm-button--md{font-size:14px;height:40px;padding:10px 24px}.rm-button--lg{font-size:15px;height:48px;padding:12px 28px}.rm-button--primary-fill{background:linear-gradient(180deg,rgba(65,100,238,.8),rgba(65,100,238,.9));border-color:#4164ee;box-shadow:0 2px 3px 0 rgba(65,100,238,.5);color:var(--r-color-white)}.rm-button--primary-fill:hover:not(:disabled){background:linear-gradient(180deg,#4164ee,#4164ee);box-shadow:0 2px 4px 0 rgba(65,100,238,.6)}.rm-button--primary-fill:active:not(:disabled){background:#3451d4;box-shadow:0 1px 2px 0 rgba(65,100,238,.4)}.rm-button--primary-stroke{box-shadow:0 2px 3px 0 rgba(65,100,238,.5);color:#4164ee}.rm-button--primary-stroke,.rm-button--primary-stroke:hover:not(:disabled){background:linear-gradient(180deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.9));border-color:#4164ee}.rm-button--primary-stroke:hover:not(:disabled){box-shadow:none}.rm-button--primary-stroke:active:not(:disabled){background:var(--r-color-primary-50);box-shadow:none}.rm-button--discovery-fill{background:linear-gradient(180deg,rgba(7,129,111,.8),rgba(7,129,111,.9));border-color:#07816f;box-shadow:0 2px 3px 0 rgba(7,129,111,.5);color:var(--r-color-white)}.rm-button--discovery-fill:hover:not(:disabled){background:linear-gradient(180deg,#07816f,#07816f);border-color:#07816f;box-shadow:none}.rm-button--discovery-fill:active:not(:disabled){background:linear-gradient(180deg,#066b5c,#066b5c);box-shadow:none}.rm-button--discovery-stroke{box-shadow:0 2px 3px 0 rgba(7,129,111,.5);color:#07816f}.rm-button--discovery-stroke,.rm-button--discovery-stroke:hover:not(:disabled){background:linear-gradient(180deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.9));border-color:#07816f}.rm-button--discovery-stroke:hover:not(:disabled){box-shadow:none}.rm-button--discovery-stroke:active:not(:disabled){background:rgba(7,129,111,.05);box-shadow:none}.rm-button--databank-fill{background:linear-gradient(180deg,rgba(224,67,128,.8),rgba(224,67,128,.9));border-color:#e04380;box-shadow:0 2px 3px 0 rgba(224,67,128,.5);color:var(--r-color-white)}.rm-button--databank-fill:hover:not(:disabled){background:linear-gradient(180deg,#e04380,#e04380);border-color:#e04380;box-shadow:none}.rm-button--databank-fill:active:not(:disabled){background:linear-gradient(180deg,#c73a6f,#c73a6f);box-shadow:none}.rm-button--databank-stroke{box-shadow:0 2px 3px 0 rgba(224,67,128,.5);color:#e04380}.rm-button--databank-stroke,.rm-button--databank-stroke:hover:not(:disabled){background:linear-gradient(180deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.9));border-color:#e04380}.rm-button--databank-stroke:hover:not(:disabled){box-shadow:none}.rm-button--databank-stroke:active:not(:disabled){background:rgba(224,67,128,.05);box-shadow:none}.rm-button--pharma-fill{background:linear-gradient(180deg,#ff7942,#f7520e);border-color:#f7520e;box-shadow:0 2px 3px 0 rgba(247,82,14,.5);color:var(--r-color-white)}.rm-button--pharma-fill:hover:not(:disabled){background:linear-gradient(180deg,#f7520e,#f7520e);border-color:#f7520e;box-shadow:none}.rm-button--pharma-fill:active:not(:disabled){background:linear-gradient(180deg,#d9470c,#d9470c);box-shadow:none}.rm-button--pharma-stroke{box-shadow:0 2px 3px 0 rgba(247,82,14,.5);color:#f7520e}.rm-button--pharma-stroke,.rm-button--pharma-stroke:hover:not(:disabled){background:linear-gradient(180deg,hsla(0,0%,100%,.5),hsla(0,0%,100%,.9));border-color:#f7520e}.rm-button--pharma-stroke:hover:not(:disabled){box-shadow:none}.rm-button--pharma-stroke:active:not(:disabled){background:rgba(247,82,14,.05);box-shadow:none}.rm-button--secondary{background-color:var(--r-color-gray-100);color:var(--r-color-gray-800)}.rm-button--secondary:hover:not(:disabled){background-color:var(--r-color-gray-200)}.rm-button--secondary:active:not(:disabled){background-color:var(--r-color-gray-300)}.rm-button--outline{background-color:transparent;border-color:var(--r-color-gray-300);color:var(--r-color-gray-700)}.rm-button--outline:hover:not(:disabled){background-color:var(--r-color-gray-50);border-color:var(--r-color-gray-400)}.rm-button--outline:active:not(:disabled){background-color:var(--r-color-gray-100)}.rm-button--ghost{background-color:transparent;color:var(--r-color-gray-700)}.rm-button--ghost:hover:not(:disabled){background-color:var(--r-color-gray-100)}.rm-button--ghost:active:not(:disabled){background-color:var(--r-color-gray-200)}.rm-button--block{width:100%}
|
|
46
54
|
</style>
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmChartCard - LE seul container de donnees autorise (chart, table, groupe
|
|
3
|
+
de KPI). Style : components.css (.chart-card*).
|
|
4
|
+
|
|
5
|
+
Le foot n'est pas une option decorative : c'est la premiere regle Critical
|
|
6
|
+
du design system. Toute donnee affichee porte l'organisme source ET la date
|
|
7
|
+
de notre verification. D'ou `source` et `verified` en props REQUISES : on ne
|
|
8
|
+
peut pas produire une carte sans dire d'ou vient la donnee ni quand on l'a
|
|
9
|
+
controlee. La date de la DONNEE (millesime, periode) vit dans le graphe ou
|
|
10
|
+
le tableau, elle ne se confond jamais avec la date de verification.
|
|
11
|
+
-->
|
|
12
|
+
<script setup lang="ts">
|
|
13
|
+
import { computed, ref, onMounted, onBeforeUnmount } from 'vue'
|
|
14
|
+
import { rmDate } from '../utils/format'
|
|
15
|
+
|
|
16
|
+
const props = withDefaults(defineProps<{
|
|
17
|
+
/** Titre de la carte. */
|
|
18
|
+
title: string
|
|
19
|
+
/** Sous-titre : perimetre, methode, aide a la lecture. */
|
|
20
|
+
context?: string
|
|
21
|
+
/** Organisme public : "HAS", "OCDE", "ANSM (Repertoire des specialites)". Jamais un outil interne. */
|
|
22
|
+
source: string
|
|
23
|
+
/** Date de notre verification. Un Date est formate en francais long ; une chaine deja formatee passe telle quelle. */
|
|
24
|
+
verified: Date | string | number
|
|
25
|
+
/** Complement de fin de foot : frequence, millesime. */
|
|
26
|
+
extra?: string
|
|
27
|
+
/** Masque le menu d'actions quand la carte n'a rien a exporter. */
|
|
28
|
+
actions?: boolean
|
|
29
|
+
}>(), {
|
|
30
|
+
context: undefined,
|
|
31
|
+
extra: undefined,
|
|
32
|
+
actions: true,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const emit = defineEmits<{
|
|
36
|
+
exportPng: []
|
|
37
|
+
exportCsv: []
|
|
38
|
+
exportXlsx: []
|
|
39
|
+
copyLink: []
|
|
40
|
+
fullscreen: []
|
|
41
|
+
}>()
|
|
42
|
+
|
|
43
|
+
const menuOpen = ref(false)
|
|
44
|
+
const root = ref<HTMLElement | null>(null)
|
|
45
|
+
|
|
46
|
+
// Une chaine deja formatee ("17 juin 2026") n'est pas parsable : on la garde
|
|
47
|
+
// telle quelle plutot que d'afficher "Invalid Date".
|
|
48
|
+
const verifiedText = computed(() => {
|
|
49
|
+
const parsed = new Date(props.verified)
|
|
50
|
+
return Number.isNaN(parsed.getTime()) ? String(props.verified) : rmDate(parsed)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
function onDocumentClick(event: MouseEvent) {
|
|
54
|
+
if (!menuOpen.value) return
|
|
55
|
+
if (root.value && !root.value.contains(event.target as Node)) menuOpen.value = false
|
|
56
|
+
}
|
|
57
|
+
function onEscape(event: KeyboardEvent) {
|
|
58
|
+
if (event.key === 'Escape') menuOpen.value = false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
onMounted(() => {
|
|
62
|
+
document.addEventListener('click', onDocumentClick)
|
|
63
|
+
document.addEventListener('keydown', onEscape)
|
|
64
|
+
})
|
|
65
|
+
onBeforeUnmount(() => {
|
|
66
|
+
document.removeEventListener('click', onDocumentClick)
|
|
67
|
+
document.removeEventListener('keydown', onEscape)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
function run(action: 'exportPng' | 'exportCsv' | 'exportXlsx' | 'copyLink' | 'fullscreen') {
|
|
71
|
+
menuOpen.value = false
|
|
72
|
+
emit(action)
|
|
73
|
+
}
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<template>
|
|
77
|
+
<div class="chart-card">
|
|
78
|
+
<div class="chart-card-head">
|
|
79
|
+
<p class="chart-card-title">{{ title }}</p>
|
|
80
|
+
<p v-if="context" class="chart-card-context">{{ context }}</p>
|
|
81
|
+
|
|
82
|
+
<div v-if="actions" ref="root" class="chart-card-actions" :class="{ open: menuOpen }">
|
|
83
|
+
<button
|
|
84
|
+
class="cc-export"
|
|
85
|
+
type="button"
|
|
86
|
+
aria-label="Actions sur cette carte"
|
|
87
|
+
:aria-expanded="menuOpen"
|
|
88
|
+
@click.stop="menuOpen = !menuOpen"
|
|
89
|
+
>
|
|
90
|
+
<RmIcon name="Gear" :size="14" />
|
|
91
|
+
</button>
|
|
92
|
+
<div class="cc-menu" role="menu">
|
|
93
|
+
<div class="cc-menu-section">Exporter</div>
|
|
94
|
+
<button class="cc-menu-item" type="button" role="menuitem" @click="run('exportPng')">
|
|
95
|
+
<RmIcon name="Image" :size="14" /> Image PNG
|
|
96
|
+
</button>
|
|
97
|
+
<button class="cc-menu-item" type="button" role="menuitem" @click="run('exportCsv')">
|
|
98
|
+
<RmIcon name="FileCsv" :size="14" /> Données CSV
|
|
99
|
+
</button>
|
|
100
|
+
<button class="cc-menu-item" type="button" role="menuitem" @click="run('exportXlsx')">
|
|
101
|
+
<RmIcon name="FileXls" :size="14" /> Excel (XLSX)
|
|
102
|
+
</button>
|
|
103
|
+
<div class="cc-menu-divider" />
|
|
104
|
+
<div class="cc-menu-section">Partager</div>
|
|
105
|
+
<button class="cc-menu-item" type="button" role="menuitem" @click="run('copyLink')">
|
|
106
|
+
<RmIcon name="Link" :size="14" /> Copier le lien direct
|
|
107
|
+
</button>
|
|
108
|
+
<button class="cc-menu-item" type="button" role="menuitem" @click="run('fullscreen')">
|
|
109
|
+
<RmIcon name="ArrowsOut" :size="14" /> Plein écran
|
|
110
|
+
</button>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="chart-card-body">
|
|
116
|
+
<slot />
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div class="chart-card-foot">
|
|
120
|
+
<span class="src">Source : <b>{{ source }}</b></span>
|
|
121
|
+
<span class="dot" />
|
|
122
|
+
<span class="updated">Vérifié le {{ verifiedText }}</span>
|
|
123
|
+
<template v-if="extra">
|
|
124
|
+
<span class="dot" />
|
|
125
|
+
<span>{{ extra }}</span>
|
|
126
|
+
</template>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</template>
|