@tekkare/romulus 0.1.30 → 0.1.32
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/components/AdvancedSearch.vue +124 -90
- package/dist/runtime/components/ChartCard.vue +45 -1
- package/dist/runtime/components/DataTable.vue +17 -2
- package/dist/runtime/components/EmptyState.vue +43 -5
- package/dist/runtime/components/ErrorState.vue +69 -0
- package/dist/runtime/components/FilterBar.vue +33 -8
- package/dist/runtime/components/LoadingState.vue +81 -0
- package/dist/runtime/components/Skeleton.vue +32 -0
- package/dist/runtime/components/Table.vue +2 -2
- 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}.chart-card-actions{position:absolute;right:12px;top:12px}.cc-export{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;color:var(--text-secondary);cursor:pointer;display:flex;font-size:14px;height:28px;justify-content:center;transition:background .15s,color .15s,border-color .15s;width:28px}.cc-export:hover{color:var(--color-secondary)}.cc-export:hover,.chart-card-actions.open .cc-export{background:var(--bg-hover);border-color:var(--color-violet-pale)}.chart-card-actions.open .cc-export{color:var(--color-primary)}.cc-menu{animation:cc-menu-in .12s cubic-bezier(.2,0,0,1);background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:var(--shadow-lg);display:none;min-width:200px;padding:6px 0;position:absolute;right:0;top:34px;z-index:var(--z-modal)}.chart-card-actions.open .cc-menu{display:block}.cc-menu-section{color:var(--text-muted);font-size:10px;font-weight:700;letter-spacing:.08em;padding:8px 14px 4px;text-transform:uppercase}.cc-menu-item{align-items:center;background:none;border:0;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:13px;gap:10px;padding:7px 14px;text-align:left;width:100%}.cc-menu-item i{color:var(--text-muted);font-size:14px}.cc-menu-item:hover{background:var(--bg-surface-3);color:var(--text-primary)}.cc-menu-item:hover i{color:var(--text-secondary)}.cc-menu-divider{background:var(--bg-surface-3);height:1px;margin:4px 0}@keyframes cc-menu-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.chart-card-body{flex:1;padding:4px 12px 8px}.chart-card-foot{align-items:center;background:var(--bg-surface-2);border-radius:0 0 10px 10px;border-top:1px solid var(--border-soft);color:var(--text-muted);display:flex;flex-wrap:wrap;font-size:11px;gap:10px;padding:12px 24px}.chart-card-foot .src{align-items:center;display:flex;gap:4px}.chart-card-foot .src b{color:var(--text-secondary);font-weight:600}.chart-card-foot .dot{background:var(--border-default);border-radius:50%;height:3px;width:3px}.chart-card-foot .updated{color:var(--text-secondary);font-weight:500}.kpi-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:0 2px 8px rgba(30,41,59,.06);padding:16px 18px}.kpi-label{color:var(--text-muted);font-size:11px;font-weight:600;letter-spacing:.1em;margin:0 0 6px;text-transform:uppercase}.kpi-value{color:var(--color-primary);font-family:var(--font-serif);font-size:28px;font-weight:700;line-height:1;margin:0}.kpi-value.healthcare{color:var(--color-healthcare)}.kpi-value.discovery{color:var(--color-discovery)}.kpi-value.pharma{color:var(--color-pharma)}.kpi-value.databank{color:var(--color-databank)}.kpi-trend{align-items:center;display:inline-flex;font-size:12px;font-weight:600;gap:4px;margin-top:6px}.kpi-trend i{font-size:11px}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-danger)}.kpi-foot,.kpi-trend--flat{color:var(--text-muted)}.kpi-foot{font-size:11px;margin-top:4px}.badge{border-radius:4px;display:inline-block;font-size:10px;font-weight:600;padding:2px 8px}.badge-active{background:var(--color-success-bg);color:var(--color-success)}.badge-pending{background:var(--color-warning-bg);color:var(--color-warning)}.badge-error{background:var(--color-danger-bg);color:var(--color-danger)}.badge-info{background:var(--bg-hover);color:var(--color-primary)}.badge-grey{background:var(--bg-surface-3);color:var(--text-secondary)}table.tk{border-collapse:collapse;font-size:13px;width:100%}.sr-only,table.tk>caption.sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;white-space:nowrap}table.tk caption{caption-side:top;color:var(--text-primary);font-size:var(--font-13);font-weight:var(--weight-bold);padding:0 0 var(--space-8);text-align:left}table.tk th{background:var(--bg-surface-2);border-bottom:1px solid var(--border-default);color:var(--text-secondary);font-size:11px;font-weight:700;letter-spacing:.08em;padding:12px 14px;text-align:left;text-transform:uppercase}table.tk tbody th[scope=row]{background:transparent;font-size:var(--font-13);font-weight:var(--weight-bold);letter-spacing:0;text-transform:none}table.tk tbody th[scope=row],table.tk td{border-bottom:1px solid var(--border-soft);color:var(--text-primary)}table.tk td{padding:12px 14px}table.tk tbody tr:nth-child(2n) td,table.tk tbody tr:nth-child(2n) th[scope=row]{background:var(--bg-surface-2)}table.tk tbody tr:hover td,table.tk tbody tr:hover th[scope=row]{background:var(--bg-hover)}table.tk th[aria-sort]{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}table.tk th[aria-sort]:hover{color:var(--text-primary)}.tk-wrap{overflow-x:auto}.tk-num{font-variant-numeric:tabular-nums;text-align:right}.nav-group{display:flex;flex-direction:column}.nav-group-header{align-items:center;background:none;border:0;color:var(--text-muted);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:11px;font-weight:700;gap:8px;letter-spacing:.08em;padding:8px 12px;text-align:left;text-transform:uppercase;transition:color var(--duration-fast) var(--ease-out);width:100%}.nav-group-header:hover{color:var(--text-secondary)}.nav-group-header .caret{display:inline-flex;margin-left:auto;transition:transform var(--duration-fast) var(--ease-out)}.nav-group[data-open=true]>.nav-group-header{color:var(--color-primary)}.nav-group[data-open=true]>.nav-group-header .caret{transform:rotate(180deg)}.nav-group-items{display:flex;flex-direction:column;gap:2px;padding:2px 0 8px}.cc-export:focus-visible,.cc-menu-item:focus-visible,.chart-card :focus-visible,.nav-group-header:focus-visible,table.tk th[aria-sort]:focus-visible{box-shadow:var(--shadow-focus);outline:2px solid var(--color-secondary);outline-offset:2px}.cc-menu-item,.nav-group-header{min-height:40px}body.text-large .kpi-label{font-size:13px}body.text-large .kpi-value{font-size:34px}body.text-large .kpi-trend{font-size:14px}body.text-large .kpi-foot{font-size:13px}body.text-large .chart-card-title{font-size:17px}body.text-large .chart-card-context{font-size:14px}body.text-large .chart-card-foot{font-size:12px}body.text-large .badge{font-size:12px;padding:3px 9px}body.text-large table.tk{font-size:15px}body.text-large table.tk th{font-size:13px}body.text-xl .kpi-label{font-size:14px}body.text-xl .kpi-value{font-size:40px}body.text-xl .kpi-trend{font-size:16px}body.text-xl .kpi-foot{font-size:14px}body.text-xl .chart-card-title{font-size:20px}body.text-xl .chart-card-context{font-size:16px}body.text-xl .chart-card-foot{font-size:14px}body.text-xl .badge{font-size:14px;padding:3px 10px}body.text-xl table.tk{font-size:16px}body.text-xl table.tk th{font-size:14px}body.high-contrast .cc-export,body.high-contrast .cc-menu,body.high-contrast .chart-card,body.high-contrast .kpi-card{border-width:2px}body.high-contrast .chart-card:hover{border-color:var(--border-default)}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}
|
|
1
|
+
.grid-2{grid-template-columns:repeat(2,1fr)}.grid-2,.grid-3{display:grid;gap:20px}.grid-3{grid-template-columns:repeat(3,1fr)}.grid-4{display:grid;gap:16px;grid-template-columns:repeat(4,1fr)}.grid-2-1{display:grid;gap:20px;grid-template-columns:2fr 1fr}.pane-title{color:var(--text-primary);font-family:var(--font-serif);font-size:32px;font-weight:700;letter-spacing:-.02em;line-height:1.1;margin:0 0 16px}.pane-sub{color:var(--text-muted);font-size:13px;line-height:1.55;margin:0 0 24px;max-width:78ch}.chart-section{margin-inline:auto;max-width:var(--content-max-w,1440px);padding:0 0 40px;width:100%}.section-label{align-items:center;color:var(--text-secondary);display:flex;font-size:11px;font-weight:700;gap:8px;letter-spacing:.12em;margin:40px 0 18px;text-transform:uppercase}body.text-large .pane-title{font-size:38px}body.text-xl .pane-title{font-size:46px}body.text-large .section-label{font-size:13px}body.text-xl .section-label{font-size:14px}.chart-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:10px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;transition:box-shadow .18s,border-color .18s}.chart-card:hover{border-color:var(--color-violet-pale);box-shadow:0 6px 16px rgba(107,63,255,.1),0 1px 3px rgba(30,41,59,.04)}.chart-card-head{padding:22px 64px 14px 24px;position:relative}.chart-card-title{color:var(--text-primary);font-size:15px;font-weight:700;letter-spacing:-.01em;line-height:1.3;margin:0 0 5px}.chart-card-context{color:var(--text-muted);font-size:12px;line-height:1.55;margin:0}.chart-card-actions{position:absolute;right:12px;top:12px}.cc-export{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;color:var(--text-secondary);cursor:pointer;display:flex;font-size:14px;height:28px;justify-content:center;transition:background .15s,color .15s,border-color .15s;width:28px}.cc-export:hover{color:var(--color-secondary)}.cc-export:hover,.chart-card-actions.open .cc-export{background:var(--bg-hover);border-color:var(--color-violet-pale)}.chart-card-actions.open .cc-export{color:var(--color-primary)}.cc-menu{animation:cc-menu-in .12s cubic-bezier(.2,0,0,1);background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:var(--shadow-lg);display:none;min-width:200px;padding:6px 0;position:absolute;right:0;top:34px;z-index:var(--z-modal)}.chart-card-actions.open .cc-menu{display:block}.cc-menu-section{color:var(--text-muted);font-size:10px;font-weight:700;letter-spacing:.08em;padding:8px 14px 4px;text-transform:uppercase}.cc-menu-item{align-items:center;background:none;border:0;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:13px;gap:10px;padding:7px 14px;text-align:left;width:100%}.cc-menu-item i{color:var(--text-muted);font-size:14px}.cc-menu-item:hover{background:var(--bg-surface-3);color:var(--text-primary)}.cc-menu-item:hover i{color:var(--text-secondary)}.cc-menu-divider{background:var(--bg-surface-3);height:1px;margin:4px 0}@keyframes cc-menu-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.chart-card-body{flex:1;padding:4px 12px 8px}.chart-card-body>.rm-empty{box-sizing:border-box;gap:6px;justify-content:center;max-width:none;min-height:240px;padding:36px 24px}.chart-card-foot{align-items:center;background:var(--bg-surface-2);border-radius:0 0 10px 10px;border-top:1px solid var(--border-soft);color:var(--text-muted);display:flex;flex-wrap:wrap;font-size:11px;gap:10px;padding:12px 24px}.chart-card-foot .src{align-items:center;display:flex;gap:4px}.chart-card-foot .src b{color:var(--text-secondary);font-weight:600}.chart-card-foot .dot{background:var(--border-default);border-radius:50%;height:3px;width:3px}.chart-card-foot .updated{color:var(--text-secondary);font-weight:500}.kpi-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:0 2px 8px rgba(30,41,59,.06);padding:16px 18px}.kpi-label{color:var(--text-muted);font-size:11px;font-weight:600;letter-spacing:.1em;margin:0 0 6px;text-transform:uppercase}.kpi-value{color:var(--color-primary);font-family:var(--font-serif);font-size:28px;font-weight:700;line-height:1;margin:0}.kpi-value.healthcare{color:var(--color-healthcare)}.kpi-value.discovery{color:var(--color-discovery)}.kpi-value.pharma{color:var(--color-pharma)}.kpi-value.databank{color:var(--color-databank)}.kpi-trend{align-items:center;display:inline-flex;font-size:12px;font-weight:600;gap:4px;margin-top:6px}.kpi-trend i{font-size:11px}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-danger)}.kpi-foot,.kpi-trend--flat{color:var(--text-muted)}.kpi-foot{font-size:11px;margin-top:4px}.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,129 +1,163 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Constructeur de recherche complexe : une ligne par terme, chacune reliee aux
|
|
3
|
+
precedentes par ET, OU ou SAUF.
|
|
4
|
+
|
|
5
|
+
Le composant ne garde pas d'etat : les lignes vivent chez l'appelant
|
|
6
|
+
(`v-model`), qui les traduit en requete. Il vit dans le panneau
|
|
7
|
+
« Recherche avancee » de RmFilterBar (slot #Advanced), a cote des panneaux
|
|
8
|
+
Filtres et Personnaliser.
|
|
9
|
+
|
|
10
|
+
L'operateur se choisit en ajoutant la ligne (« + and », « | or », « − not »)
|
|
11
|
+
et se change ensuite en cliquant sa pastille, qui tourne and → or → not.
|
|
12
|
+
La premiere ligne n'a pas d'operateur : il n'y a rien avant elle.
|
|
13
|
+
-->
|
|
1
14
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export interface AdvancedSearchRow {
|
|
15
|
+
import { computed, ref, watch } from 'vue'
|
|
16
|
+
|
|
17
|
+
export type RmSearchOperator = 'and' | 'or' | 'not'
|
|
18
|
+
|
|
19
|
+
export type RmSearchRow = {
|
|
20
|
+
/** Identite stable de la ligne, pour la cle de rendu. */
|
|
10
21
|
id: number
|
|
11
|
-
type:
|
|
22
|
+
type: RmSearchOperator
|
|
12
23
|
value: string
|
|
13
24
|
}
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
const props = withDefaults(
|
|
27
|
+
defineProps<{
|
|
28
|
+
modelValue: RmSearchRow[]
|
|
29
|
+
lang?: 'fr' | 'en'
|
|
30
|
+
/** Placeholder des champs de terme. */
|
|
31
|
+
placeholder?: string
|
|
32
|
+
}>(),
|
|
33
|
+
{ lang: 'fr', placeholder: '' },
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const emit = defineEmits<{ 'update:modelValue': [rows: RmSearchRow[]] }>()
|
|
37
|
+
|
|
38
|
+
const LABELS = {
|
|
39
|
+
fr: { keyword: 'Mot-clé', clear: 'Tout effacer', remove: 'Retirer cette ligne' },
|
|
40
|
+
en: { keyword: 'Keyword', clear: 'Clear all', remove: 'Remove this row' },
|
|
41
|
+
} as const
|
|
42
|
+
|
|
43
|
+
const t = computed(() => LABELS[props.lang] ?? LABELS.fr)
|
|
44
|
+
const ORDER: RmSearchOperator[] = ['and', 'or', 'not']
|
|
45
|
+
|
|
46
|
+
/** Toujours une ligne a remplir, sinon le panneau s'ouvre vide. */
|
|
47
|
+
function normalize(list: RmSearchRow[]): RmSearchRow[] {
|
|
48
|
+
return list.length > 0 ? list.map((row) => ({ ...row })) : [{ id: 0, type: 'and', value: '' }]
|
|
49
|
+
}
|
|
25
50
|
|
|
26
|
-
|
|
51
|
+
function same(a: RmSearchRow[], b: RmSearchRow[]) {
|
|
52
|
+
return (
|
|
53
|
+
a.length === b.length &&
|
|
54
|
+
a.every((row, i) => row.id === b[i]?.id && row.type === b[i]?.type && row.value === b[i]?.value)
|
|
55
|
+
)
|
|
56
|
+
}
|
|
27
57
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Les lignes sont editees en local puis publiees.
|
|
60
|
+
*
|
|
61
|
+
* Lier les champs directement a `modelValue` faisait un aller-retour par
|
|
62
|
+
* frappe : le parent republie, le champ se repatche, et une frappe rapide se
|
|
63
|
+
* perdait dans l'intervalle. Le parent ne renvoie donc l'etat que quand il
|
|
64
|
+
* change vraiment ailleurs (la barre de recherche, un scope repris).
|
|
65
|
+
*/
|
|
66
|
+
const rows = ref<RmSearchRow[]>(normalize(props.modelValue))
|
|
67
|
+
|
|
68
|
+
watch(
|
|
69
|
+
() => props.modelValue,
|
|
70
|
+
(val) => {
|
|
71
|
+
if (!same(val, rows.value)) rows.value = normalize(val)
|
|
72
|
+
},
|
|
73
|
+
{ deep: true },
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
function nextId() {
|
|
77
|
+
return rows.value.reduce((max, row) => Math.max(max, row.id), 0) + 1
|
|
78
|
+
}
|
|
31
79
|
|
|
32
|
-
|
|
80
|
+
/** La premiere ligne n'est reliee a rien : son operateur est toujours ET. */
|
|
81
|
+
function commit() {
|
|
82
|
+
emit(
|
|
83
|
+
'update:modelValue',
|
|
84
|
+
rows.value.map((row, i) => (i === 0 ? { ...row, type: 'and' } : { ...row })),
|
|
85
|
+
)
|
|
86
|
+
}
|
|
33
87
|
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
emit('apply', rows.value.filter(r => r.value.trim() !== ''))
|
|
38
|
-
}, 500)
|
|
88
|
+
function cycleType(row: RmSearchRow) {
|
|
89
|
+
row.type = ORDER[(ORDER.indexOf(row.type) + 1) % ORDER.length]
|
|
90
|
+
commit()
|
|
39
91
|
}
|
|
40
92
|
|
|
41
|
-
function addRow(type:
|
|
42
|
-
rows.value.push({ id: nextId
|
|
93
|
+
function addRow(type: RmSearchOperator) {
|
|
94
|
+
rows.value.push({ id: nextId(), type, value: '' })
|
|
95
|
+
commit()
|
|
43
96
|
}
|
|
44
97
|
|
|
45
98
|
function removeRow(id: number) {
|
|
46
|
-
rows.value = rows.value.filter(
|
|
47
|
-
|
|
48
|
-
rows.value.push({ id: nextId++, type: 'and', value: '' })
|
|
49
|
-
}
|
|
50
|
-
emitApply()
|
|
99
|
+
rows.value = normalize(rows.value.filter((row) => row.id !== id))
|
|
100
|
+
commit()
|
|
51
101
|
}
|
|
52
102
|
|
|
53
103
|
function clearAll() {
|
|
54
|
-
rows.value = [
|
|
55
|
-
|
|
56
|
-
emitApply()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const typeColors: Record<string, string> = {
|
|
60
|
-
and: 'var(--color-healthcare)',
|
|
61
|
-
or: 'var(--color-discovery)',
|
|
62
|
-
not: 'var(--color-pharma)',
|
|
104
|
+
rows.value = normalize([])
|
|
105
|
+
commit()
|
|
63
106
|
}
|
|
64
107
|
</script>
|
|
65
108
|
|
|
66
109
|
<template>
|
|
67
|
-
<div
|
|
68
|
-
<div class="
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<div
|
|
77
|
-
v-for="row in rows"
|
|
78
|
-
:key="row.id"
|
|
79
|
-
class="advanced-search__row"
|
|
80
|
-
>
|
|
81
|
-
<span
|
|
82
|
-
class="advanced-search__type"
|
|
83
|
-
:style="{ color: typeColors[row.type] }"
|
|
110
|
+
<div class="rm-asearch">
|
|
111
|
+
<div class="rm-asearch__rows">
|
|
112
|
+
<div v-for="(row, index) in rows" :key="row.id" class="rm-asearch__row">
|
|
113
|
+
<button
|
|
114
|
+
v-if="index > 0"
|
|
115
|
+
type="button"
|
|
116
|
+
class="rm-asearch__type"
|
|
117
|
+
:class="`rm-asearch__type--${row.type}`"
|
|
118
|
+
@click="cycleType(row)"
|
|
84
119
|
>
|
|
85
|
-
{{ row.type
|
|
86
|
-
</
|
|
120
|
+
{{ row.type }}
|
|
121
|
+
</button>
|
|
122
|
+
<span v-else class="rm-asearch__type rm-asearch__type--first">and</span>
|
|
123
|
+
|
|
87
124
|
<input
|
|
88
125
|
v-model="row.value"
|
|
89
126
|
type="text"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
'advanced-search__input--and': row.type === 'and',
|
|
94
|
-
'advanced-search__input--or': row.type === 'or',
|
|
95
|
-
'advanced-search__input--not': row.type === 'not',
|
|
96
|
-
}"
|
|
97
|
-
@input="emitApply"
|
|
98
|
-
@keydown.enter="emitApply"
|
|
127
|
+
class="rm-asearch__input"
|
|
128
|
+
:placeholder="placeholder || t.keyword"
|
|
129
|
+
@input="commit"
|
|
99
130
|
>
|
|
100
|
-
|
|
101
|
-
|
|
131
|
+
|
|
132
|
+
<button
|
|
133
|
+
type="button"
|
|
134
|
+
class="rm-asearch__remove"
|
|
135
|
+
:aria-label="t.remove"
|
|
136
|
+
@click="removeRow(row.id)"
|
|
137
|
+
>
|
|
138
|
+
<RmIcon name="Trash" :size="16" />
|
|
102
139
|
</button>
|
|
103
140
|
</div>
|
|
104
141
|
</div>
|
|
105
142
|
|
|
106
|
-
<div class="
|
|
107
|
-
<button class="
|
|
108
|
-
<
|
|
109
|
-
and
|
|
143
|
+
<div class="rm-asearch__actions">
|
|
144
|
+
<button type="button" class="rm-asearch__add rm-asearch__add--and" @click="addRow('and')">
|
|
145
|
+
<RmIcon name="Plus" :size="12" /> and
|
|
110
146
|
</button>
|
|
111
|
-
<button class="
|
|
112
|
-
<
|
|
113
|
-
or
|
|
147
|
+
<button type="button" class="rm-asearch__add rm-asearch__add--or" @click="addRow('or')">
|
|
148
|
+
<RmIcon name="LineVertical" :size="12" /> or
|
|
114
149
|
</button>
|
|
115
|
-
<button class="
|
|
116
|
-
<
|
|
117
|
-
not
|
|
150
|
+
<button type="button" class="rm-asearch__add rm-asearch__add--not" @click="addRow('not')">
|
|
151
|
+
<RmIcon name="Minus" :size="12" /> not
|
|
118
152
|
</button>
|
|
119
|
-
</div>
|
|
120
153
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
154
|
+
<button type="button" class="rm-asearch__clear" @click="clearAll">
|
|
155
|
+
{{ t.clear }}
|
|
156
|
+
</button>
|
|
157
|
+
</div>
|
|
124
158
|
</div>
|
|
125
159
|
</template>
|
|
126
160
|
|
|
127
161
|
<style scoped>
|
|
128
|
-
.
|
|
162
|
+
.rm-asearch{font-family:var(--font-sans);gap:12px;width:100%}.rm-asearch,.rm-asearch__rows{display:flex;flex-direction:column}.rm-asearch__rows{gap:8px}.rm-asearch__row{align-items:center;display:flex;gap:8px}.rm-asearch__type{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-pill);cursor:pointer;flex:0 0 auto;font-family:inherit;font-size:var(--font-13);font-weight:var(--weight-semibold);height:32px;min-width:56px;padding:0 12px;text-transform:capitalize}.rm-asearch__type--first{align-items:center;color:var(--text-muted);cursor:default;display:inline-flex;justify-content:center}.rm-asearch__type--and{color:var(--color-primary)}.rm-asearch__type--or{color:var(--color-success)}.rm-asearch__type--not{color:var(--color-danger)}.rm-asearch__input{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-pill);box-sizing:border-box;color:var(--text-primary);flex:1 1 auto;font-family:inherit;font-size:var(--font-14);height:32px;min-width:0;padding:0 14px}.rm-asearch__input:focus{border-color:var(--color-primary);outline:none}.rm-asearch__remove{background:none;border:none;border-radius:var(--radius-md);color:var(--text-muted);cursor:pointer;display:inline-flex;flex:0 0 auto;padding:6px}.rm-asearch__remove:hover{background:var(--bg-surface-3);color:var(--color-danger)}.rm-asearch__actions{align-items:center;display:flex;gap:8px}.rm-asearch__add{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-pill);cursor:pointer;display:inline-flex;font-family:inherit;font-size:var(--font-13);font-weight:var(--weight-medium);gap:4px;height:28px;padding:0 12px}.rm-asearch__add:hover{background:var(--bg-surface-3)}.rm-asearch__add--and{color:var(--color-primary)}.rm-asearch__add--or{color:var(--color-success)}.rm-asearch__add--not{color:var(--color-danger)}.rm-asearch__clear{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-family:inherit;font-size:var(--font-14);font-weight:var(--weight-medium);margin-left:auto;padding:0;text-decoration-line:underline}
|
|
129
163
|
</style>
|
|
@@ -26,10 +26,26 @@ const props = withDefaults(defineProps<{
|
|
|
26
26
|
extra?: string
|
|
27
27
|
/** Masque le menu d'actions quand la carte n'a rien a exporter. */
|
|
28
28
|
actions?: boolean
|
|
29
|
+
/**
|
|
30
|
+
* Etat de la donnee. Les trois variantes se posent dans le CORPS ; la tete
|
|
31
|
+
* et le pied restent stables, pour que le titre et la source restent sous
|
|
32
|
+
* les yeux pendant que la donnee charge, manque ou echoue.
|
|
33
|
+
*/
|
|
34
|
+
state?: 'ready' | 'loading' | 'empty' | 'error'
|
|
35
|
+
/**
|
|
36
|
+
* Libelle du bouton du corps en erreur, qui emet `retry`. Vide : aucun
|
|
37
|
+
* bouton. Un bouton que la page n'ecoute pas ne repare rien, il ment.
|
|
38
|
+
*/
|
|
39
|
+
retryLabel?: string
|
|
40
|
+
/** Libelle du bouton du corps vide, qui emet `emptyAction`. Vide : aucun bouton. */
|
|
41
|
+
emptyActionLabel?: string
|
|
29
42
|
}>(), {
|
|
30
43
|
context: undefined,
|
|
31
44
|
extra: undefined,
|
|
32
45
|
actions: true,
|
|
46
|
+
state: 'ready',
|
|
47
|
+
retryLabel: '',
|
|
48
|
+
emptyActionLabel: '',
|
|
33
49
|
})
|
|
34
50
|
|
|
35
51
|
const emit = defineEmits<{
|
|
@@ -39,6 +55,10 @@ const emit = defineEmits<{
|
|
|
39
55
|
exportXlsx: []
|
|
40
56
|
copyLink: []
|
|
41
57
|
fullscreen: []
|
|
58
|
+
/** L'utilisateur relance la donnee depuis le corps en erreur. */
|
|
59
|
+
retry: []
|
|
60
|
+
/** L'utilisateur veut sortir du corps vide (elargir ses filtres). */
|
|
61
|
+
emptyAction: []
|
|
42
62
|
}>()
|
|
43
63
|
|
|
44
64
|
const menuOpen = ref(false)
|
|
@@ -116,8 +136,32 @@ function run(action: 'exportPng' | 'exportSvg' | 'exportCsv' | 'exportXlsx' | 'c
|
|
|
116
136
|
</div>
|
|
117
137
|
</div>
|
|
118
138
|
|
|
139
|
+
<!-- Un corps vide n'est jamais laisse blanc : chaque etat a son rendu par
|
|
140
|
+
defaut, qu'un slot dedie peut remplacer quand la page a mieux a dire. -->
|
|
119
141
|
<div class="chart-card-body">
|
|
120
|
-
<slot />
|
|
142
|
+
<slot v-if="state === 'ready'" />
|
|
143
|
+
<slot v-else-if="state === 'loading'" name="loading">
|
|
144
|
+
<RmLoadingState />
|
|
145
|
+
</slot>
|
|
146
|
+
<!-- Pas de cadre pointille ici : dans un corps de carte, la carte porte
|
|
147
|
+
deja son propre cadre. Le pointille est reserve aux vues entieres. -->
|
|
148
|
+
<slot v-else-if="state === 'empty'" name="empty">
|
|
149
|
+
<RmEmptyState
|
|
150
|
+
icon="MagnifyingGlassMinus"
|
|
151
|
+
title="Aucun résultat"
|
|
152
|
+
text="Aucune ligne ne correspond aux filtres en cours. Élargissez votre période ou retirez une dimension."
|
|
153
|
+
:action-label="emptyActionLabel"
|
|
154
|
+
action-icon="Broom"
|
|
155
|
+
@action="emit('emptyAction')"
|
|
156
|
+
/>
|
|
157
|
+
</slot>
|
|
158
|
+
<slot v-else name="error">
|
|
159
|
+
<RmErrorState
|
|
160
|
+
text="La donnée de cette carte n'a pas pu être chargée."
|
|
161
|
+
:action-label="retryLabel"
|
|
162
|
+
@action="emit('retry')"
|
|
163
|
+
/>
|
|
164
|
+
</slot>
|
|
121
165
|
</div>
|
|
122
166
|
|
|
123
167
|
<div class="chart-card-foot">
|
|
@@ -35,11 +35,17 @@ const props = withDefaults(defineProps<{
|
|
|
35
35
|
sortDir?: 'asc' | 'desc'
|
|
36
36
|
/** Message affiche quand il n'y a aucune ligne. Le hors-perimetre se dit, il ne s'estime pas. */
|
|
37
37
|
emptyText?: string
|
|
38
|
+
/** Squelette de lignes pendant le chargement, plutot qu'un tableau vide. */
|
|
39
|
+
loading?: boolean
|
|
40
|
+
/** Nombre de lignes du squelette : la table garde sa hauteur a l'arrivee de la donnee. */
|
|
41
|
+
loadingRows?: number
|
|
38
42
|
}>(), {
|
|
39
43
|
captionVisible: false,
|
|
40
44
|
sortKey: undefined,
|
|
41
45
|
sortDir: undefined,
|
|
42
46
|
emptyText: 'Aucune donnée disponible pour cette sélection.',
|
|
47
|
+
loading: false,
|
|
48
|
+
loadingRows: 5,
|
|
43
49
|
})
|
|
44
50
|
|
|
45
51
|
const emit = defineEmits<{ sort: [key: string] }>()
|
|
@@ -72,9 +78,18 @@ function ariaSort(column: RmColumn) {
|
|
|
72
78
|
</th>
|
|
73
79
|
</tr>
|
|
74
80
|
</thead>
|
|
75
|
-
<tbody>
|
|
81
|
+
<tbody v-if="loading">
|
|
82
|
+
<!-- Une table vide pendant le chargement se lit comme une absence de
|
|
83
|
+
donnee : le squelette dit que la donnee arrive. -->
|
|
84
|
+
<tr v-for="index in loadingRows" :key="index">
|
|
85
|
+
<td v-for="column in columns" :key="column.key">
|
|
86
|
+
<div class="rm-skeleton tk-skeleton" />
|
|
87
|
+
</td>
|
|
88
|
+
</tr>
|
|
89
|
+
</tbody>
|
|
90
|
+
<tbody v-else>
|
|
76
91
|
<tr v-if="!rows.length">
|
|
77
|
-
<td :colspan="columns.length">{{ emptyText }}</td>
|
|
92
|
+
<td :colspan="columns.length" class="tk-empty">{{ emptyText }}</td>
|
|
78
93
|
</tr>
|
|
79
94
|
<tr v-for="(row, index) in rows" :key="index">
|
|
80
95
|
<template v-for="column in columns" :key="column.key">
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
Etat vide d'une liste : une icone dans un disque, un titre, une
|
|
2
|
+
Etat vide d'une liste ou d'une vue : une icone dans un disque, un titre, une
|
|
3
|
+
phrase, et l'action a faire.
|
|
3
4
|
|
|
4
5
|
Remplace `arg-errors` d'Auriga. Les textes viennent de la page : le
|
|
5
6
|
hors-perimetre se dit, il ne s'estime pas.
|
|
7
|
+
|
|
8
|
+
Le systeme de design demande trois choses de plus qu'un simple message.
|
|
9
|
+
D'abord une action, sauf s'il n'y a vraiment rien a faire : un ecran vide
|
|
10
|
+
sans issue laisse l'utilisateur devant une impasse. Ensuite un ton sans
|
|
11
|
+
catastrophisme, « Aucun résultat » plutot que « Erreur ». Enfin un titre qui
|
|
12
|
+
dit ce qui manque et une phrase qui dit pourquoi ET quoi faire.
|
|
13
|
+
|
|
14
|
+
`tone` colore le disque selon la nature du vide : violet quand il reste
|
|
15
|
+
quelque chose a creer, rouge quand un droit bloque, neutre quand une
|
|
16
|
+
recherche ne ramene rien. `framed` pose le cadre pointille des etats
|
|
17
|
+
transitoires.
|
|
6
18
|
-->
|
|
7
19
|
<script setup lang="ts">
|
|
8
20
|
withDefaults(
|
|
@@ -10,23 +22,49 @@ withDefaults(
|
|
|
10
22
|
icon?: string
|
|
11
23
|
title: string
|
|
12
24
|
text?: string
|
|
25
|
+
/** Couleur du disque : `neutral` recherche vide, `primary` a creer, `danger` bloquant. */
|
|
26
|
+
tone?: 'neutral' | 'primary' | 'danger'
|
|
27
|
+
/** Libelle de l'action. Vide pour n'en poser aucune. */
|
|
28
|
+
actionLabel?: string
|
|
29
|
+
/** Icone de l'action. */
|
|
30
|
+
actionIcon?: string
|
|
31
|
+
/** Cadre pointille, pour signaler un etat transitoire. */
|
|
32
|
+
framed?: boolean
|
|
13
33
|
}>(),
|
|
14
|
-
{
|
|
34
|
+
{
|
|
35
|
+
icon: 'MagnifyingGlass',
|
|
36
|
+
text: '',
|
|
37
|
+
tone: 'neutral',
|
|
38
|
+
actionLabel: '',
|
|
39
|
+
actionIcon: '',
|
|
40
|
+
framed: false,
|
|
41
|
+
},
|
|
15
42
|
)
|
|
43
|
+
|
|
44
|
+
const emit = defineEmits<{ action: [] }>()
|
|
16
45
|
</script>
|
|
17
46
|
|
|
18
47
|
<template>
|
|
19
|
-
<div class="rm-empty">
|
|
48
|
+
<div class="rm-empty" :class="[`rm-empty--${tone}`, { 'rm-empty--framed': framed }]">
|
|
20
49
|
<div class="rm-empty__icon">
|
|
21
|
-
<RmIcon :name="icon" :size="
|
|
50
|
+
<RmIcon :name="icon" :size="28" />
|
|
22
51
|
</div>
|
|
23
52
|
<div class="rm-empty__text">
|
|
24
53
|
<p class="rm-empty__title">{{ title }}</p>
|
|
25
54
|
<p v-if="text" class="rm-empty__descr">{{ text }}</p>
|
|
26
55
|
</div>
|
|
56
|
+
<button
|
|
57
|
+
v-if="actionLabel"
|
|
58
|
+
type="button"
|
|
59
|
+
class="rm-empty__action"
|
|
60
|
+
@click="emit('action')"
|
|
61
|
+
>
|
|
62
|
+
<RmIcon v-if="actionIcon" :name="actionIcon" :size="14" />
|
|
63
|
+
{{ actionLabel }}
|
|
64
|
+
</button>
|
|
27
65
|
</div>
|
|
28
66
|
</template>
|
|
29
67
|
|
|
30
68
|
<style scoped>
|
|
31
|
-
.rm-empty{color:var(--text-primary);flex-direction:column;font-family:var(--font-sans);gap:24px;max-width:409px}.rm-empty
|
|
69
|
+
.rm-empty{align-items:center;color:var(--text-primary);display:flex;flex-direction:column;font-family:var(--font-sans);gap:24px;max-width:409px}.rm-empty--framed{background:var(--bg-surface-2);border:1px dashed var(--border-default);border-radius:12px;box-sizing:border-box;max-width:none;padding:32px 16px;width:100%}.rm-empty__icon{align-items:center;background:var(--bg-surface-3);border-radius:50%;color:var(--text-muted);display:flex;flex:0 0 auto;height:56px;justify-content:center;width:56px}.rm-empty--primary .rm-empty__icon{background:var(--bg-hover);color:var(--color-secondary)}.rm-empty--danger .rm-empty__icon{background:var(--color-danger-bg);color:var(--color-danger)}.rm-empty__text{align-items:center;align-self:stretch;display:flex;flex-direction:column;gap:8px;text-align:center}.rm-empty__title{font-size:var(--font-16);font-weight:var(--weight-bold);line-height:normal;margin:0}.rm-empty__descr{color:var(--text-secondary);font-size:var(--font-14);line-height:160%;margin:0}.rm-empty__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-13);font-weight:var(--weight-semibold);gap:6px;padding:7px 14px}.rm-empty__action:hover{background:var(--bg-surface-3);color:var(--text-primary)}
|
|
32
70
|
</style>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmErrorState - corps de carte ou de vue dont la donnee a echoue. Style :
|
|
3
|
+
components.css (.rm-state, .rm-state--error).
|
|
4
|
+
|
|
5
|
+
Le systeme de design impose trois pieces d'information, dans cet ordre : ce
|
|
6
|
+
qui a echoue (`title`), pourquoi (`text`), et quoi faire (`actionLabel` avec
|
|
7
|
+
l'evenement `action`). Une erreur sans issue est une impasse, d'ou l'action
|
|
8
|
+
posee par defaut.
|
|
9
|
+
|
|
10
|
+
Le ton reste factuel : « Connecteur indisponible », pas « ERREUR FATALE ».
|
|
11
|
+
Les textes viennent donc de l'appelant, qui seul sait ce qui a echoue.
|
|
12
|
+
|
|
13
|
+
Pour une erreur serveur, `code` ajoute l'identifiant que le support
|
|
14
|
+
demandera. L'etat ne se signale jamais par le rouge seul : couleur, icone
|
|
15
|
+
et texte disent la meme chose, pour rester lisible en vision des couleurs
|
|
16
|
+
alteree.
|
|
17
|
+
-->
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
withDefaults(
|
|
20
|
+
defineProps<{
|
|
21
|
+
/** Ce qui a echoue. */
|
|
22
|
+
title?: string
|
|
23
|
+
/** Pourquoi, et depuis quand. */
|
|
24
|
+
text?: string
|
|
25
|
+
/** Icone du disque : `CloudWarning` reseau, `LockKey` droits, `WarningOctagon` sinon. */
|
|
26
|
+
icon?: string
|
|
27
|
+
/** Libelle de l'action de recuperation. Vide pour aucune action. */
|
|
28
|
+
actionLabel?: string
|
|
29
|
+
/** Icone de l'action. */
|
|
30
|
+
actionIcon?: string
|
|
31
|
+
/** Identifiant a citer au support, pour une erreur serveur. */
|
|
32
|
+
code?: string
|
|
33
|
+
}>(),
|
|
34
|
+
{
|
|
35
|
+
title: 'Donnée indisponible',
|
|
36
|
+
text: '',
|
|
37
|
+
icon: 'WarningOctagon',
|
|
38
|
+
actionLabel: 'Réessayer',
|
|
39
|
+
actionIcon: 'ArrowClockwise',
|
|
40
|
+
code: '',
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const emit = defineEmits<{ action: [] }>()
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<template>
|
|
48
|
+
<div class="rm-state rm-state--error" role="alert">
|
|
49
|
+
<div class="rm-state__icon">
|
|
50
|
+
<RmIcon :name="icon" :size="26" />
|
|
51
|
+
</div>
|
|
52
|
+
<p class="rm-state__title">{{ title }}</p>
|
|
53
|
+
<p v-if="text" class="rm-state__text">{{ text }}</p>
|
|
54
|
+
<p v-if="code" class="rm-state__code">Code : {{ code }}</p>
|
|
55
|
+
<button
|
|
56
|
+
v-if="actionLabel"
|
|
57
|
+
type="button"
|
|
58
|
+
class="rm-state__action"
|
|
59
|
+
@click="emit('action')"
|
|
60
|
+
>
|
|
61
|
+
<RmIcon v-if="actionIcon" :name="actionIcon" :size="14" />
|
|
62
|
+
{{ actionLabel }}
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<style scoped>
|
|
68
|
+
.rm-state__code{color:var(--text-muted);font-family:var(--font-mono);font-size:var(--font-11);margin:2px 0 0}
|
|
69
|
+
</style>
|
|
@@ -49,24 +49,29 @@ const props = withDefaults(
|
|
|
49
49
|
const emit = defineEmits<{ clearFilters: []; saveScope: [] }>()
|
|
50
50
|
|
|
51
51
|
const LABELS = {
|
|
52
|
-
fr: { title: 'Filtres', save: 'Sauvegarder un scope', clear: 'Tout effacer', customize: 'Personnaliser' },
|
|
53
|
-
en: { title: 'Filters', save: 'Save a scope', clear: 'Clear all', customize: 'Customize' },
|
|
52
|
+
fr: { title: 'Filtres', save: 'Sauvegarder un scope', clear: 'Tout effacer', customize: 'Personnaliser', advanced: 'Recherche avancée' },
|
|
53
|
+
en: { title: 'Filters', save: 'Save a scope', clear: 'Clear all', customize: 'Customize', advanced: 'Advanced search' },
|
|
54
54
|
} as const
|
|
55
55
|
|
|
56
56
|
const t = computed(() => LABELS[props.lang] ?? LABELS.fr)
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
60
|
-
* (slot #
|
|
61
|
-
* ouvert a la
|
|
59
|
+
* Trois panneaux possibles sous la barre : les filtres, la recherche avancee
|
|
60
|
+
* (slot #Advanced) et la personnalisation (slot #Customize). Les deux derniers
|
|
61
|
+
* n'ont de puce que si la page fournit le slot. Un seul panneau ouvert a la
|
|
62
|
+
* fois : ils occupent la meme place et se liraient l'un par-dessus l'autre.
|
|
62
63
|
*/
|
|
63
|
-
|
|
64
|
+
type RmFilterBarPanel = 'filters' | 'advanced' | 'customize'
|
|
65
|
+
|
|
66
|
+
const panel = ref<RmFilterBarPanel | null>(null)
|
|
64
67
|
const open = computed(() => panel.value === 'filters')
|
|
68
|
+
const advancedOpen = computed(() => panel.value === 'advanced')
|
|
65
69
|
const customizeOpen = computed(() => panel.value === 'customize')
|
|
66
70
|
const slots = useSlots()
|
|
71
|
+
const hasAdvanced = computed(() => !!slots.Advanced)
|
|
67
72
|
const hasCustomize = computed(() => !!slots.Customize)
|
|
68
73
|
|
|
69
|
-
function toggle(which:
|
|
74
|
+
function toggle(which: RmFilterBarPanel) {
|
|
70
75
|
panel.value = panel.value === which ? null : which
|
|
71
76
|
}
|
|
72
77
|
|
|
@@ -131,6 +136,19 @@ onBeforeUnmount(() => {
|
|
|
131
136
|
<slot name="Search" />
|
|
132
137
|
</div>
|
|
133
138
|
|
|
139
|
+
<!-- La recherche avancee s'ouvre par un lien pose au bout du champ : elle
|
|
140
|
+
prolonge la recherche, elle n'est pas un troisieme reglage a cote de
|
|
141
|
+
« Filtres » et « Personnaliser ». -->
|
|
142
|
+
<button
|
|
143
|
+
v-if="hasAdvanced"
|
|
144
|
+
type="button"
|
|
145
|
+
class="rm-fbar__advanced"
|
|
146
|
+
:class="{ 'is-active': advancedOpen }"
|
|
147
|
+
@click="toggle('advanced')"
|
|
148
|
+
>
|
|
149
|
+
{{ t.advanced }}
|
|
150
|
+
</button>
|
|
151
|
+
|
|
134
152
|
<button
|
|
135
153
|
v-if="hasCustomize"
|
|
136
154
|
type="button"
|
|
@@ -170,6 +188,13 @@ onBeforeUnmount(() => {
|
|
|
170
188
|
</div>
|
|
171
189
|
</div>
|
|
172
190
|
|
|
191
|
+
<div v-if="hasAdvanced" v-show="advancedOpen" class="rm-fbar__panel rm-fbar__panel--wide">
|
|
192
|
+
<div class="rm-fbar__panel-head">
|
|
193
|
+
<p class="rm-fbar__panel-title">{{ t.advanced }}</p>
|
|
194
|
+
</div>
|
|
195
|
+
<slot name="Advanced" />
|
|
196
|
+
</div>
|
|
197
|
+
|
|
173
198
|
<div v-if="hasCustomize" v-show="customizeOpen" class="rm-fbar__panel">
|
|
174
199
|
<div class="rm-fbar__panel-head">
|
|
175
200
|
<p class="rm-fbar__panel-title">{{ t.customize }}</p>
|
|
@@ -182,5 +207,5 @@ onBeforeUnmount(() => {
|
|
|
182
207
|
</template>
|
|
183
208
|
|
|
184
209
|
<style scoped>
|
|
185
|
-
.rm-fbar{margin-inline:calc((min(var(--oip-content-max, 100%), 100%) - var(--oip-main-w, 100%))/2);position:sticky;width:var(--oip-main-w,100%);z-index:91;--rm-fbar-field-h:32px;backdrop-filter:blur(12px);background:rgba(245,245,249,.8);border-bottom:1px solid var(--border-default);display:flex;flex-direction:column;font-family:var(--font-sans)}.rm-fbar__row{align-items:flex-start;display:flex;gap:12px;padding-block:8px;padding-inline:max(32px,(100% - var(--oip-content-max,100%))/2 + 32px)}.rm-fbar--open .rm-fbar__row{border-bottom:1px solid var(--border-default)}.rm-fbar__toggle :deep(p),.rm-fbar__toggle :deep(span){font-size:inherit;font-weight:inherit;line-height:inherit;margin:0}.rm-fbar__toggle{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-pill);color:var(--text-primary);cursor:pointer;display:flex;flex:0 0 auto;font-family:inherit;font-size:var(--font-14);font-weight:var(--weight-medium);gap:6px;height:var(--rm-fbar-field-h);justify-content:center;padding:0 14px}.rm-fbar__toggle:hover{background:var(--bg-surface-3)}.rm-fbar__toggle.is-active,.rm-fbar__toggle.is-active:hover{background:var(--color-primary);border-color:var(--color-primary);color:var(--color-text-inverse)}.rm-fbar__toggle.is-active .rm-fbar__badge{background:var(--color-text-inverse);color:var(--color-primary)}.rm-fbar__badge{align-items:center;background:var(--color-primary);border-radius:16px;color:var(--color-text-inverse);display:inline-flex;font-size:var(--font-11);font-weight:var(--weight-bold);justify-content:center;min-width:18px;padding:0 5px}.rm-fbar__view{align-items:center;display:flex;flex:0 0 auto}.rm-fbar__search{align-items:center;display:flex;flex:1 1 auto;gap:8px;min-width:0}.rm-fbar__search:empty{display:none}.rm-fbar__search :deep(.filters_input_search){border-radius:var(--radius-pill);box-sizing:border-box;height:auto;min-height:var(--rm-fbar-field-h);width:100%}.rm-fbar__search :deep(.research_tags:not(:has(*))){display:none}.rm-fbar__search :deep(.select_shortcut){box-sizing:border-box;font-size:var(--font-11);height:24px;padding-block:0}.rm-fbar__search :deep(.select_shortcut:hover){background:var(--bg-surface-3)!important;border-color:var(--border-default)!important;color:inherit!important}.rm-fbar__search :deep(.select_shortcut:hover svg){color:inherit!important}.rm-fbar__search :deep(.remove_search_section){border-left:none;border-radius:0 var(--radius-pill) var(--radius-pill) 0}.rm-fbar__search :deep(.remove_search_section:hover){background:transparent!important}.rm-fbar__search :deep(.remove_search_section:hover svg){color:var(--text-primary)!important}.rm-fbar__search :deep(.filters_input_search.no_bottom){border-bottom:1.5px solid var(--color-primary)!important;border-radius:var(--radius-pill)}.rm-fbar__search :deep(.search_suggestions){border:1px solid var(--border-default);border-radius:var(--radius-2xl);box-shadow:var(--shadow-lg);left:0;padding:6px;right:0;top:calc(100% + 6px);width:auto}.rm-fbar__search :deep(.sugg_container>*){border-radius:var(--radius-lg)}.rm-fbar__search :deep(.sugg_container>.selected){background:color-mix(in srgb,var(--color-primary) 6%,transparent)!important}.rm-fbar__panel{--rm-filter-col:224px;align-items:end;display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(0,var(--rm-filter-col)));justify-content:center;padding-block:16px 20px;padding-inline:56px}.rm-fbar__filters{display:contents}.rm-fbar__panel-head{align-items:baseline;display:flex;gap:16px;grid-column:1/-1;width:100%}.rm-fbar__customize{grid-column:1/-1}.rm-fbar__panel :deep(.filters_row_break){grid-column:1/-1}.rm-fbar__panel-title{font-size:var(--font-18);font-weight:800;margin:0}.rm-fbar__action{background:none;border:none;cursor:pointer;font-family:inherit;font-size:var(--font-14);font-weight:var(--weight-medium);padding:0;text-decoration-line:underline}.rm-fbar__action--save{color:var(--color-primary)}.rm-fbar__action--clear{color:var(--text-secondary)}
|
|
210
|
+
.rm-fbar{margin-inline:calc((min(var(--oip-content-max, 100%), 100%) - var(--oip-main-w, 100%))/2);position:sticky;width:var(--oip-main-w,100%);z-index:91;--rm-fbar-field-h:32px;backdrop-filter:blur(12px);background:rgba(245,245,249,.8);border-bottom:1px solid var(--border-default);display:flex;flex-direction:column;font-family:var(--font-sans)}.rm-fbar__row{align-items:flex-start;display:flex;gap:12px;padding-block:8px;padding-inline:max(32px,(100% - var(--oip-content-max,100%))/2 + 32px)}.rm-fbar--open .rm-fbar__row{border-bottom:1px solid var(--border-default)}.rm-fbar__toggle :deep(p),.rm-fbar__toggle :deep(span){font-size:inherit;font-weight:inherit;line-height:inherit;margin:0}.rm-fbar__toggle{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-pill);color:var(--text-primary);cursor:pointer;display:flex;flex:0 0 auto;font-family:inherit;font-size:var(--font-14);font-weight:var(--weight-medium);gap:6px;height:var(--rm-fbar-field-h);justify-content:center;padding:0 14px}.rm-fbar__toggle:hover{background:var(--bg-surface-3)}.rm-fbar__toggle.is-active,.rm-fbar__toggle.is-active:hover{background:var(--color-primary);border-color:var(--color-primary);color:var(--color-text-inverse)}.rm-fbar__toggle.is-active .rm-fbar__badge{background:var(--color-text-inverse);color:var(--color-primary)}.rm-fbar__badge{align-items:center;background:var(--color-primary);border-radius:16px;color:var(--color-text-inverse);display:inline-flex;font-size:var(--font-11);font-weight:var(--weight-bold);justify-content:center;min-width:18px;padding:0 5px}.rm-fbar__view{align-items:center;display:flex;flex:0 0 auto}.rm-fbar__advanced{align-items:center;background:none;border:none;color:var(--color-primary);cursor:pointer;display:inline-flex;flex:0 0 auto;font-family:inherit;font-size:var(--font-13);font-weight:var(--weight-medium);height:var(--rm-fbar-field-h);padding:0;text-decoration-line:underline;white-space:nowrap}.rm-fbar__advanced.is-active,.rm-fbar__advanced:hover{color:var(--color-primary-hover,var(--color-primary));text-decoration-thickness:2px}.rm-fbar__search{align-items:center;display:flex;flex:1 1 auto;gap:8px;min-width:0}.rm-fbar__search:empty{display:none}.rm-fbar__search :deep(.filters_input_search){border-radius:var(--radius-pill);box-sizing:border-box;height:auto;min-height:var(--rm-fbar-field-h);width:100%}.rm-fbar__search :deep(.research_tags:not(:has(*))){display:none}.rm-fbar__search :deep(.select_shortcut){box-sizing:border-box;font-size:var(--font-11);height:24px;padding-block:0}.rm-fbar__search :deep(.select_shortcut:hover){background:var(--bg-surface-3)!important;border-color:var(--border-default)!important;color:inherit!important}.rm-fbar__search :deep(.select_shortcut:hover svg){color:inherit!important}.rm-fbar__search :deep(.remove_search_section){border-left:none;border-radius:0 var(--radius-pill) var(--radius-pill) 0}.rm-fbar__search :deep(.remove_search_section:hover){background:transparent!important}.rm-fbar__search :deep(.remove_search_section:hover svg){color:var(--text-primary)!important}.rm-fbar__search :deep(.filters_input_search.no_bottom){border-bottom:1.5px solid var(--color-primary)!important;border-radius:var(--radius-pill)}.rm-fbar__search :deep(.search_suggestions){border:1px solid var(--border-default);border-radius:var(--radius-2xl);box-shadow:var(--shadow-lg);left:0;padding:6px;right:0;top:calc(100% + 6px);width:auto}.rm-fbar__search :deep(.sugg_container>*){border-radius:var(--radius-lg)}.rm-fbar__search :deep(.sugg_container>.selected){background:color-mix(in srgb,var(--color-primary) 6%,transparent)!important}.rm-fbar__panel{--rm-filter-col:224px;align-items:end;display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(0,var(--rm-filter-col)));justify-content:center;padding-block:16px 20px;padding-inline:56px}.rm-fbar__panel--wide{align-items:stretch;display:flex;flex-direction:column;gap:12px}.rm-fbar__filters{display:contents}.rm-fbar__panel-head{align-items:baseline;display:flex;gap:16px;grid-column:1/-1;width:100%}.rm-fbar__customize{grid-column:1/-1}.rm-fbar__panel :deep(.filters_row_break){grid-column:1/-1}.rm-fbar__panel-title{font-size:var(--font-18);font-weight:800;margin:0}.rm-fbar__action{background:none;border:none;cursor:pointer;font-family:inherit;font-size:var(--font-14);font-weight:var(--weight-medium);padding:0;text-decoration-line:underline}.rm-fbar__action--save{color:var(--color-primary)}.rm-fbar__action--clear{color:var(--text-secondary)}
|
|
186
211
|
</style>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmLoadingState - corps de carte en chargement. Style : components.css
|
|
3
|
+
(.rm-state-loading).
|
|
4
|
+
|
|
5
|
+
Le systeme de design tranche : squelette plutot que rotative des qu'on
|
|
6
|
+
connait la forme du contenu, et jamais de blanc pendant une attente. Ce
|
|
7
|
+
composant porte la forme la plus courante d'un corps de carte, un titre,
|
|
8
|
+
une plage de graphe, deux lignes de legende. `chart` a false donne une
|
|
9
|
+
simple pile de lignes, pour une liste ou un tableau.
|
|
10
|
+
|
|
11
|
+
Une rotative reste possible via `spinner`, pour une action ponctuelle dont
|
|
12
|
+
on ignore la structure du resultat.
|
|
13
|
+
-->
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { computed } from 'vue'
|
|
16
|
+
|
|
17
|
+
const props = withDefaults(
|
|
18
|
+
defineProps<{
|
|
19
|
+
/** Pose la plage pleine du graphe entre les lignes. */
|
|
20
|
+
chart?: boolean
|
|
21
|
+
/** Nombre de lignes de texte simulees. */
|
|
22
|
+
lines?: number
|
|
23
|
+
/** Hauteur de la plage de graphe. */
|
|
24
|
+
chartHeight?: string | number
|
|
25
|
+
/** Rotative plutot que squelette, avec ce libelle. */
|
|
26
|
+
spinner?: boolean
|
|
27
|
+
/** Texte sous la rotative. */
|
|
28
|
+
label?: string
|
|
29
|
+
/** Hauteur minimale du bloc, pour epouser la carte qu'il remplit. */
|
|
30
|
+
minHeight?: string | number
|
|
31
|
+
}>(),
|
|
32
|
+
{
|
|
33
|
+
chart: true,
|
|
34
|
+
lines: 2,
|
|
35
|
+
chartHeight: 120,
|
|
36
|
+
spinner: false,
|
|
37
|
+
label: '',
|
|
38
|
+
minHeight: undefined,
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
/** Largeurs decroissantes : une pile de lignes egales ne ressemble a rien. */
|
|
43
|
+
const WIDTHS = ['90%', '70%', '80%', '60%', '75%']
|
|
44
|
+
|
|
45
|
+
const size = (value: string | number | undefined) =>
|
|
46
|
+
value === undefined ? undefined : typeof value === 'number' ? `${value}px` : value
|
|
47
|
+
|
|
48
|
+
const style = computed(() => ({ minHeight: size(props.minHeight) }))
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<div
|
|
53
|
+
class="rm-state-loading"
|
|
54
|
+
:style="style"
|
|
55
|
+
role="status"
|
|
56
|
+
aria-busy="true"
|
|
57
|
+
:aria-label="label || 'Chargement en cours'"
|
|
58
|
+
>
|
|
59
|
+
<template v-if="spinner">
|
|
60
|
+
<div class="rm-loading__center">
|
|
61
|
+
<div class="rm-spinner" />
|
|
62
|
+
<p v-if="label" class="rm-loading__label">{{ label }}</p>
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
<template v-else>
|
|
67
|
+
<RmSkeleton width="50%" :height="14" />
|
|
68
|
+
<RmSkeleton v-if="chart" width="100%" :height="chartHeight" radius="8px" />
|
|
69
|
+
<RmSkeleton
|
|
70
|
+
v-for="index in lines"
|
|
71
|
+
:key="index"
|
|
72
|
+
:width="WIDTHS[(index - 1) % WIDTHS.length]"
|
|
73
|
+
:height="10"
|
|
74
|
+
/>
|
|
75
|
+
</template>
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<style scoped>
|
|
80
|
+
.rm-loading__center{align-items:center;display:flex;flex:1;flex-direction:column;gap:12px;justify-content:center}.rm-loading__label{color:var(--text-muted);font-family:var(--font-sans);font-size:var(--font-12);margin:0}
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmSkeleton - un bloc de squelette anime, pose a la place du contenu qui
|
|
3
|
+
charge. Style : components.css (.rm-skeleton).
|
|
4
|
+
|
|
5
|
+
Il n'a aucune forme propre : c'est l'appelant qui donne la largeur et la
|
|
6
|
+
hauteur du contenu remplace. Un squelette qui ne ressemble pas au contenu
|
|
7
|
+
final fait sauter la mise en page a l'arrivee de la donnee, ce que le
|
|
8
|
+
squelette est justement la pour eviter.
|
|
9
|
+
-->
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
withDefaults(
|
|
12
|
+
defineProps<{
|
|
13
|
+
/** Largeur CSS. Un nombre est lu en pixels. */
|
|
14
|
+
width?: string | number
|
|
15
|
+
/** Hauteur CSS. Un nombre est lu en pixels. */
|
|
16
|
+
height?: string | number
|
|
17
|
+
/** Rayon CSS, pour un disque (`50%`) ou une pastille. */
|
|
18
|
+
radius?: string
|
|
19
|
+
}>(),
|
|
20
|
+
{ width: '100%', height: 12, radius: undefined },
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const size = (value: string | number) => (typeof value === 'number' ? `${value}px` : value)
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<div
|
|
28
|
+
class="rm-skeleton"
|
|
29
|
+
aria-hidden="true"
|
|
30
|
+
:style="{ width: size(width), height: size(height), borderRadius: radius }"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
@@ -117,7 +117,7 @@ const sortedRows = computed(() => {
|
|
|
117
117
|
<tbody v-if="loading">
|
|
118
118
|
<tr v-for="i in 5" :key="i">
|
|
119
119
|
<td v-for="col in columns" :key="col.key" class="rm-table__td">
|
|
120
|
-
<div class="rm-table__skeleton" />
|
|
120
|
+
<div class="rm-skeleton rm-table__skeleton" />
|
|
121
121
|
</td>
|
|
122
122
|
</tr>
|
|
123
123
|
</tbody>
|
|
@@ -158,5 +158,5 @@ const sortedRows = computed(() => {
|
|
|
158
158
|
</template>
|
|
159
159
|
|
|
160
160
|
<style scoped>
|
|
161
|
-
.rm-table-wrapper{background:#fff;border:1px solid #dbdef0;border-radius:8px;flex:1;overflow:auto}.rm-table{border-collapse:collapse;font-family:var(--r-font-family);font-size:var(--r-text-sm);width:100%}.rm-table__head{background:var(--r-color-gray-50)}.rm-table__head--sticky{position:sticky;top:0;z-index:2}.rm-table__th{border-bottom:1px solid #dbdef0;color:var(--r-color-gray-500);font-size:12px;font-weight:600;letter-spacing:.3px;padding:10px 16px;text-align:left;text-transform:uppercase;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.rm-table__th--center{text-align:center}.rm-table__th--right{text-align:right}.rm-table__th--sortable{cursor:pointer}.rm-table__th--sortable:hover{color:var(--r-color-gray-700)}.rm-table__th-content{align-items:center;display:inline-flex;gap:4px}.rm-table__sort{color:var(--r-color-gray-300);display:inline-flex;flex-direction:column;gap:-2px}.rm-table__sort--active{color:#4164ee}.rm-table__td{border-bottom:1px solid var(--r-color-gray-100);color:var(--r-color-gray-700);padding:10px 16px;vertical-align:top}.rm-table__td--center{text-align:center}.rm-table__td--right{text-align:right}.rm-table__row:last-child .rm-table__td{border-bottom:none}.rm-table__row--striped{background-color:var(--r-color-gray-50)}.rm-table__row--hoverable:hover{background-color:rgba(65,100,238,.06);cursor:pointer}.rm-table__empty{color:var(--
|
|
161
|
+
.rm-table-wrapper{background:#fff;border:1px solid #dbdef0;border-radius:8px;flex:1;overflow:auto}.rm-table{border-collapse:collapse;font-family:var(--r-font-family);font-size:var(--r-text-sm);width:100%}.rm-table__head{background:var(--r-color-gray-50)}.rm-table__head--sticky{position:sticky;top:0;z-index:2}.rm-table__th{border-bottom:1px solid #dbdef0;color:var(--r-color-gray-500);font-size:12px;font-weight:600;letter-spacing:.3px;padding:10px 16px;text-align:left;text-transform:uppercase;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.rm-table__th--center{text-align:center}.rm-table__th--right{text-align:right}.rm-table__th--sortable{cursor:pointer}.rm-table__th--sortable:hover{color:var(--r-color-gray-700)}.rm-table__th-content{align-items:center;display:inline-flex;gap:4px}.rm-table__sort{color:var(--r-color-gray-300);display:inline-flex;flex-direction:column;gap:-2px}.rm-table__sort--active{color:#4164ee}.rm-table__td{border-bottom:1px solid var(--r-color-gray-100);color:var(--r-color-gray-700);padding:10px 16px;vertical-align:top}.rm-table__td--center{text-align:center}.rm-table__td--right{text-align:right}.rm-table__row:last-child .rm-table__td{border-bottom:none}.rm-table__row--striped{background-color:var(--r-color-gray-50)}.rm-table__row--hoverable:hover{background-color:rgba(65,100,238,.06);cursor:pointer}.rm-table__empty{color:var(--text-muted);padding:32px 16px;text-align:center}.rm-table__skeleton{height:14px;width:80%}
|
|
162
162
|
</style>
|