@tekkare/romulus 0.1.52 → 0.1.54
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/runtime/assets/styles/components.css +1 -1
- package/dist/runtime/assets/styles/shell.css +1 -1
- package/dist/runtime/components/CardActions.vue +57 -9
- package/dist/runtime/components/FilterComplex.vue +71 -8
- package/dist/runtime/components/ResultCard.vue +10 -7
- package/dist/runtime/components/SidebarLeft.vue +48 -2
- package/dist/runtime/components/TableCard.vue +6 -6
- package/dist/runtime/composables/useRmCardActions.d.ts +12 -3
- package/dist/runtime/composables/useRmCardActions.js +28 -5
- package/dist/runtime/utils/filterOptions.d.ts +82 -0
- package/dist/runtime/utils/filterOptions.js +40 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -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}.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
|
+
.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:500}.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-card-zoom,.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}.rm-card-zoom{border:0;inset:0;margin:0;position:fixed;z-index:500}body.rm-card-zoom-open{overflow:hidden}.chart-card.rm-card-zoom .chart-card-body,.chart-card:fullscreen .chart-card-body{display:flex;flex-direction:column}.chart-card.rm-card-zoom .rm-chart,.chart-card:fullscreen .rm-chart{flex:1 1 auto}.chart-card.rm-card-zoom .echart,.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{--rm-shell-bg:#f9f9fa;--rm-shell-surface:#fff;--rm-shell-border:#dbdef0;--rm-shell-subtle:#f5f5f9;--rm-shell-text:#1e293b;--rm-shell-text-dark:#475569;--rm-shell-text-muted:#878ca6;--rm-shell-accent:#2017ad;--rm-shell-inset:8px;--rm-shell-radius:12px;--rm-bar-h:56px;--rm-nav-w:240px;--rm-nav-collapsed-w:64px;--rm-rail-w:56px;--rm-dots-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAcUlEQVR42u3TwQmAMAxAUdfQfUoSZ3OBTNMFHEqLVBEtCB6M8B8EevwktOsAAACAl0SkNzNXtaw6zut7SikNYeJq2Hksh4gs27rG7ZH+eWA7bttigMC78x4TIdBbceX8fJKnkSL1s5RY8zBxAAAA+LUF0ula10BxVDoAAAAASUVORK5CYII=");--rm-dots-size:12px;--rm-dots-opacity:0.1}body.night{--rm-shell-bg:var(--bg-app);--rm-shell-surface:var(--bg-surface,#1a1a24);--rm-shell-border:var(--border-default);--rm-shell-subtle:var(--bg-subtle,#22222e);--rm-shell-text:var(--text-primary);--rm-shell-text-dark:var(--text-secondary);--rm-shell-text-muted:var(--text-muted);--rm-shell-accent:var(--color-primary);--rm-dots-opacity:0.06}.rm-shell{background:var(--rm-shell-bg);color:var(--rm-shell-text);font-family:var(--font-sans,"Figtree",sans-serif);inset:0;overflow:hidden;position:fixed}.rm-shell__dots{background-image:var(--rm-dots-image);background-repeat:repeat;background-size:var(--rm-dots-size) var(--rm-dots-size);inset:0;opacity:var(--rm-dots-opacity);pointer-events:none;position:absolute}.rm-shell__frame{border:1px solid var(--rm-shell-border);border-radius:var(--rm-shell-radius);display:flex;inset:var(--rm-shell-inset);overflow:hidden;position:absolute}.rm-shell__body{display:flex;flex:1;flex-direction:column;min-width:0}.rm-shell__main{flex:1;min-width:0;overflow-y:auto}.rm-sb{background:var(--rm-shell-surface);display:flex;flex-direction:column;flex-shrink:0;height:100%}.rm-sb--left{border-right:1px solid var(--rm-shell-border);position:relative;transition:width .2s;width:var(--rm-nav-w);z-index:200}.rm-sb--left.rm-sb--collapsed{width:var(--rm-nav-collapsed-w)}.rm-sb--right{border-left:1px solid var(--rm-shell-border);width:var(--rm-rail-w)}.rm-sb__header{align-items:center;border-bottom:1px solid var(--rm-shell-border);display:flex;flex-shrink:0;gap:8px;height:var(--rm-bar-h);padding:0 16px}.rm-sb--collapsed .rm-sb__header{justify-content:center;padding:0}.rm-sb__logo{align-items:center;color:var(--rm-shell-text);display:flex;flex:1;font-size:20px;font-weight:700;gap:8px;line-height:1.4;min-width:0;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.rm-sb__toggle{-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:0;box-shadow:none;color:var(--rm-shell-text-muted);cursor:pointer;display:block;flex-shrink:0;height:20px;margin:0;padding:0;transition:color .15s;width:20px}.rm-sb__toggle:hover{color:var(--rm-shell-text)}.rm-sb__toggle svg{display:block;height:100%;width:100%}.rm-sb__nav{display:flex;flex:1;flex-direction:column;gap:16px;overflow-y:auto;padding:16px}.rm-sb--collapsed .rm-sb__nav{padding-left:8px;padding-right:8px}.rm-sb__group{display:flex;flex-direction:column;gap:4px}.rm-sb__label{align-items:center;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:0;box-shadow:none;color:var(--rm-shell-text-muted);display:flex;font-family:inherit;font-size:10px;font-weight:500;gap:4px;letter-spacing:.025em;line-height:1.6;margin:0;padding:0 2px;text-align:left;text-transform:uppercase;width:100%}button.rm-sb__label{cursor:pointer}button.rm-sb__label:hover{color:var(--rm-shell-text)}.rm-sb__label-text{flex:1;min-width:0}.rm-sb__chev{display:block;flex-shrink:0;height:12px;transition:transform .2s;width:12px}.rm-sb__label[aria-expanded=true] .rm-sb__chev{transform:rotate(90deg)}.rm-sb__rule{background:var(--rm-shell-border);height:1px;margin:4px}.rm-mi{align-items:center;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:4px;box-shadow:none;color:var(--rm-shell-text);cursor:pointer;display:flex;font-family:inherit;gap:8px;height:26px;margin:0;padding:2px;text-align:left;text-decoration:none;transition:background-color .15s;width:100%}.rm-mi:hover{background:color-mix(in srgb,var(--rm-shell-subtle) 50%,transparent)}.rm-mi.rm-mi--active{background:var(--rm-shell-subtle)}.rm-mi.rm-mi--current{background:color-mix(in srgb,var(--rm-shell-accent) 5%,transparent)}.rm-mi.rm-mi--current .rm-mi__icon{background:var(--rm-shell-accent);color:#fff}.rm-mi.rm-mi--current .rm-mi__icon svg{stroke:#fff}.rm-mi__icon{align-items:center;background:var(--rm-shell-subtle);border-radius:4px;color:var(--rm-shell-text-dark);display:flex;flex-shrink:0;height:22px;justify-content:center;line-height:1;width:22px}.rm-mi__icon svg{display:block;height:14px;width:14px}.rm-mi__icon i{font-size:14px;line-height:1}.rm-mi__label{color:var(--rm-shell-text);flex:1;font-size:14px;line-height:1.625;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rm-mi__badge{background:var(--rm-shell-subtle);border-radius:4px;color:var(--rm-shell-text-muted);flex-shrink:0;font-size:9px;font-weight:700;letter-spacing:.025em;padding:2px 4px;text-transform:uppercase}.rm-sb--collapsed .rm-mi{justify-content:center}.rm-sb--collapsed .rm-mi>:not(.rm-mi__icon),.rm-sb--collapsed .rm-sb__sub{display:none}.rm-sb__sub{display:flex;flex-direction:column;gap:2px;padding:2px 0 2px 28px}.rm-sb__sub-item{border-radius:4px;color:var(--rm-shell-text);display:block;font-size:14px;line-height:1.625;overflow:hidden;padding:2px 4px;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.rm-sb__sub-item:hover{background:color-mix(in srgb,var(--rm-shell-subtle) 50%,transparent)}.rm-rail__item{align-items:center;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:0;box-shadow:none;cursor:pointer;display:flex;flex-direction:column;font-family:inherit;gap:4px;margin:0;padding:12px 0 8px;text-decoration:none;width:100%}.rm-rail__avatar{align-items:center;border:2px solid var(--rm-shell-surface);border-radius:9999px;color:#fff;display:flex;font-size:14px;height:32px;justify-content:center;transition:opacity .15s;width:32px}.rm-rail__item:hover .rm-rail__avatar{opacity:.8}.rm-rail__tile{align-items:center;background:var(--rm-shell-subtle);border-radius:8px;color:var(--rm-shell-text-dark);display:flex;font-size:16px;height:32px;justify-content:center;transition:background-color .15s,color .15s;width:32px}.rm-rail__item:hover .rm-rail__tile{background:var(--rm-shell-border)}.rm-rail__item.rm-rail__item--active .rm-rail__tile{background:var(--rm-shell-accent);color:#fff}.rm-rail__tile{position:relative}.rm-rail__badge{background:var(--rm-shell-accent);border-radius:9999px;color:#fff;font-size:10px;font-weight:700;height:16px;line-height:16px;min-width:16px;padding:0 4px;position:absolute;right:-4px;text-align:center;top:-4px}.rm-rail__item--active .rm-rail__badge{background:#fff;color:var(--rm-shell-accent)}.rm-rail__caption{color:var(--rm-shell-text-muted);font-size:10px;line-height:1;max-width:100%;overflow:hidden;padding:0 2px;text-overflow:ellipsis;transition:color .15s;white-space:nowrap}.rm-rail__item--active .rm-rail__caption,.rm-rail__item:hover .rm-rail__caption{color:var(--rm-shell-text)}.rm-rail__bottom{display:flex;justify-content:center;margin-top:auto;padding-bottom:16px}.rm-lang{background:var(--rm-shell-subtle);border:1px solid var(--rm-shell-accent);border-radius:9999px;display:grid;gap:2px;grid-template-rows:1fr 1fr;padding:3px;position:relative}.rm-lang__thumb{background:var(--rm-shell-surface);border-radius:9999px;box-shadow:0 1px 4px rgba(30,41,59,.16);height:32px;left:3px;position:absolute;top:3px;transition:transform .26s cubic-bezier(.34,1.56,.64,1);width:32px}.rm-lang--second .rm-lang__thumb{transform:translateY(34px)}.rm-lang__option{align-items:center;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:9999px;box-shadow:none;cursor:pointer;display:flex;filter:grayscale(1);font-size:17px;height:32px;justify-content:center;line-height:1;margin:0;opacity:.5;padding:0;position:relative;transition:filter .18s ease,opacity .18s ease,transform .18s ease;width:32px}.rm-lang__option:hover{filter:grayscale(0);opacity:.85}.rm-lang__option.rm-lang__option--active{filter:grayscale(0);opacity:1}.rm-lang__option:focus-visible{outline:2px solid var(--rm-shell-accent);outline-offset:2px}@media (prefers-reduced-motion:reduce){.rm-lang__option,.rm-lang__thumb{transition:none}}.rm-hd{background:var(--rm-shell-surface);border-bottom:1px solid var(--rm-shell-border);display:flex;flex-direction:column;flex-shrink:0}.rm-hd__row{gap:16px;height:var(--rm-bar-h);padding:0 16px}.rm-hd__crumbs,.rm-hd__row{align-items:center;display:flex}.rm-hd__crumbs{flex:1;font-size:14px;gap:4px;min-width:0}.rm-hd__crumb{align-items:center;color:var(--rm-shell-text-muted);display:inline-flex;gap:6px;text-decoration:none;transition:color .15s;white-space:nowrap}a.rm-hd__crumb:hover{color:var(--rm-shell-text)}.rm-hd__sep{color:var(--rm-shell-text-muted)}.rm-hd__picker{display:inline-flex;min-width:0;position:relative}.rm-hd__crumb--picker{-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:4px;box-shadow:none;cursor:pointer;font:inherit;font-size:14px;margin:0;padding:2px 4px;transition:background-color .15s,color .15s}.rm-hd__crumb--picker:hover{background:var(--rm-shell-subtle)}.rm-hd__caret{display:block;flex-shrink:0;height:12px;opacity:.7;transition:transform .15s;width:12px}.rm-hd__caret--open{transform:rotate(180deg)}.rm-hd__menu{background:var(--rm-shell-surface);border:1px solid var(--rm-shell-border);border-radius:8px;box-shadow:0 8px 24px rgba(30,41,59,.12);left:0;max-height:60vh;max-width:320px;min-width:200px;overflow-y:auto;padding:4px;position:absolute;top:calc(100% + 4px);z-index:300}.rm-hd__menu-item{align-items:center;border-radius:4px;color:var(--rm-shell-text);display:flex;font-size:14px;gap:8px;line-height:1.5;overflow:hidden;padding:4px 6px;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.rm-hd__menu-item:hover{background:var(--rm-shell-subtle)}.rm-hd__menu-group{color:var(--rm-shell-text-muted);display:block;font-size:10px;font-weight:500;letter-spacing:.025em;line-height:1.6;padding:8px 6px 2px;text-transform:uppercase}.rm-hd__menu-group:first-child{padding-top:2px}.rm-hd__menu-item--active{background:color-mix(in srgb,var(--rm-shell-accent) 5%,transparent);font-weight:600}.rm-hd__menu-icon{align-items:center;background:var(--rm-shell-subtle);border-radius:4px;color:var(--rm-shell-text-dark);display:flex;flex-shrink:0;height:22px;justify-content:center;line-height:1;width:22px}.rm-hd__menu-icon i{font-size:14px;line-height:1}.rm-hd__menu-item--active .rm-hd__menu-icon{background:var(--rm-shell-accent);color:#fff}.rm-hd__crumb--last{color:var(--rm-shell-text);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rm-hd__search{align-items:center;background:var(--rm-shell-surface);border:1px solid var(--rm-shell-border);border-radius:9999px;display:flex;flex-shrink:0;gap:4px;padding:4px 12px;width:300px}.rm-hd__search input{background:transparent;border:none;color:var(--rm-shell-text);flex:1;font-family:inherit;font-size:14px;line-height:20px;min-width:0;outline:none;padding:0}.rm-hd__search input::-moz-placeholder{color:var(--rm-shell-text-muted)}.rm-hd__search input::placeholder{color:var(--rm-shell-text-muted)}.rm-hd__search i,.rm-hd__search svg{color:var(--rm-shell-text-muted);display:block;flex-shrink:0;font-size:14px;height:14px;line-height:20px;width:14px}.rm-hd__tabs{display:flex;gap:16px;padding:0 16px}.rm-hd__tab{align-items:flex-start;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-bottom:2px solid transparent;border-radius:0;box-shadow:none;color:var(--rm-shell-text-dark);cursor:pointer;display:flex;flex-direction:column;font-family:inherit;font-size:14px;font-weight:700;line-height:1.6;margin:0;padding:4px 4px 8px;white-space:nowrap}.rm-hd__tab--active{border-bottom-color:var(--rm-shell-accent);color:var(--rm-shell-text)}@media (max-width:767px){.rm-sb--left{inset:0 auto 0 0;position:absolute;transform:translateX(-100%);transition:transform .2s;z-index:40}.rm-sb--left.rm-sb--open{transform:translateX(0)}.rm-sb--right{display:none}.rm-hd__search{flex:1;width:auto}}.rm-panel{background:var(--rm-shell-surface);border-left:1px solid var(--rm-shell-border);bottom:0;box-shadow:-8px 0 24px rgba(30,41,59,.08);display:flex;flex-direction:column;position:absolute;right:0;top:0;width:var(--rm-panel-w,360px);z-index:401}.rm-panel__veil{backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);background:var(--rm-panel-veil,rgba(100,116,139,.12));cursor:default;inset:0;position:absolute;z-index:400}.rm-panel-veil-enter-active,.rm-panel-veil-leave-active{transition:opacity .22s ease}.rm-panel-veil-enter-from,.rm-panel-veil-leave-to{opacity:0}@media (prefers-reduced-motion:reduce){.rm-panel-veil-enter-active,.rm-panel-veil-leave-active{transition:none}}.rm-sb--right{position:relative;z-index:402}.rm-panel-enter-active,.rm-panel-leave-active,.rm-sb--right{transition:transform .22s cubic-bezier(.4,0,.2,1)}.rm-panel-enter-from,.rm-panel-leave-to{transform:translateX(100%)}@media (prefers-reduced-motion:reduce){.rm-panel-enter-active,.rm-panel-leave-active,.rm-sb--right{transition:none}}.rm-panel__header{align-items:center;border-bottom:1px solid var(--rm-shell-border);display:flex;flex-shrink:0;gap:8px;height:var(--rm-bar-h);padding:0 16px}.rm-panel__title{color:var(--rm-shell-text);flex:1;font-size:14px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rm-panel__close{-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:0;box-shadow:none;color:var(--rm-shell-text-muted);cursor:pointer;display:block;flex-shrink:0;height:20px;margin:0;padding:0;transition:color .15s;width:20px}.rm-panel__close:hover{color:var(--rm-shell-text)}.rm-panel__close svg{display:block;height:100%;width:100%}.rm-panel__body{flex:1;min-height:0;overflow-y:auto;padding:16px}@media (max-width:767px){.rm-panel{border-left:none;left:0;width:auto}}
|
|
1
|
+
:root{--rm-shell-bg:#f9f9fa;--rm-shell-surface:#fff;--rm-shell-border:#dbdef0;--rm-shell-subtle:#f5f5f9;--rm-shell-text:#1e293b;--rm-shell-text-dark:#475569;--rm-shell-text-muted:#878ca6;--rm-shell-accent:#2017ad;--rm-shell-inset:8px;--rm-shell-radius:12px;--rm-bar-h:56px;--rm-nav-w:240px;--rm-nav-collapsed-w:64px;--rm-rail-w:56px;--rm-dots-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAcUlEQVR42u3TwQmAMAxAUdfQfUoSZ3OBTNMFHEqLVBEtCB6M8B8EevwktOsAAACAl0SkNzNXtaw6zut7SikNYeJq2Hksh4gs27rG7ZH+eWA7bttigMC78x4TIdBbceX8fJKnkSL1s5RY8zBxAAAA+LUF0ula10BxVDoAAAAASUVORK5CYII=");--rm-dots-size:12px;--rm-dots-opacity:0.1}body.night{--rm-shell-bg:var(--bg-app);--rm-shell-surface:var(--bg-surface,#1a1a24);--rm-shell-border:var(--border-default);--rm-shell-subtle:var(--bg-subtle,#22222e);--rm-shell-text:var(--text-primary);--rm-shell-text-dark:var(--text-secondary);--rm-shell-text-muted:var(--text-muted);--rm-shell-accent:var(--color-primary);--rm-dots-opacity:0.06}.rm-shell{background:var(--rm-shell-bg);color:var(--rm-shell-text);font-family:var(--font-sans,"Figtree",sans-serif);inset:0;overflow:hidden;position:fixed}.rm-shell__dots{background-image:var(--rm-dots-image);background-repeat:repeat;background-size:var(--rm-dots-size) var(--rm-dots-size);inset:0;opacity:var(--rm-dots-opacity);pointer-events:none;position:absolute}.rm-shell__frame{border:1px solid var(--rm-shell-border);border-radius:var(--rm-shell-radius);display:flex;inset:var(--rm-shell-inset);overflow:hidden;position:absolute}.rm-shell__body{display:flex;flex:1;flex-direction:column;min-width:0}.rm-shell__main{flex:1;min-width:0;overflow-y:auto}.rm-sb{background:var(--rm-shell-surface);display:flex;flex-direction:column;flex-shrink:0;height:100%}.rm-sb--left{border-right:1px solid var(--rm-shell-border);position:relative;transition:width .2s;width:var(--rm-nav-w);z-index:200}.rm-sb--left.rm-sb--collapsed{width:var(--rm-nav-collapsed-w)}.rm-sb--right{border-left:1px solid var(--rm-shell-border);width:var(--rm-rail-w)}.rm-sb__header{align-items:center;border-bottom:1px solid var(--rm-shell-border);display:flex;flex-shrink:0;gap:8px;height:var(--rm-bar-h);padding:0 16px}.rm-sb--collapsed .rm-sb__header{justify-content:center;padding:0}.rm-sb__logo{align-items:center;color:var(--rm-shell-text);display:flex;flex:1;font-size:20px;font-weight:700;gap:8px;line-height:1.4;min-width:0;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.rm-sb__toggle{-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:0;box-shadow:none;color:var(--rm-shell-text-muted);cursor:pointer;display:block;flex-shrink:0;height:20px;margin:0;padding:0;transition:color .15s;width:20px}.rm-sb__toggle:hover{color:var(--rm-shell-text)}.rm-sb__toggle svg{display:block;height:100%;width:100%}.rm-sb__nav{display:flex;flex:1;flex-direction:column;gap:16px;overflow-y:auto;padding:16px}.rm-sb--collapsed .rm-sb__nav{padding-left:8px;padding-right:8px}.rm-sb__group{display:flex;flex-direction:column;gap:4px}.rm-sb__label{align-items:center;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:0;box-shadow:none;color:var(--rm-shell-text-muted);display:flex;font-family:inherit;font-size:10px;font-weight:500;gap:4px;letter-spacing:.025em;line-height:1.6;margin:0;padding:0 2px;text-align:left;text-transform:uppercase;width:100%}button.rm-sb__label{cursor:pointer}button.rm-sb__label:hover{color:var(--rm-shell-text)}.rm-sb__label-text{flex:1;min-width:0}.rm-sb__chev{display:block;flex-shrink:0;height:12px;transition:transform .2s;width:12px}.rm-sb__label[aria-expanded=true] .rm-sb__chev{transform:rotate(90deg)}.rm-sb__rule{background:var(--rm-shell-border);height:1px;margin:4px}.rm-mi{align-items:center;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:4px;box-shadow:none;color:var(--rm-shell-text);cursor:pointer;display:flex;font-family:inherit;gap:8px;height:26px;margin:0;padding:2px;text-align:left;text-decoration:none;transition:background-color .15s;width:100%}.rm-mi:hover{background:color-mix(in srgb,var(--rm-shell-subtle) 50%,transparent)}.rm-mi.rm-mi--active{background:var(--rm-shell-subtle)}.rm-mi.rm-mi--current{background:color-mix(in srgb,var(--rm-shell-accent) 5%,transparent)}.rm-mi.rm-mi--current .rm-mi__icon{background:var(--rm-shell-accent);color:#fff}.rm-mi.rm-mi--current .rm-mi__icon svg{stroke:#fff}.rm-mi__icon{align-items:center;background:var(--rm-shell-subtle);border-radius:4px;color:var(--rm-shell-text-dark);display:flex;flex-shrink:0;height:22px;justify-content:center;line-height:1;width:22px}.rm-mi__icon svg{display:block;height:14px;width:14px}.rm-mi__icon i{font-size:14px;line-height:1}.rm-mi__label{color:var(--rm-shell-text);flex:1;font-size:14px;line-height:1.625;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rm-mi__badge{background:var(--rm-shell-subtle);border-radius:4px;color:var(--rm-shell-text-muted);flex-shrink:0;font-size:9px;font-weight:700;letter-spacing:.025em;padding:2px 4px;text-transform:uppercase}.rm-sb--collapsed .rm-mi{justify-content:center}.rm-sb--collapsed .rm-mi>:not(.rm-mi__icon),.rm-sb--collapsed .rm-sb__sub{display:none}.rm-sb__tip{background:var(--rm-shell-surface);border:1px solid var(--rm-shell-border);border-radius:6px;box-shadow:0 4px 12px rgba(30,41,59,.1);color:var(--rm-shell-text);font-size:12px;line-height:1.3;padding:5px 9px;pointer-events:none;position:fixed;transform:translateY(-50%);white-space:nowrap;z-index:60}.rm-sb__sub{display:flex;flex-direction:column;gap:2px;padding:2px 0 2px 28px}.rm-sb__sub-item{border-radius:4px;color:var(--rm-shell-text);display:block;font-size:14px;line-height:1.625;overflow:hidden;padding:2px 4px;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.rm-sb__sub-item:hover{background:color-mix(in srgb,var(--rm-shell-subtle) 50%,transparent)}.rm-rail__item{align-items:center;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:0;box-shadow:none;cursor:pointer;display:flex;flex-direction:column;font-family:inherit;gap:4px;margin:0;padding:12px 0 8px;text-decoration:none;width:100%}.rm-rail__avatar{align-items:center;border:2px solid var(--rm-shell-surface);border-radius:9999px;color:#fff;display:flex;font-size:14px;height:32px;justify-content:center;transition:opacity .15s;width:32px}.rm-rail__item:hover .rm-rail__avatar{opacity:.8}.rm-rail__tile{align-items:center;background:var(--rm-shell-subtle);border-radius:8px;color:var(--rm-shell-text-dark);display:flex;font-size:16px;height:32px;justify-content:center;transition:background-color .15s,color .15s;width:32px}.rm-rail__item:hover .rm-rail__tile{background:var(--rm-shell-border)}.rm-rail__item.rm-rail__item--active .rm-rail__tile{background:var(--rm-shell-accent);color:#fff}.rm-rail__tile{position:relative}.rm-rail__badge{background:var(--rm-shell-accent);border-radius:9999px;color:#fff;font-size:10px;font-weight:700;height:16px;line-height:16px;min-width:16px;padding:0 4px;position:absolute;right:-4px;text-align:center;top:-4px}.rm-rail__item--active .rm-rail__badge{background:#fff;color:var(--rm-shell-accent)}.rm-rail__caption{color:var(--rm-shell-text-muted);font-size:10px;line-height:1;max-width:100%;overflow:hidden;padding:0 2px;text-overflow:ellipsis;transition:color .15s;white-space:nowrap}.rm-rail__item--active .rm-rail__caption,.rm-rail__item:hover .rm-rail__caption{color:var(--rm-shell-text)}.rm-rail__bottom{display:flex;justify-content:center;margin-top:auto;padding-bottom:16px}.rm-lang{background:var(--rm-shell-subtle);border:1px solid var(--rm-shell-accent);border-radius:9999px;display:grid;gap:2px;grid-template-rows:1fr 1fr;padding:3px;position:relative}.rm-lang__thumb{background:var(--rm-shell-surface);border-radius:9999px;box-shadow:0 1px 4px rgba(30,41,59,.16);height:32px;left:3px;position:absolute;top:3px;transition:transform .26s cubic-bezier(.34,1.56,.64,1);width:32px}.rm-lang--second .rm-lang__thumb{transform:translateY(34px)}.rm-lang__option{align-items:center;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:9999px;box-shadow:none;cursor:pointer;display:flex;filter:grayscale(1);font-size:17px;height:32px;justify-content:center;line-height:1;margin:0;opacity:.5;padding:0;position:relative;transition:filter .18s ease,opacity .18s ease,transform .18s ease;width:32px}.rm-lang__option:hover{filter:grayscale(0);opacity:.85}.rm-lang__option.rm-lang__option--active{filter:grayscale(0);opacity:1}.rm-lang__option:focus-visible{outline:2px solid var(--rm-shell-accent);outline-offset:2px}@media (prefers-reduced-motion:reduce){.rm-lang__option,.rm-lang__thumb{transition:none}}.rm-hd{background:var(--rm-shell-surface);border-bottom:1px solid var(--rm-shell-border);display:flex;flex-direction:column;flex-shrink:0}.rm-hd__row{gap:16px;height:var(--rm-bar-h);padding:0 16px}.rm-hd__crumbs,.rm-hd__row{align-items:center;display:flex}.rm-hd__crumbs{flex:1;font-size:14px;gap:4px;min-width:0}.rm-hd__crumb{align-items:center;color:var(--rm-shell-text-muted);display:inline-flex;gap:6px;text-decoration:none;transition:color .15s;white-space:nowrap}a.rm-hd__crumb:hover{color:var(--rm-shell-text)}.rm-hd__sep{color:var(--rm-shell-text-muted)}.rm-hd__picker{display:inline-flex;min-width:0;position:relative}.rm-hd__crumb--picker{-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:4px;box-shadow:none;cursor:pointer;font:inherit;font-size:14px;margin:0;padding:2px 4px;transition:background-color .15s,color .15s}.rm-hd__crumb--picker:hover{background:var(--rm-shell-subtle)}.rm-hd__caret{display:block;flex-shrink:0;height:12px;opacity:.7;transition:transform .15s;width:12px}.rm-hd__caret--open{transform:rotate(180deg)}.rm-hd__menu{background:var(--rm-shell-surface);border:1px solid var(--rm-shell-border);border-radius:8px;box-shadow:0 8px 24px rgba(30,41,59,.12);left:0;max-height:60vh;max-width:320px;min-width:200px;overflow-y:auto;padding:4px;position:absolute;top:calc(100% + 4px);z-index:300}.rm-hd__menu-item{align-items:center;border-radius:4px;color:var(--rm-shell-text);display:flex;font-size:14px;gap:8px;line-height:1.5;overflow:hidden;padding:4px 6px;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.rm-hd__menu-item:hover{background:var(--rm-shell-subtle)}.rm-hd__menu-group{color:var(--rm-shell-text-muted);display:block;font-size:10px;font-weight:500;letter-spacing:.025em;line-height:1.6;padding:8px 6px 2px;text-transform:uppercase}.rm-hd__menu-group:first-child{padding-top:2px}.rm-hd__menu-item--active{background:color-mix(in srgb,var(--rm-shell-accent) 5%,transparent);font-weight:600}.rm-hd__menu-icon{align-items:center;background:var(--rm-shell-subtle);border-radius:4px;color:var(--rm-shell-text-dark);display:flex;flex-shrink:0;height:22px;justify-content:center;line-height:1;width:22px}.rm-hd__menu-icon i{font-size:14px;line-height:1}.rm-hd__menu-item--active .rm-hd__menu-icon{background:var(--rm-shell-accent);color:#fff}.rm-hd__crumb--last{color:var(--rm-shell-text);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rm-hd__search{align-items:center;background:var(--rm-shell-surface);border:1px solid var(--rm-shell-border);border-radius:9999px;display:flex;flex-shrink:0;gap:4px;padding:4px 12px;width:300px}.rm-hd__search input{background:transparent;border:none;color:var(--rm-shell-text);flex:1;font-family:inherit;font-size:14px;line-height:20px;min-width:0;outline:none;padding:0}.rm-hd__search input::-moz-placeholder{color:var(--rm-shell-text-muted)}.rm-hd__search input::placeholder{color:var(--rm-shell-text-muted)}.rm-hd__search i,.rm-hd__search svg{color:var(--rm-shell-text-muted);display:block;flex-shrink:0;font-size:14px;height:14px;line-height:20px;width:14px}.rm-hd__tabs{display:flex;gap:16px;padding:0 16px}.rm-hd__tab{align-items:flex-start;-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-bottom:2px solid transparent;border-radius:0;box-shadow:none;color:var(--rm-shell-text-dark);cursor:pointer;display:flex;flex-direction:column;font-family:inherit;font-size:14px;font-weight:700;line-height:1.6;margin:0;padding:4px 4px 8px;white-space:nowrap}.rm-hd__tab--active{border-bottom-color:var(--rm-shell-accent);color:var(--rm-shell-text)}@media (max-width:767px){.rm-sb--left{inset:0 auto 0 0;position:absolute;transform:translateX(-100%);transition:transform .2s;z-index:40}.rm-sb--left.rm-sb--open{transform:translateX(0)}.rm-sb--right{display:none}.rm-hd__search{flex:1;width:auto}}.rm-panel{background:var(--rm-shell-surface);border-left:1px solid var(--rm-shell-border);bottom:0;box-shadow:-8px 0 24px rgba(30,41,59,.08);display:flex;flex-direction:column;position:absolute;right:0;top:0;width:var(--rm-panel-w,360px);z-index:401}.rm-panel__veil{backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);background:var(--rm-panel-veil,rgba(100,116,139,.12));cursor:default;inset:0;position:absolute;z-index:400}.rm-panel-veil-enter-active,.rm-panel-veil-leave-active{transition:opacity .22s ease}.rm-panel-veil-enter-from,.rm-panel-veil-leave-to{opacity:0}@media (prefers-reduced-motion:reduce){.rm-panel-veil-enter-active,.rm-panel-veil-leave-active{transition:none}}.rm-sb--right{position:relative;z-index:402}.rm-panel-enter-active,.rm-panel-leave-active,.rm-sb--right{transition:transform .22s cubic-bezier(.4,0,.2,1)}.rm-panel-enter-from,.rm-panel-leave-to{transform:translateX(100%)}@media (prefers-reduced-motion:reduce){.rm-panel-enter-active,.rm-panel-leave-active,.rm-sb--right{transition:none}}.rm-panel__header{align-items:center;border-bottom:1px solid var(--rm-shell-border);display:flex;flex-shrink:0;gap:8px;height:var(--rm-bar-h);padding:0 16px}.rm-panel__title{color:var(--rm-shell-text);flex:1;font-size:14px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rm-panel__close{-moz-appearance:none;appearance:none;-webkit-appearance:none;background:none;border:none;border-radius:0;box-shadow:none;color:var(--rm-shell-text-muted);cursor:pointer;display:block;flex-shrink:0;height:20px;margin:0;padding:0;transition:color .15s;width:20px}.rm-panel__close:hover{color:var(--rm-shell-text)}.rm-panel__close svg{display:block;height:100%;width:100%}.rm-panel__body{flex:1;min-height:0;overflow-y:auto;padding:16px}@media (max-width:767px){.rm-panel{border-left:none;left:0;width:auto}}
|
|
@@ -14,10 +14,16 @@
|
|
|
14
14
|
|
|
15
15
|
Les evenements sont emis EN PLUS de l'action, jamais a sa place : ils
|
|
16
16
|
servent au suivi (PostHog), pas a implementer l'export dans la page.
|
|
17
|
+
|
|
18
|
+
Le menu est pose en `position: fixed`, calcule a l'ouverture : les cartes
|
|
19
|
+
rognent ce qui depasse (`overflow: hidden`, sans quoi le fond du pied
|
|
20
|
+
deborderait des coins arrondis), et un menu en flux etait coupe par le bord
|
|
21
|
+
de la carte. Il se referme au defilement, faute de quoi il resterait plante
|
|
22
|
+
ou la carte n'est plus.
|
|
17
23
|
-->
|
|
18
24
|
<script setup lang="ts">
|
|
19
|
-
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
|
20
|
-
import type {
|
|
25
|
+
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
|
26
|
+
import type { RmExportPayload } from '../utils/export'
|
|
21
27
|
import { rmCardCharts, rmCardOf } from '../utils/cardData'
|
|
22
28
|
import { useRmCardActions } from '../composables/useRmCardActions'
|
|
23
29
|
|
|
@@ -29,10 +35,15 @@ const props = withDefaults(defineProps<{
|
|
|
29
35
|
/** Nom du fichier et de l'onglet. A defaut, le titre lu dans la carte. */
|
|
30
36
|
name?: string
|
|
31
37
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
38
|
+
* Ce que la page a de mieux que le rendu de la carte : ses onglets complets
|
|
39
|
+
* (`sheets`), mais aussi ses chiffres cles, ses sources, son sous-titre. Ce
|
|
40
|
+
* qui est fourni ici remplace ce que le DOM aurait donne, pour que l'export
|
|
41
|
+
* d'une carte se presente comme l'export global de la page.
|
|
42
|
+
*
|
|
43
|
+
* Une fonction est preferable des qu'une liste monte la meme carte vingt
|
|
44
|
+
* fois : elle n'est appelee qu'a l'export.
|
|
34
45
|
*/
|
|
35
|
-
|
|
46
|
+
payload?: Partial<RmExportPayload> | (() => Partial<RmExportPayload> | null | undefined)
|
|
36
47
|
/**
|
|
37
48
|
* Force les items visuels. Non renseigne : ils suivent la presence d'un
|
|
38
49
|
* graphe dans la carte, ce qui est le bon defaut dans les deux sens.
|
|
@@ -42,7 +53,7 @@ const props = withDefaults(defineProps<{
|
|
|
42
53
|
lang: 'fr',
|
|
43
54
|
link: undefined,
|
|
44
55
|
name: undefined,
|
|
45
|
-
|
|
56
|
+
payload: undefined,
|
|
46
57
|
visual: undefined,
|
|
47
58
|
})
|
|
48
59
|
|
|
@@ -91,7 +102,9 @@ const t = computed(() => WORDS[props.lang])
|
|
|
91
102
|
const { runCardAction, toggleCardFullscreen } = useRmCardActions()
|
|
92
103
|
|
|
93
104
|
const root = ref<HTMLElement | null>(null)
|
|
105
|
+
const menu = ref<HTMLElement | null>(null)
|
|
94
106
|
const menuOpen = ref(false)
|
|
107
|
+
const menuStyle = ref<Record<string, string>>({})
|
|
95
108
|
const busy = ref(false)
|
|
96
109
|
const note = ref('')
|
|
97
110
|
const hasChart = ref(false)
|
|
@@ -103,11 +116,38 @@ function cardEl(): Element | null {
|
|
|
103
116
|
return rmCardOf(root.value)
|
|
104
117
|
}
|
|
105
118
|
|
|
106
|
-
|
|
119
|
+
/** Marge minimale entre le menu et un bord de la fenetre. */
|
|
120
|
+
const EDGE = 8
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Place le menu sous le bouton, en coordonnees de fenetre. Il bascule au-dessus
|
|
124
|
+
* quand le bas manque : une carte en fin de page ouvrait un menu dont les
|
|
125
|
+
* derniers items tombaient hors de l'ecran.
|
|
126
|
+
*/
|
|
127
|
+
function place() {
|
|
128
|
+
const button = root.value?.querySelector('.cc-export')
|
|
129
|
+
const box = button?.getBoundingClientRect()
|
|
130
|
+
const size = menu.value?.getBoundingClientRect()
|
|
131
|
+
if (!box || !size) return
|
|
132
|
+
|
|
133
|
+
const left = Math.max(EDGE, Math.min(box.right - size.width, window.innerWidth - size.width - EDGE))
|
|
134
|
+
const below = box.bottom + 6
|
|
135
|
+
const above = box.top - size.height - 6
|
|
136
|
+
const top = below + size.height > window.innerHeight - EDGE && above > EDGE ? above : below
|
|
137
|
+
|
|
138
|
+
menuStyle.value = { position: 'fixed', left: `${Math.round(left)}px`, top: `${Math.round(top)}px`, right: 'auto' }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function toggleMenu() {
|
|
107
142
|
const card = cardEl()
|
|
108
143
|
hasChart.value = card ? rmCardCharts(card).length > 0 : false
|
|
109
144
|
note.value = ''
|
|
110
145
|
menuOpen.value = !menuOpen.value
|
|
146
|
+
if (!menuOpen.value) return
|
|
147
|
+
// Le menu doit etre rendu pour etre mesure : les items visuels viennent
|
|
148
|
+
// d'etre ajoutes ou retires, sa hauteur en depend.
|
|
149
|
+
await nextTick()
|
|
150
|
+
place()
|
|
111
151
|
}
|
|
112
152
|
|
|
113
153
|
/**
|
|
@@ -127,7 +167,7 @@ async function act(action: 'png' | 'svg' | 'csv' | 'xlsx' | 'link') {
|
|
|
127
167
|
lang: props.lang,
|
|
128
168
|
name: props.name,
|
|
129
169
|
link: props.link,
|
|
130
|
-
|
|
170
|
+
payload: props.payload,
|
|
131
171
|
})
|
|
132
172
|
if (action === 'link') {
|
|
133
173
|
// Le presse-papier ne se voit pas : sans un mot, rien ne distingue un
|
|
@@ -174,14 +214,22 @@ function onDocumentClick(event: MouseEvent) {
|
|
|
174
214
|
function onEscape(event: KeyboardEvent) {
|
|
175
215
|
if (event.key === 'Escape') menuOpen.value = false
|
|
176
216
|
}
|
|
217
|
+
/** Le menu est fixe : au defilement, il ne suit plus sa carte. */
|
|
218
|
+
function onScroll() {
|
|
219
|
+
if (menuOpen.value) menuOpen.value = false
|
|
220
|
+
}
|
|
177
221
|
|
|
178
222
|
onMounted(() => {
|
|
179
223
|
document.addEventListener('click', onDocumentClick)
|
|
180
224
|
document.addEventListener('keydown', onEscape)
|
|
225
|
+
window.addEventListener('scroll', onScroll, true)
|
|
226
|
+
window.addEventListener('resize', onScroll)
|
|
181
227
|
})
|
|
182
228
|
onBeforeUnmount(() => {
|
|
183
229
|
document.removeEventListener('click', onDocumentClick)
|
|
184
230
|
document.removeEventListener('keydown', onEscape)
|
|
231
|
+
window.removeEventListener('scroll', onScroll, true)
|
|
232
|
+
window.removeEventListener('resize', onScroll)
|
|
185
233
|
})
|
|
186
234
|
</script>
|
|
187
235
|
|
|
@@ -199,7 +247,7 @@ onBeforeUnmount(() => {
|
|
|
199
247
|
<RmIcon name="Gear" :size="14" />
|
|
200
248
|
</button>
|
|
201
249
|
|
|
202
|
-
<div class="cc-menu" role="menu">
|
|
250
|
+
<div ref="menu" class="cc-menu" role="menu" :style="menuStyle">
|
|
203
251
|
<div class="cc-menu-section">{{ t.exportSection }}</div>
|
|
204
252
|
<button v-if="showVisual" class="cc-menu-item" type="button" role="menuitem" :disabled="busy" @click="act('png')">
|
|
205
253
|
<RmIcon name="Image" :size="14" /> {{ t.png }}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
France, UK +3 » se lit sans ouvrir le panneau, « Pays (5) » non.
|
|
15
15
|
-->
|
|
16
16
|
<script setup lang="ts">
|
|
17
|
-
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
17
|
+
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
18
18
|
import {
|
|
19
19
|
PhCaretDown,
|
|
20
20
|
PhMagnifyingGlass,
|
|
@@ -71,6 +71,33 @@ const isOpen = ref(false)
|
|
|
71
71
|
const wrapperRef = ref<HTMLElement>()
|
|
72
72
|
const delSearch = ref('')
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Saisie de la colonne d'ajout, telle qu'elle est tapee. En mode `remote`,
|
|
76
|
+
* chaque frappe declencherait une requete : « pembro » en lancerait six, et
|
|
77
|
+
* l'utilisateur verrait la liste d'origine tant que la derniere n'est pas
|
|
78
|
+
* revenue, ce qui se lit comme une recherche qui ne marche pas. Le champ reste
|
|
79
|
+
* donc immediat, et le modele que l'appelant interroge ne bouge qu'une fois la
|
|
80
|
+
* frappe posee.
|
|
81
|
+
*/
|
|
82
|
+
const REMOTE_DEBOUNCE = 250
|
|
83
|
+
const addInput = ref(addSearch.value)
|
|
84
|
+
let debounce: ReturnType<typeof setTimeout> | undefined
|
|
85
|
+
|
|
86
|
+
// La saisie peut aussi etre remise a zero de l'exterieur (fermeture du
|
|
87
|
+
// panneau) : le champ suit.
|
|
88
|
+
watch(addSearch, (value) => {
|
|
89
|
+
if (value !== addInput.value) addInput.value = value
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
watch(addInput, (value) => {
|
|
93
|
+
if (!props.remote) {
|
|
94
|
+
addSearch.value = value
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
clearTimeout(debounce)
|
|
98
|
+
debounce = setTimeout(() => { addSearch.value = value }, REMOTE_DEBOUNCE)
|
|
99
|
+
})
|
|
100
|
+
|
|
74
101
|
/** Le chrome du panneau, dans les deux langues. */
|
|
75
102
|
const WORDS = {
|
|
76
103
|
fr: {
|
|
@@ -84,6 +111,7 @@ const WORDS = {
|
|
|
84
111
|
searchSelection: 'Filtrer la sélection...',
|
|
85
112
|
noResult: 'Aucun résultat',
|
|
86
113
|
noSelection: 'Aucune sélection',
|
|
114
|
+
more: 'valeurs de plus, faites défiler',
|
|
87
115
|
},
|
|
88
116
|
en: {
|
|
89
117
|
quick: 'Quick selection',
|
|
@@ -96,6 +124,7 @@ const WORDS = {
|
|
|
96
124
|
searchSelection: 'Filter selection...',
|
|
97
125
|
noResult: 'No results',
|
|
98
126
|
noSelection: 'No selection',
|
|
127
|
+
more: 'more values, keep scrolling',
|
|
99
128
|
},
|
|
100
129
|
} as const
|
|
101
130
|
|
|
@@ -113,6 +142,32 @@ const filteredAvailable = computed(() => {
|
|
|
113
142
|
return availableOptions.value.filter(o => o.label.toLowerCase().includes(q))
|
|
114
143
|
})
|
|
115
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Rendu par tranches. Un referentiel entier (cinq mille marques) pose autant
|
|
147
|
+
* de boutons dans le DOM : le panneau mettait plus d'une seconde a s'ouvrir.
|
|
148
|
+
* On affiche donc les premieres valeurs, et la suite arrive quand on descend.
|
|
149
|
+
* Rien n'est cache : la recherche, elle, travaille sur la liste complete.
|
|
150
|
+
*/
|
|
151
|
+
const CHUNK = 300
|
|
152
|
+
const shown = ref(CHUNK)
|
|
153
|
+
|
|
154
|
+
const visibleAvailable = computed(() => filteredAvailable.value.slice(0, shown.value))
|
|
155
|
+
const hiddenCount = computed(() => Math.max(0, filteredAvailable.value.length - shown.value))
|
|
156
|
+
|
|
157
|
+
// Toute nouvelle liste repart du haut : sinon une recherche affiche encore les
|
|
158
|
+
// tranches ouvertes pour la precedente.
|
|
159
|
+
watch(filteredAvailable, () => { shown.value = CHUNK })
|
|
160
|
+
|
|
161
|
+
function onListScroll(event: Event) {
|
|
162
|
+
if (hiddenCount.value === 0) return
|
|
163
|
+
const el = event.target as HTMLElement
|
|
164
|
+
// Une hauteur d'ecran avant le bas : la tranche suivante est prete quand on
|
|
165
|
+
// y arrive.
|
|
166
|
+
if (el.scrollTop + el.clientHeight >= el.scrollHeight - el.clientHeight) {
|
|
167
|
+
shown.value += CHUNK
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
116
171
|
/**
|
|
117
172
|
* La selection se lit sur `selected`, pas sur `options` : une valeur choisie
|
|
118
173
|
* puis sortie de la liste (recherche suivante, referentiel pagine) doit rester
|
|
@@ -196,6 +251,7 @@ function clearAll() {
|
|
|
196
251
|
function toggle() {
|
|
197
252
|
isOpen.value = !isOpen.value
|
|
198
253
|
if (!isOpen.value) {
|
|
254
|
+
addInput.value = ''
|
|
199
255
|
addSearch.value = ''
|
|
200
256
|
delSearch.value = ''
|
|
201
257
|
}
|
|
@@ -204,13 +260,17 @@ function toggle() {
|
|
|
204
260
|
function onClickOutside(e: Event) {
|
|
205
261
|
if (wrapperRef.value && !wrapperRef.value.contains(e.target as Node)) {
|
|
206
262
|
isOpen.value = false
|
|
263
|
+
addInput.value = ''
|
|
207
264
|
addSearch.value = ''
|
|
208
265
|
delSearch.value = ''
|
|
209
266
|
}
|
|
210
267
|
}
|
|
211
268
|
|
|
212
269
|
onMounted(() => document.addEventListener('click', onClickOutside))
|
|
213
|
-
onUnmounted(() =>
|
|
270
|
+
onUnmounted(() => {
|
|
271
|
+
clearTimeout(debounce)
|
|
272
|
+
document.removeEventListener('click', onClickOutside)
|
|
273
|
+
})
|
|
214
274
|
</script>
|
|
215
275
|
|
|
216
276
|
<template>
|
|
@@ -268,22 +328,22 @@ onUnmounted(() => document.removeEventListener('click', onClickOutside))
|
|
|
268
328
|
<div class="filter-complex__search">
|
|
269
329
|
<PhMagnifyingGlass :size="14" color="var(--text-muted)" />
|
|
270
330
|
<input
|
|
271
|
-
v-model="
|
|
331
|
+
v-model="addInput"
|
|
272
332
|
type="text"
|
|
273
333
|
:placeholder="t.searchAdd"
|
|
274
334
|
class="filter-complex__search-input"
|
|
275
335
|
>
|
|
276
336
|
<PhX
|
|
277
|
-
v-if="
|
|
337
|
+
v-if="addInput"
|
|
278
338
|
:size="12"
|
|
279
339
|
class="filter-complex__search-clear"
|
|
280
|
-
@click="
|
|
340
|
+
@click="addInput = ''"
|
|
281
341
|
/>
|
|
282
342
|
</div>
|
|
283
343
|
|
|
284
|
-
<div class="filter-complex__list">
|
|
344
|
+
<div class="filter-complex__list" @scroll.passive="onListScroll">
|
|
285
345
|
<button
|
|
286
|
-
v-for="option in
|
|
346
|
+
v-for="option in visibleAvailable"
|
|
287
347
|
:key="option.value"
|
|
288
348
|
class="filter-complex__item"
|
|
289
349
|
@click="addOption(option.value)"
|
|
@@ -298,6 +358,9 @@ onUnmounted(() => document.removeEventListener('click', onClickOutside))
|
|
|
298
358
|
<p v-if="filteredAvailable.length === 0" class="filter-complex__empty">
|
|
299
359
|
{{ t.noResult }}
|
|
300
360
|
</p>
|
|
361
|
+
<p v-else-if="hiddenCount > 0" class="filter-complex__more">
|
|
362
|
+
{{ hiddenCount }} {{ t.more }}
|
|
363
|
+
</p>
|
|
301
364
|
</div>
|
|
302
365
|
</div>
|
|
303
366
|
|
|
@@ -358,5 +421,5 @@ onUnmounted(() => document.removeEventListener('click', onClickOutside))
|
|
|
358
421
|
</template>
|
|
359
422
|
|
|
360
423
|
<style scoped>
|
|
361
|
-
.filter-complex{position:relative}.filter-complex__trigger{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-pill);box-sizing:border-box;color:var(--text-primary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);font-weight:var(--weight-medium);gap:6px;height:var(--rm-fbar-field-h,32px);max-width:320px;padding:0 12px;transition:all var(--duration-fast) var(--ease-in-out);white-space:nowrap}.filter-complex__trigger:hover{background:var(--bg-surface-3)}.filter-complex__icon{color:var(--color-primary);flex-shrink:0}.filter-complex__trigger--active{border-color:var(--color-primary)}.filter-complex__label{color:var(--text-muted);flex-shrink:0;font-weight:var(--weight-normal)}.filter-complex__summary{font-weight:var(--weight-semibold);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.filter-complex__badge{align-items:center;background-color:color-mix(in srgb,var(--color-primary) 10%,transparent);border-radius:var(--radius-pill);color:var(--color-primary);display:inline-flex;flex-shrink:0;font-size:var(--font-12);font-weight:var(--weight-semibold);height:18px;justify-content:center;line-height:1;min-width:18px;padding:0 6px}.filter-complex__caret{color:var(--text-muted);flex-shrink:0;transition:transform var(--duration-fast) var(--ease-in-out)}.filter-complex__caret--open{transform:rotate(180deg)}.filter-complex__panel{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-xl,12px);box-shadow:0 4px 24px rgba(var(--shadow-rgb),.1),0 2px 8px rgba(var(--shadow-rgb),.05);display:flex;left:0;max-height:460px;max-width:calc(100vw - 32px);overflow:hidden;position:absolute;top:calc(100% + 6px);width:720px;z-index:50}.filter-complex__left,.filter-complex__right{display:flex;flex-direction:column;gap:10px;overflow:hidden;padding:20px}.filter-complex__left{flex:1 1 50%}.filter-complex__right{border-left:1px solid var(--border-default);flex:1 1 50%}.filter-complex__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.filter-complex__title{color:var(--text-primary);font-size:var(--font-14);font-weight:var(--weight-bold);margin:0}.filter-complex__presets{display:flex;flex-wrap:wrap;gap:8px}.filter-complex__preset{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-pill);color:var(--text-primary);cursor:pointer;font-family:var(--font-sans);font-size:var(--font-12);font-weight:var(--weight-medium);height:26px;padding:0 12px;transition:all var(--duration-fast) var(--ease-in-out);white-space:nowrap}.filter-complex__preset:hover{border-color:var(--color-primary);color:var(--color-primary)}.filter-complex__preset.is-active{background:var(--color-primary);border-color:var(--color-primary);color:var(--color-text-inverse)}.filter-complex__search{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-md,8px);display:flex;gap:8px;padding:7px 10px}.filter-complex__search:focus-within{border-color:var(--color-primary)}.filter-complex__search-input{background:transparent;border:none;color:var(--text-primary);flex:1;font-family:var(--font-sans);font-size:var(--font-14);outline:none}.filter-complex__search-input::-moz-placeholder{color:var(--text-muted)}.filter-complex__search-input::placeholder{color:var(--text-muted)}.filter-complex__search-clear{color:var(--text-muted);cursor:pointer;flex-shrink:0}.filter-complex__search-clear:hover{color:var(--text-secondary)}.filter-complex__action{background:none;border:none;cursor:pointer;font-family:var(--font-sans);font-size:var(--font-12);font-weight:var(--weight-medium);padding:0;text-decoration:underline;white-space:nowrap}.filter-complex__action--add{color:var(--color-primary)}.filter-complex__action--add:hover{color:var(--color-primary-hover)}.filter-complex__action--clear{color:var(--color-danger)}.filter-complex__list{display:flex;flex:1;flex-direction:column;overflow-y:auto}.filter-complex__list::-webkit-scrollbar{width:4px}.filter-complex__list::-webkit-scrollbar-thumb{background:var(--border-default);border-radius:100px}.filter-complex__list::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}.filter-complex__item{align-items:center;background:none;border:none;border-radius:var(--radius-md,8px);color:var(--text-primary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);gap:10px;padding:7px 8px;text-align:left;transition:background-color var(--duration-fast) var(--ease-in-out)}.filter-complex__item:hover{background-color:var(--bg-surface-3)}.filter-complex__item-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.filter-complex__item-icon{flex-shrink:0}.filter-complex__item-icon--add{color:var(--color-primary)}.filter-complex__item-icon--remove{color:var(--color-danger)}.filter-complex__empty{color:var(--text-muted);font-size:var(--font-14);font-style:italic;padding:8px}.dropdown-enter-active,.dropdown-leave-active{transition:opacity var(--duration-fast) var(--ease-in-out),transform var(--duration-fast) var(--ease-in-out)}.dropdown-enter-from,.dropdown-leave-to{opacity:0;transform:translateY(-4px)}
|
|
424
|
+
.filter-complex{position:relative}.filter-complex__trigger{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-pill);box-sizing:border-box;color:var(--text-primary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);font-weight:var(--weight-medium);gap:6px;height:var(--rm-fbar-field-h,32px);max-width:320px;padding:0 12px;transition:all var(--duration-fast) var(--ease-in-out);white-space:nowrap}.filter-complex__trigger:hover{background:var(--bg-surface-3)}.filter-complex__icon{color:var(--color-primary);flex-shrink:0}.filter-complex__trigger--active{border-color:var(--color-primary)}.filter-complex__label{color:var(--text-muted);flex-shrink:0;font-weight:var(--weight-normal)}.filter-complex__summary{font-weight:var(--weight-semibold);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.filter-complex__badge{align-items:center;background-color:color-mix(in srgb,var(--color-primary) 10%,transparent);border-radius:var(--radius-pill);color:var(--color-primary);display:inline-flex;flex-shrink:0;font-size:var(--font-12);font-weight:var(--weight-semibold);height:18px;justify-content:center;line-height:1;min-width:18px;padding:0 6px}.filter-complex__caret{color:var(--text-muted);flex-shrink:0;transition:transform var(--duration-fast) var(--ease-in-out)}.filter-complex__caret--open{transform:rotate(180deg)}.filter-complex__panel{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-xl,12px);box-shadow:0 4px 24px rgba(var(--shadow-rgb),.1),0 2px 8px rgba(var(--shadow-rgb),.05);display:flex;left:0;max-height:460px;max-width:calc(100vw - 32px);overflow:hidden;position:absolute;top:calc(100% + 6px);width:720px;z-index:50}.filter-complex__left,.filter-complex__right{display:flex;flex-direction:column;gap:10px;overflow:hidden;padding:20px}.filter-complex__left{flex:1 1 50%}.filter-complex__right{border-left:1px solid var(--border-default);flex:1 1 50%}.filter-complex__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.filter-complex__title{color:var(--text-primary);font-size:var(--font-14);font-weight:var(--weight-bold);margin:0}.filter-complex__presets{display:flex;flex-wrap:wrap;gap:8px}.filter-complex__preset{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-pill);color:var(--text-primary);cursor:pointer;font-family:var(--font-sans);font-size:var(--font-12);font-weight:var(--weight-medium);height:26px;padding:0 12px;transition:all var(--duration-fast) var(--ease-in-out);white-space:nowrap}.filter-complex__preset:hover{border-color:var(--color-primary);color:var(--color-primary)}.filter-complex__preset.is-active{background:var(--color-primary);border-color:var(--color-primary);color:var(--color-text-inverse)}.filter-complex__search{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-md,8px);display:flex;gap:8px;padding:7px 10px}.filter-complex__search:focus-within{border-color:var(--color-primary)}.filter-complex__search-input{background:transparent;border:none;color:var(--text-primary);flex:1;font-family:var(--font-sans);font-size:var(--font-14);outline:none}.filter-complex__search-input::-moz-placeholder{color:var(--text-muted)}.filter-complex__search-input::placeholder{color:var(--text-muted)}.filter-complex__search-clear{color:var(--text-muted);cursor:pointer;flex-shrink:0}.filter-complex__search-clear:hover{color:var(--text-secondary)}.filter-complex__action{background:none;border:none;cursor:pointer;font-family:var(--font-sans);font-size:var(--font-12);font-weight:var(--weight-medium);padding:0;text-decoration:underline;white-space:nowrap}.filter-complex__action--add{color:var(--color-primary)}.filter-complex__action--add:hover{color:var(--color-primary-hover)}.filter-complex__action--clear{color:var(--color-danger)}.filter-complex__list{display:flex;flex:1;flex-direction:column;overflow-y:auto}.filter-complex__list::-webkit-scrollbar{width:4px}.filter-complex__list::-webkit-scrollbar-thumb{background:var(--border-default);border-radius:100px}.filter-complex__list::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}.filter-complex__item{align-items:center;background:none;border:none;border-radius:var(--radius-md,8px);color:var(--text-primary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:var(--font-14);gap:10px;padding:7px 8px;text-align:left;transition:background-color var(--duration-fast) var(--ease-in-out)}.filter-complex__item:hover{background-color:var(--bg-surface-3)}.filter-complex__item-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.filter-complex__item-icon{flex-shrink:0}.filter-complex__item-icon--add{color:var(--color-primary)}.filter-complex__item-icon--remove{color:var(--color-danger)}.filter-complex__more{color:var(--text-muted);font-size:var(--font-12);padding:8px}.filter-complex__empty{color:var(--text-muted);font-size:var(--font-14);font-style:italic;padding:8px}.dropdown-enter-active,.dropdown-leave-active{transition:opacity var(--duration-fast) var(--ease-in-out),transform var(--duration-fast) var(--ease-in-out)}.dropdown-enter-from,.dropdown-leave-to{opacity:0;transform:translateY(-4px)}
|
|
362
425
|
</style>
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
.rm-rcard__desc description 14px, gris
|
|
31
31
|
-->
|
|
32
32
|
<script setup lang="ts">
|
|
33
|
-
import type {
|
|
33
|
+
import type { RmExportPayload } from '../utils/export'
|
|
34
34
|
|
|
35
35
|
withDefaults(
|
|
36
36
|
defineProps<{
|
|
@@ -59,11 +59,14 @@ withDefaults(
|
|
|
59
59
|
/** Langue du menu d'actions. Le contenu projete garde la sienne. */
|
|
60
60
|
lang?: 'fr' | 'en'
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* dans la carte.
|
|
62
|
+
* Ce que la page a de mieux que le rendu de la carte : ses onglets
|
|
63
|
+
* complets (les cinq avis derriere le compteur), ses chiffres cles, ses
|
|
64
|
+
* sources. A defaut, les champs lus dans la carte.
|
|
65
|
+
*
|
|
66
|
+
* Une fonction est preferable dans une liste : elle n'est appelee qu'a
|
|
67
|
+
* l'export, alors qu'un objet est recalcule a chaque rendu de la carte.
|
|
65
68
|
*/
|
|
66
|
-
|
|
69
|
+
payload?: Partial<RmExportPayload> | (() => Partial<RmExportPayload> | null | undefined)
|
|
67
70
|
}>(),
|
|
68
71
|
{
|
|
69
72
|
count: null,
|
|
@@ -74,7 +77,7 @@ withDefaults(
|
|
|
74
77
|
actions: true,
|
|
75
78
|
link: undefined,
|
|
76
79
|
lang: 'fr',
|
|
77
|
-
|
|
80
|
+
payload: undefined,
|
|
78
81
|
},
|
|
79
82
|
)
|
|
80
83
|
</script>
|
|
@@ -92,7 +95,7 @@ withDefaults(
|
|
|
92
95
|
v-if="actions"
|
|
93
96
|
:lang="lang"
|
|
94
97
|
:link="link"
|
|
95
|
-
:
|
|
98
|
+
:payload="payload"
|
|
96
99
|
:visual="false"
|
|
97
100
|
/>
|
|
98
101
|
</div>
|
|
@@ -138,6 +138,36 @@ const isCollapsible = (s: RmNavSection) => !!s.label && s.collapsible !== false
|
|
|
138
138
|
const isOpen = (s: RmNavSection) =>
|
|
139
139
|
!isCollapsible(s) || props.collapsed || openSection.value === s.key
|
|
140
140
|
const isActive = (item: RmNavItem) => item.active ?? (!!item.to && item.to === props.activePath)
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Infobulle du rail replie.
|
|
144
|
+
*
|
|
145
|
+
* Replie, il ne reste que l'icone. Le `title` du navigateur nommait deja
|
|
146
|
+
* l'entree, mais il se fait attendre une seconde environ, et ce delai n'est
|
|
147
|
+
* pas reglable : parcourir un rail de vingt icones devenait une suite
|
|
148
|
+
* d'attentes. Celle-ci sort a l'entree du pointeur, sans delai.
|
|
149
|
+
*
|
|
150
|
+
* Elle est posee en `position: fixed`, hors de `.rm-sb__nav` : ce dernier
|
|
151
|
+
* defile (`overflow-y: auto`), ce qui force `overflow-x` a `auto` lui aussi
|
|
152
|
+
* et rognait toute bulle debordant du rail de 64px. Ses coordonnees viennent
|
|
153
|
+
* de l'entree survolee plutot que de la largeur du rail, pour rester justes
|
|
154
|
+
* quel que soit le placement de la barre.
|
|
155
|
+
*/
|
|
156
|
+
const tip = ref<{ label: string; x: number; y: number } | null>(null)
|
|
157
|
+
|
|
158
|
+
function showTip(event: Event, item: RmNavItem) {
|
|
159
|
+
if (!props.collapsed) return
|
|
160
|
+
const box = (event.currentTarget as HTMLElement | null)?.getBoundingClientRect()
|
|
161
|
+
if (!box) return
|
|
162
|
+
tip.value = { label: item.label, x: box.right + 8, y: box.top + box.height / 2 }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function hideTip() {
|
|
166
|
+
tip.value = null
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Deplier la barre pendant un survol laisserait la bulle seule a l'ecran.
|
|
170
|
+
watch(() => props.collapsed, hideTip)
|
|
141
171
|
</script>
|
|
142
172
|
|
|
143
173
|
<template>
|
|
@@ -210,7 +240,11 @@ const isActive = (item: RmNavItem) => item.active ?? (!!item.to && item.to === p
|
|
|
210
240
|
target="_blank"
|
|
211
241
|
rel="noopener noreferrer"
|
|
212
242
|
class="rm-mi"
|
|
213
|
-
:title="item.label"
|
|
243
|
+
:title="collapsed ? undefined : item.label"
|
|
244
|
+
@mouseenter="showTip($event, item)"
|
|
245
|
+
@mouseleave="hideTip"
|
|
246
|
+
@focus="showTip($event, item)"
|
|
247
|
+
@blur="hideTip"
|
|
214
248
|
>
|
|
215
249
|
<span class="rm-mi__icon"><i v-if="item.icon" :class="`ph ph-${item.icon}`" /></span>
|
|
216
250
|
<span class="rm-mi__label">{{ item.label }}</span>
|
|
@@ -225,9 +259,13 @@ const isActive = (item: RmNavItem) => item.active ?? (!!item.to && item.to === p
|
|
|
225
259
|
:to="item.to"
|
|
226
260
|
class="rm-mi"
|
|
227
261
|
:class="{ 'rm-mi--active': isActive(item) }"
|
|
228
|
-
:title="item.label"
|
|
262
|
+
:title="collapsed ? undefined : item.label"
|
|
229
263
|
:aria-current="isActive(item) ? 'page' : undefined"
|
|
230
264
|
@click="item.to ? emit('navigate', item.to) : undefined"
|
|
265
|
+
@mouseenter="showTip($event, item)"
|
|
266
|
+
@mouseleave="hideTip"
|
|
267
|
+
@focus="showTip($event, item)"
|
|
268
|
+
@blur="hideTip"
|
|
231
269
|
>
|
|
232
270
|
<span class="rm-mi__icon"><i v-if="item.icon" :class="`ph ph-${item.icon}`" /></span>
|
|
233
271
|
<span class="rm-mi__label">{{ item.label }}</span>
|
|
@@ -241,5 +279,13 @@ const isActive = (item: RmNavItem) => item.active ?? (!!item.to && item.to === p
|
|
|
241
279
|
</nav>
|
|
242
280
|
|
|
243
281
|
<slot name="footer" />
|
|
282
|
+
|
|
283
|
+
<!-- Hors de `.rm-sb__nav`, qui defile et rognerait la bulle. -->
|
|
284
|
+
<div
|
|
285
|
+
v-if="tip"
|
|
286
|
+
class="rm-sb__tip"
|
|
287
|
+
role="tooltip"
|
|
288
|
+
:style="{ left: `${tip.x}px`, top: `${tip.y}px` }"
|
|
289
|
+
>{{ tip.label }}</div>
|
|
244
290
|
</aside>
|
|
245
291
|
</template>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
pagination).
|
|
10
10
|
-->
|
|
11
11
|
<script setup lang="ts">
|
|
12
|
-
import type {
|
|
12
|
+
import type { RmExportPayload } from '../utils/export'
|
|
13
13
|
|
|
14
14
|
withDefaults(
|
|
15
15
|
defineProps<{
|
|
@@ -24,12 +24,12 @@ withDefaults(
|
|
|
24
24
|
/** Langue du menu d'actions. Le contenu projete garde la sienne. */
|
|
25
25
|
lang?: 'fr' | 'en'
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
* la
|
|
27
|
+
* Ce que la page a de mieux que le rendu : la donnee complete plutot que
|
|
28
|
+
* la page affichee du tableau, ses chiffres cles, ses sources.
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
payload?: Partial<RmExportPayload> | (() => Partial<RmExportPayload> | null | undefined)
|
|
31
31
|
}>(),
|
|
32
|
-
{ actions: true, link: undefined, lang: 'fr',
|
|
32
|
+
{ actions: true, link: undefined, lang: 'fr', payload: undefined },
|
|
33
33
|
)
|
|
34
34
|
</script>
|
|
35
35
|
|
|
@@ -37,7 +37,7 @@ withDefaults(
|
|
|
37
37
|
<section class="rm-table-card">
|
|
38
38
|
<div v-if="$slots.header || actions" class="rm-table-card__header">
|
|
39
39
|
<slot name="header" />
|
|
40
|
-
<RmCardActions v-if="actions" :lang="lang" :link="link" :
|
|
40
|
+
<RmCardActions v-if="actions" :lang="lang" :link="link" :payload="payload" />
|
|
41
41
|
</div>
|
|
42
42
|
<slot />
|
|
43
43
|
</section>
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
* d'une declaration de la page : c'est ce qui rend le menu utilisable sur les
|
|
13
13
|
* cartes deja ecrites, sans les rouvrir une par une.
|
|
14
14
|
*/
|
|
15
|
-
import type {
|
|
15
|
+
import type { RmExportPayload } from '../utils/export.js';
|
|
16
16
|
/** Les actions qu'une carte propose. `link` copie, il ne telecharge pas. */
|
|
17
17
|
export type RmCardAction = 'png' | 'svg' | 'csv' | 'xlsx' | 'link';
|
|
18
|
+
export type RmCardPayload = Partial<RmExportPayload>;
|
|
18
19
|
export interface RmCardActionContext {
|
|
19
20
|
/** La carte visee. */
|
|
20
21
|
card: Element;
|
|
@@ -23,8 +24,16 @@ export interface RmCardActionContext {
|
|
|
23
24
|
name?: string;
|
|
24
25
|
/** Lien copie par « Copier le lien direct ». A defaut, l'URL courante. */
|
|
25
26
|
link?: string;
|
|
26
|
-
/**
|
|
27
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Ce que la page a de mieux que le rendu a l'ecran : ses onglets complets,
|
|
29
|
+
* ses chiffres cles, ses sources, son sous-titre. Ce qui est pose ici
|
|
30
|
+
* remplace ce que le DOM aurait donne.
|
|
31
|
+
*
|
|
32
|
+
* Une fonction est preferable des qu'une liste monte la meme carte vingt
|
|
33
|
+
* fois : elle n'est appelee qu'a l'export, alors qu'un objet lie en prop est
|
|
34
|
+
* recalcule a chaque rendu de chaque carte.
|
|
35
|
+
*/
|
|
36
|
+
payload?: RmCardPayload | (() => RmCardPayload | null | undefined);
|
|
28
37
|
}
|
|
29
38
|
export declare function useRmCardActions(): {
|
|
30
39
|
runCardAction: (action: RmCardAction, context: RmCardActionContext) => Promise<import("./useRmExport").RmExportResult>;
|
|
@@ -27,18 +27,30 @@ function absolute(link) {
|
|
|
27
27
|
if (typeof window === "undefined") return link;
|
|
28
28
|
return new URL(link, window.location.href).href;
|
|
29
29
|
}
|
|
30
|
+
const ZOOM_CLASS = "rm-card-zoom";
|
|
31
|
+
const ZOOM_BODY_CLASS = "rm-card-zoom-open";
|
|
32
|
+
function closeZoom() {
|
|
33
|
+
document.querySelectorAll(`.${ZOOM_CLASS}`).forEach((el) => el.classList.remove(ZOOM_CLASS));
|
|
34
|
+
document.body.classList.remove(ZOOM_BODY_CLASS);
|
|
35
|
+
document.removeEventListener("keydown", onZoomKey);
|
|
36
|
+
}
|
|
37
|
+
function onZoomKey(event) {
|
|
38
|
+
if (event.key === "Escape") closeZoom();
|
|
39
|
+
}
|
|
30
40
|
export function useRmCardActions() {
|
|
31
41
|
const { run } = useRmExport();
|
|
32
42
|
async function runCardAction(action, context) {
|
|
33
43
|
const { card, lang = "fr" } = context;
|
|
34
|
-
const
|
|
44
|
+
const provided = (typeof context.payload === "function" ? context.payload() : context.payload) ?? {};
|
|
45
|
+
const title = context.name || provided.title || rmCardTitle(card) || "Export";
|
|
35
46
|
const format = action === "link" ? "link" : action;
|
|
36
|
-
const sheets = action === "png" || action === "svg" ? [] :
|
|
47
|
+
const sheets = action === "png" || action === "svg" ? [] : provided.sheets?.length ? provided.sheets : await rmCardSheets(card, title, lang);
|
|
37
48
|
return run(configOf(format, fileNameOf(title)), {
|
|
38
49
|
title,
|
|
39
|
-
sheets,
|
|
40
50
|
charts: rmCardCharts(card).map((el) => ({ title, el })),
|
|
41
|
-
permalink: absolute(context.link)
|
|
51
|
+
permalink: absolute(context.link),
|
|
52
|
+
...provided,
|
|
53
|
+
sheets
|
|
42
54
|
}, lang);
|
|
43
55
|
}
|
|
44
56
|
async function toggleCardFullscreen(card) {
|
|
@@ -47,7 +59,18 @@ export function useRmCardActions() {
|
|
|
47
59
|
await document.exitFullscreen();
|
|
48
60
|
return;
|
|
49
61
|
}
|
|
50
|
-
|
|
62
|
+
if (card.classList.contains(ZOOM_CLASS)) {
|
|
63
|
+
closeZoom();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
await card.requestFullscreen?.();
|
|
68
|
+
return;
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
card.classList.add(ZOOM_CLASS);
|
|
72
|
+
document.body.classList.add(ZOOM_BODY_CLASS);
|
|
73
|
+
document.addEventListener("keydown", onZoomKey);
|
|
51
74
|
}
|
|
52
75
|
return { runCardAction, toggleCardFullscreen };
|
|
53
76
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Les options d'un filtre a referentiel long, cherchees dans l'index.
|
|
3
|
+
*
|
|
4
|
+
* Un filtre agrege un champ (les marques, les molecules) et cherche sur
|
|
5
|
+
* d'autres. La regle qui compte est la : sur un index ou une ligne porte a la
|
|
6
|
+
* fois la marque, la molecule, la classe et le laboratoire, chercher dans un
|
|
7
|
+
* filtre doit interroger AUSSI les champs freres, sans changer ce que le
|
|
8
|
+
* filtre remonte. « keytruda » tape dans le filtre molecule trouve la ligne
|
|
9
|
+
* par sa marque et l'agregation en sort PEMBROLIZUMAB ; « pembrolizumab »
|
|
10
|
+
* tape dans le filtre marque en sort Keytruda. Sans cela l'utilisateur doit
|
|
11
|
+
* connaitre le nom qu'il cherche dans le vocabulaire exact du filtre, ce qui
|
|
12
|
+
* est justement ce qu'il vient demander.
|
|
13
|
+
*
|
|
14
|
+
* Le module ne parle pas au moteur : il ecrit le corps de la requete et relit
|
|
15
|
+
* les seaux. Le transport (proxy, cache, index) reste a l'application.
|
|
16
|
+
*/
|
|
17
|
+
export interface RmFilterOption {
|
|
18
|
+
label: string;
|
|
19
|
+
value: string;
|
|
20
|
+
}
|
|
21
|
+
export interface RmFilterOptionsSpec {
|
|
22
|
+
/**
|
|
23
|
+
* Ce que le filtre agrege. Un champ : la valeur est son propre libelle
|
|
24
|
+
* (`brand.keyword`). Deux champs `[libelle, valeur]` : le premier s'affiche,
|
|
25
|
+
* le second est ce que l'analyse filtre (un libelle ATC et son code).
|
|
26
|
+
*/
|
|
27
|
+
field: string | [string, string];
|
|
28
|
+
/**
|
|
29
|
+
* Champs interroges par la saisie, dans l'ordre : le champ propre au filtre
|
|
30
|
+
* d'abord, ses synonymes ensuite. Champs analyses (`brand`), pas leur
|
|
31
|
+
* `.keyword` : un `.keyword` ne repond pas a une recherche partielle.
|
|
32
|
+
*/
|
|
33
|
+
search: string[];
|
|
34
|
+
/** La saisie, telle qu'elle est tapee. Vide, tout le referentiel remonte. */
|
|
35
|
+
input: string;
|
|
36
|
+
/**
|
|
37
|
+
* Plafond de la liste. Au-dela, c'est la recherche qui trie : une liste
|
|
38
|
+
* coupee a cent laisse croire que ce qui suit n'existe pas.
|
|
39
|
+
*/
|
|
40
|
+
size?: number;
|
|
41
|
+
}
|
|
42
|
+
/** Le corps de la requete d'agregation, a poster tel quel sur l'index. */
|
|
43
|
+
export declare function rmFilterOptionsQuery(spec: RmFilterOptionsSpec): {
|
|
44
|
+
size: number;
|
|
45
|
+
query: {
|
|
46
|
+
bool: {
|
|
47
|
+
must: {
|
|
48
|
+
query_string: {
|
|
49
|
+
fields: string[];
|
|
50
|
+
query: string;
|
|
51
|
+
analyze_wildcard: boolean;
|
|
52
|
+
default_operator: string;
|
|
53
|
+
};
|
|
54
|
+
}[];
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
aggs: {
|
|
58
|
+
count: {
|
|
59
|
+
multi_terms: {
|
|
60
|
+
terms: {
|
|
61
|
+
field: string;
|
|
62
|
+
}[];
|
|
63
|
+
size: number;
|
|
64
|
+
order: {
|
|
65
|
+
_key: string;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
terms?: undefined;
|
|
69
|
+
} | {
|
|
70
|
+
terms: {
|
|
71
|
+
field: string;
|
|
72
|
+
size: number;
|
|
73
|
+
order: {
|
|
74
|
+
_key: string;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
multi_terms?: undefined;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
/** Les options, relues dans la reponse du moteur. */
|
|
82
|
+
export declare function rmFilterOptions(response: any): RmFilterOption[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const DEFAULT_SIZE = 5e3;
|
|
2
|
+
function wildcard(input) {
|
|
3
|
+
return ("*" + input + "*").normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
4
|
+
}
|
|
5
|
+
export function rmFilterOptionsQuery(spec) {
|
|
6
|
+
const size = spec.size ?? DEFAULT_SIZE;
|
|
7
|
+
const count = Array.isArray(spec.field) ? {
|
|
8
|
+
multi_terms: {
|
|
9
|
+
terms: spec.field.map((field) => ({ field })),
|
|
10
|
+
size,
|
|
11
|
+
order: { _key: "asc" }
|
|
12
|
+
}
|
|
13
|
+
} : { terms: { field: spec.field, size, order: { _key: "asc" } } };
|
|
14
|
+
return {
|
|
15
|
+
size: 0,
|
|
16
|
+
query: {
|
|
17
|
+
bool: {
|
|
18
|
+
must: [
|
|
19
|
+
{
|
|
20
|
+
query_string: {
|
|
21
|
+
// Plusieurs champs valent un OU : la ligne repond des qu'un seul
|
|
22
|
+
// d'entre eux porte la saisie. C'est ce qui rend les synonymes.
|
|
23
|
+
fields: spec.search,
|
|
24
|
+
query: wildcard(spec.input),
|
|
25
|
+
analyze_wildcard: true,
|
|
26
|
+
default_operator: "AND"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
aggs: { count }
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function rmFilterOptions(response) {
|
|
36
|
+
const buckets = response?.aggregations?.count?.buckets ?? [];
|
|
37
|
+
return buckets.map(
|
|
38
|
+
(bucket) => Array.isArray(bucket.key) ? { label: String(bucket.key[0]), value: String(bucket.key[1]) } : { label: String(bucket.key), value: String(bucket.key) }
|
|
39
|
+
);
|
|
40
|
+
}
|