@svadmin/ui 0.78.2 → 0.79.0

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/app.css CHANGED
@@ -9551,6 +9551,191 @@ details.svadmin-collapsible:not([open]) > :not(summary) {
9551
9551
  max-width: 32rem;
9552
9552
  }
9553
9553
 
9554
+ /* --- Auditable content primitives --- */
9555
+ .svadmin-filter-bottom-sheet[data-slot="sheet"] {
9556
+ inset-block-start: auto;
9557
+ inset-block-end: 0;
9558
+ inset-inline: 0;
9559
+ width: 100%;
9560
+ max-width: none;
9561
+ height: auto;
9562
+ max-height: 85dvh;
9563
+ overflow-y: auto;
9564
+ padding: 1rem 1rem max(1rem, env(safe-area-inset-bottom));
9565
+ border-radius: var(--radius-lg) var(--radius-lg) 0 0;
9566
+ }
9567
+ .svadmin-filter-bottom-sheet .svadmin-sheet-close { min-width: 44px; min-height: 44px; }
9568
+ .svadmin-filter-bottom-sheet [data-slot="sheet-content"] { display: grid; gap: 1rem; }
9569
+ .layout-clean-flat [data-svadmin-audit-section],
9570
+ .layout-clean-flat [data-svadmin-chart-container],
9571
+ .layout-clean-flat [data-svadmin-evidence-card],
9572
+ .layout-clean-flat [data-svadmin-coverage-matrix] {
9573
+ min-width: 0;
9574
+ }
9575
+
9576
+ .layout-clean-flat [data-svadmin-audit-section] {
9577
+ position: relative;
9578
+ border-block: 1px solid var(--svadmin-border, var(--border));
9579
+ background: var(--svadmin-surface, var(--card));
9580
+ }
9581
+
9582
+ .layout-clean-flat [data-svadmin-audit-section]::before,
9583
+ .layout-clean-flat [data-svadmin-evidence-card]::before {
9584
+ position: absolute;
9585
+ inset-block: 0;
9586
+ inset-inline-start: 0;
9587
+ width: 4px;
9588
+ background: var(--svadmin-accent-color, var(--primary));
9589
+ content: "";
9590
+ }
9591
+
9592
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="info"],
9593
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="info"],
9594
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="info"] {
9595
+ --svadmin-accent-color: var(--info);
9596
+ }
9597
+
9598
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="success"],
9599
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="success"],
9600
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="success"] {
9601
+ --svadmin-accent-color: var(--success);
9602
+ }
9603
+
9604
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="warning"],
9605
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="warning"],
9606
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="warning"] {
9607
+ --svadmin-accent-color: var(--warning);
9608
+ }
9609
+
9610
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="danger"],
9611
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="danger"],
9612
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="danger"] {
9613
+ --svadmin-accent-color: var(--destructive);
9614
+ }
9615
+
9616
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="neutral"],
9617
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="neutral"],
9618
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="neutral"] {
9619
+ --svadmin-accent-color: var(--muted-foreground);
9620
+ }
9621
+
9622
+ .svadmin-audit-section__header,
9623
+ .svadmin-chart-container__header,
9624
+ .svadmin-evidence-card__header {
9625
+ display: flex;
9626
+ align-items: flex-start;
9627
+ justify-content: space-between;
9628
+ gap: 1rem;
9629
+ }
9630
+
9631
+ .svadmin-audit-section__header {
9632
+ padding: 1rem 1.25rem;
9633
+ border-block-end: 1px solid var(--svadmin-border, var(--border));
9634
+ }
9635
+
9636
+ .svadmin-audit-section__heading,
9637
+ .svadmin-chart-container__header > div {
9638
+ min-width: 0;
9639
+ }
9640
+
9641
+ .svadmin-audit-section__eyebrow {
9642
+ margin: 0 0 0.25rem;
9643
+ color: var(--svadmin-text-muted, var(--muted-foreground));
9644
+ font-size: 0.6875rem;
9645
+ font-weight: 600;
9646
+ letter-spacing: 0;
9647
+ }
9648
+
9649
+ .svadmin-audit-section__header h2,
9650
+ .svadmin-chart-container__header h3,
9651
+ .svadmin-evidence-card__header h3 {
9652
+ margin: 0;
9653
+ color: var(--svadmin-text, var(--foreground));
9654
+ font-size: 0.9375rem;
9655
+ font-weight: 600;
9656
+ line-height: 1.3;
9657
+ }
9658
+
9659
+ .svadmin-audit-section__description,
9660
+ .svadmin-chart-container__header p,
9661
+ .svadmin-evidence-card__description,
9662
+ .svadmin-evidence-card__source {
9663
+ margin: 0.25rem 0 0;
9664
+ color: var(--svadmin-text-muted, var(--muted-foreground));
9665
+ font-size: 0.75rem;
9666
+ line-height: 1.45;
9667
+ }
9668
+
9669
+ .svadmin-audit-section__body {
9670
+ min-width: 0;
9671
+ }
9672
+
9673
+ .svadmin-audit-section__header,
9674
+ .svadmin-audit-section__body,
9675
+ .svadmin-evidence-card__header,
9676
+ .svadmin-evidence-card__description,
9677
+ .svadmin-evidence-card__source {
9678
+ overflow-wrap: anywhere;
9679
+ }
9680
+ .svadmin-audit-section__actions { flex-shrink: 0; max-width: 100%; }
9681
+ .svadmin-audit-notice {
9682
+ display: flex;
9683
+ flex-wrap: wrap;
9684
+ align-items: center;
9685
+ justify-content: space-between;
9686
+ gap: 0.75rem;
9687
+ padding: 0.75rem 1rem;
9688
+ border-inline-start: 4px solid var(--warning);
9689
+ background: var(--muted);
9690
+ font-size: 0.8125rem;
9691
+ }
9692
+ .svadmin-audit-notice[data-state="readonly"] { border-inline-start-color: var(--muted-foreground); }
9693
+ .svadmin-status-pill[data-slot="badge"] { border-radius: 999px; white-space: normal; overflow-wrap: anywhere; max-width: 100%; }
9694
+ .layout-clean-flat [data-svadmin-coverage-matrix]:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
9695
+ .layout-clean-flat [data-svadmin-coverage-matrix] table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
9696
+ .layout-clean-flat [data-svadmin-coverage-matrix] caption { padding: 0.75rem; text-align: start; font-weight: 600; }
9697
+ .layout-clean-flat [data-svadmin-coverage-matrix] :is(th, td) { padding: 0.75rem; text-align: start; min-width: 9rem; overflow-wrap: anywhere; border-bottom: 1px solid var(--border); }
9698
+ .layout-clean-flat [data-svadmin-coverage-matrix] thead { color: var(--muted-foreground); background: var(--muted); }
9699
+ .layout-clean-flat [data-svadmin-coverage-matrix] tbody th { border-inline-start: 4px solid var(--svadmin-accent-color, var(--border)); }
9700
+ .svadmin-evidence-card__header { flex-wrap: wrap; }
9701
+ @media (max-width: 767px) {
9702
+ .svadmin-audit-section__header { flex-wrap: wrap; }
9703
+ .svadmin-audit-section__actions a { display: inline-flex; align-items: center; min-height: 44px; }
9704
+ }
9705
+
9706
+ .svadmin-chart-container__body {
9707
+ min-width: 0;
9708
+ padding: 1rem;
9709
+ }
9710
+
9711
+ .layout-clean-flat [data-svadmin-evidence-card] {
9712
+ position: relative;
9713
+ min-width: 0;
9714
+ border: 1px solid var(--svadmin-border, var(--border));
9715
+ background: var(--svadmin-surface, var(--card));
9716
+ padding: 1rem 1rem 1rem 1.25rem;
9717
+ }
9718
+
9719
+ .svadmin-evidence-card__body {
9720
+ margin-top: 0.75rem;
9721
+ color: var(--svadmin-text, var(--foreground));
9722
+ font-size: 0.8125rem;
9723
+ line-height: 1.5;
9724
+ }
9725
+
9726
+ .svadmin-evidence-card__source {
9727
+ margin-top: 1rem;
9728
+ color: var(--primary);
9729
+ font-weight: 600;
9730
+ }
9731
+
9732
+ .layout-clean-flat [data-svadmin-coverage-matrix] {
9733
+ overflow-x: auto;
9734
+ border: 1px solid var(--svadmin-border, var(--border));
9735
+ background: var(--svadmin-surface, var(--card));
9736
+ }
9737
+
9738
+
9554
9739
  @media (min-width: 768px) {
9555
9740
  .layout-clean-flat [data-svadmin-form-settings] [data-svadmin-field] {
9556
9741
  display: grid;
@@ -10108,7 +10293,8 @@ details.svadmin-collapsible:not([open]) > :not(summary) {
10108
10293
  }
10109
10294
 
10110
10295
  .layout-clean-flat[data-theme="stripe"] .sidebar-menu-item-active::before {
10111
- content: none;
10296
+ width: 4px;
10297
+ content: "";
10112
10298
  }
10113
10299
 
10114
10300
  .layout-clean-flat[data-theme="stripe"] .sidebar-menu-item-active svg {
@@ -9551,6 +9551,191 @@ details.svadmin-collapsible:not([open]) > :not(summary) {
9551
9551
  max-width: 32rem;
9552
9552
  }
9553
9553
 
9554
+ /* --- Auditable content primitives --- */
9555
+ .svadmin-filter-bottom-sheet[data-slot="sheet"] {
9556
+ inset-block-start: auto;
9557
+ inset-block-end: 0;
9558
+ inset-inline: 0;
9559
+ width: 100%;
9560
+ max-width: none;
9561
+ height: auto;
9562
+ max-height: 85dvh;
9563
+ overflow-y: auto;
9564
+ padding: 1rem 1rem max(1rem, env(safe-area-inset-bottom));
9565
+ border-radius: var(--radius-lg) var(--radius-lg) 0 0;
9566
+ }
9567
+ .svadmin-filter-bottom-sheet .svadmin-sheet-close { min-width: 44px; min-height: 44px; }
9568
+ .svadmin-filter-bottom-sheet [data-slot="sheet-content"] { display: grid; gap: 1rem; }
9569
+ .layout-clean-flat [data-svadmin-audit-section],
9570
+ .layout-clean-flat [data-svadmin-chart-container],
9571
+ .layout-clean-flat [data-svadmin-evidence-card],
9572
+ .layout-clean-flat [data-svadmin-coverage-matrix] {
9573
+ min-width: 0;
9574
+ }
9575
+
9576
+ .layout-clean-flat [data-svadmin-audit-section] {
9577
+ position: relative;
9578
+ border-block: 1px solid var(--svadmin-border, var(--border));
9579
+ background: var(--svadmin-surface, var(--card));
9580
+ }
9581
+
9582
+ .layout-clean-flat [data-svadmin-audit-section]::before,
9583
+ .layout-clean-flat [data-svadmin-evidence-card]::before {
9584
+ position: absolute;
9585
+ inset-block: 0;
9586
+ inset-inline-start: 0;
9587
+ width: 4px;
9588
+ background: var(--svadmin-accent-color, var(--primary));
9589
+ content: "";
9590
+ }
9591
+
9592
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="info"],
9593
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="info"],
9594
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="info"] {
9595
+ --svadmin-accent-color: var(--info);
9596
+ }
9597
+
9598
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="success"],
9599
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="success"],
9600
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="success"] {
9601
+ --svadmin-accent-color: var(--success);
9602
+ }
9603
+
9604
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="warning"],
9605
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="warning"],
9606
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="warning"] {
9607
+ --svadmin-accent-color: var(--warning);
9608
+ }
9609
+
9610
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="danger"],
9611
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="danger"],
9612
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="danger"] {
9613
+ --svadmin-accent-color: var(--destructive);
9614
+ }
9615
+
9616
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="neutral"],
9617
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="neutral"],
9618
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="neutral"] {
9619
+ --svadmin-accent-color: var(--muted-foreground);
9620
+ }
9621
+
9622
+ .svadmin-audit-section__header,
9623
+ .svadmin-chart-container__header,
9624
+ .svadmin-evidence-card__header {
9625
+ display: flex;
9626
+ align-items: flex-start;
9627
+ justify-content: space-between;
9628
+ gap: 1rem;
9629
+ }
9630
+
9631
+ .svadmin-audit-section__header {
9632
+ padding: 1rem 1.25rem;
9633
+ border-block-end: 1px solid var(--svadmin-border, var(--border));
9634
+ }
9635
+
9636
+ .svadmin-audit-section__heading,
9637
+ .svadmin-chart-container__header > div {
9638
+ min-width: 0;
9639
+ }
9640
+
9641
+ .svadmin-audit-section__eyebrow {
9642
+ margin: 0 0 0.25rem;
9643
+ color: var(--svadmin-text-muted, var(--muted-foreground));
9644
+ font-size: 0.6875rem;
9645
+ font-weight: 600;
9646
+ letter-spacing: 0;
9647
+ }
9648
+
9649
+ .svadmin-audit-section__header h2,
9650
+ .svadmin-chart-container__header h3,
9651
+ .svadmin-evidence-card__header h3 {
9652
+ margin: 0;
9653
+ color: var(--svadmin-text, var(--foreground));
9654
+ font-size: 0.9375rem;
9655
+ font-weight: 600;
9656
+ line-height: 1.3;
9657
+ }
9658
+
9659
+ .svadmin-audit-section__description,
9660
+ .svadmin-chart-container__header p,
9661
+ .svadmin-evidence-card__description,
9662
+ .svadmin-evidence-card__source {
9663
+ margin: 0.25rem 0 0;
9664
+ color: var(--svadmin-text-muted, var(--muted-foreground));
9665
+ font-size: 0.75rem;
9666
+ line-height: 1.45;
9667
+ }
9668
+
9669
+ .svadmin-audit-section__body {
9670
+ min-width: 0;
9671
+ }
9672
+
9673
+ .svadmin-audit-section__header,
9674
+ .svadmin-audit-section__body,
9675
+ .svadmin-evidence-card__header,
9676
+ .svadmin-evidence-card__description,
9677
+ .svadmin-evidence-card__source {
9678
+ overflow-wrap: anywhere;
9679
+ }
9680
+ .svadmin-audit-section__actions { flex-shrink: 0; max-width: 100%; }
9681
+ .svadmin-audit-notice {
9682
+ display: flex;
9683
+ flex-wrap: wrap;
9684
+ align-items: center;
9685
+ justify-content: space-between;
9686
+ gap: 0.75rem;
9687
+ padding: 0.75rem 1rem;
9688
+ border-inline-start: 4px solid var(--warning);
9689
+ background: var(--muted);
9690
+ font-size: 0.8125rem;
9691
+ }
9692
+ .svadmin-audit-notice[data-state="readonly"] { border-inline-start-color: var(--muted-foreground); }
9693
+ .svadmin-status-pill[data-slot="badge"] { border-radius: 999px; white-space: normal; overflow-wrap: anywhere; max-width: 100%; }
9694
+ .layout-clean-flat [data-svadmin-coverage-matrix]:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
9695
+ .layout-clean-flat [data-svadmin-coverage-matrix] table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
9696
+ .layout-clean-flat [data-svadmin-coverage-matrix] caption { padding: 0.75rem; text-align: start; font-weight: 600; }
9697
+ .layout-clean-flat [data-svadmin-coverage-matrix] :is(th, td) { padding: 0.75rem; text-align: start; min-width: 9rem; overflow-wrap: anywhere; border-bottom: 1px solid var(--border); }
9698
+ .layout-clean-flat [data-svadmin-coverage-matrix] thead { color: var(--muted-foreground); background: var(--muted); }
9699
+ .layout-clean-flat [data-svadmin-coverage-matrix] tbody th { border-inline-start: 4px solid var(--svadmin-accent-color, var(--border)); }
9700
+ .svadmin-evidence-card__header { flex-wrap: wrap; }
9701
+ @media (max-width: 767px) {
9702
+ .svadmin-audit-section__header { flex-wrap: wrap; }
9703
+ .svadmin-audit-section__actions a { display: inline-flex; align-items: center; min-height: 44px; }
9704
+ }
9705
+
9706
+ .svadmin-chart-container__body {
9707
+ min-width: 0;
9708
+ padding: 1rem;
9709
+ }
9710
+
9711
+ .layout-clean-flat [data-svadmin-evidence-card] {
9712
+ position: relative;
9713
+ min-width: 0;
9714
+ border: 1px solid var(--svadmin-border, var(--border));
9715
+ background: var(--svadmin-surface, var(--card));
9716
+ padding: 1rem 1rem 1rem 1.25rem;
9717
+ }
9718
+
9719
+ .svadmin-evidence-card__body {
9720
+ margin-top: 0.75rem;
9721
+ color: var(--svadmin-text, var(--foreground));
9722
+ font-size: 0.8125rem;
9723
+ line-height: 1.5;
9724
+ }
9725
+
9726
+ .svadmin-evidence-card__source {
9727
+ margin-top: 1rem;
9728
+ color: var(--primary);
9729
+ font-weight: 600;
9730
+ }
9731
+
9732
+ .layout-clean-flat [data-svadmin-coverage-matrix] {
9733
+ overflow-x: auto;
9734
+ border: 1px solid var(--svadmin-border, var(--border));
9735
+ background: var(--svadmin-surface, var(--card));
9736
+ }
9737
+
9738
+
9554
9739
  @media (min-width: 768px) {
9555
9740
  .layout-clean-flat [data-svadmin-form-settings] [data-svadmin-field] {
9556
9741
  display: grid;
@@ -10108,7 +10293,8 @@ details.svadmin-collapsible:not([open]) > :not(summary) {
10108
10293
  }
10109
10294
 
10110
10295
  .layout-clean-flat[data-theme="stripe"] .sidebar-menu-item-active::before {
10111
- content: none;
10296
+ width: 4px;
10297
+ content: "";
10112
10298
  }
10113
10299
 
10114
10300
  .layout-clean-flat[data-theme="stripe"] .sidebar-menu-item-active svg {
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { definedOptions } from '@svadmin/core/options';
4
+ import { useTranslation } from '@svadmin/core/i18n';
5
+ import DataState from './DataState.svelte';
6
+ import { Button } from '../ui/button/index.js';
7
+ import { RefreshCw } from '@lucide/svelte';
8
+ import type { AuditDataState } from './audit-state.js';
9
+
10
+ let { state = 'ready', message, retry, children }: {
11
+ state?: AuditDataState;
12
+ message?: string | undefined;
13
+ retry?: (() => void) | undefined;
14
+ children?: Snippet | undefined;
15
+ } = $props();
16
+ const i18n = useTranslation();
17
+ </script>
18
+
19
+ {#if state === 'loading' || state === 'empty' || state === 'error' || state === 'forbidden'}
20
+ <DataState {state} {...definedOptions({ title: message, retry: state === 'error' ? retry : undefined })} />
21
+ {:else}
22
+ {#if state === 'partial' || state === 'readonly'}
23
+ <div class="svadmin-audit-notice" data-state={state} role="status">
24
+ <span>{message ?? (state === 'partial'
25
+ ? (i18n.locale === 'zh-CN' ? '部分数据暂不可用' : 'Some data is unavailable')
26
+ : (i18n.locale === 'zh-CN' ? '只读' : 'Read only'))}</span>
27
+ {#if state === 'partial' && retry}
28
+ <Button variant="outline" size="sm" onclick={retry}><RefreshCw size={16} aria-hidden="true" />{i18n.t('common.retry')}</Button>
29
+ {/if}
30
+ </div>
31
+ {/if}
32
+ {@render children?.()}
33
+ {/if}
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { AuditDataState } from './audit-state.js';
3
+ type $$ComponentProps = {
4
+ state?: AuditDataState;
5
+ message?: string | undefined;
6
+ retry?: (() => void) | undefined;
7
+ children?: Snippet | undefined;
8
+ };
9
+ declare const AuditContent: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type AuditContent = ReturnType<typeof AuditContent>;
11
+ export default AuditContent;
@@ -0,0 +1,46 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../../utils.js';
4
+ import AuditContent from './AuditContent.svelte';
5
+ import type { AuditDataState } from './audit-state.js';
6
+
7
+ export type AuditAccent = 'primary' | 'info' | 'success' | 'warning' | 'danger' | 'neutral';
8
+
9
+ interface Props {
10
+ title: string;
11
+ description?: string | undefined;
12
+ eyebrow?: string;
13
+ accent?: AuditAccent;
14
+ actions?: Snippet | undefined;
15
+ children?: Snippet;
16
+ class?: string;
17
+ state?: AuditDataState;
18
+ message?: string | undefined;
19
+ retry?: (() => void) | undefined;
20
+ }
21
+
22
+ let {
23
+ title,
24
+ description,
25
+ eyebrow,
26
+ accent = 'primary',
27
+ actions,
28
+ children,
29
+ class: className = '',
30
+ state = 'ready',
31
+ message,
32
+ retry,
33
+ }: Props = $props();
34
+ </script>
35
+
36
+ <section aria-label={title} class={cn('svadmin-audit-section', className)} data-svadmin-audit-section data-accent={accent} data-state={state}>
37
+ <header class="svadmin-audit-section__header">
38
+ <div class="svadmin-audit-section__heading">
39
+ {#if eyebrow}<p class="svadmin-audit-section__eyebrow">{eyebrow}</p>{/if}
40
+ <h2>{title}</h2>
41
+ {#if description}<p class="svadmin-audit-section__description">{description}</p>{/if}
42
+ </div>
43
+ {#if actions && state !== 'forbidden'}<div class="svadmin-audit-section__actions">{@render actions()}</div>{/if}
44
+ </header>
45
+ <div class="svadmin-audit-section__body"><AuditContent {state} {message} {retry} {children} /></div>
46
+ </section>
@@ -0,0 +1,18 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { AuditDataState } from './audit-state.js';
3
+ export type AuditAccent = 'primary' | 'info' | 'success' | 'warning' | 'danger' | 'neutral';
4
+ interface Props {
5
+ title: string;
6
+ description?: string | undefined;
7
+ eyebrow?: string;
8
+ accent?: AuditAccent;
9
+ actions?: Snippet | undefined;
10
+ children?: Snippet;
11
+ class?: string;
12
+ state?: AuditDataState;
13
+ message?: string | undefined;
14
+ retry?: (() => void) | undefined;
15
+ }
16
+ declare const AuditSection: import("svelte").Component<Props, {}, "">;
17
+ type AuditSection = ReturnType<typeof AuditSection>;
18
+ export default AuditSection;
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../../utils.js';
4
+ import AuditSection from './AuditSection.svelte';
5
+ import type { AuditDataState } from './audit-state.js';
6
+
7
+ interface Props {
8
+ title: string;
9
+ description?: string;
10
+ actions?: Snippet;
11
+ children?: Snippet;
12
+ class?: string;
13
+ state?: AuditDataState;
14
+ message?: string | undefined;
15
+ retry?: (() => void) | undefined;
16
+ }
17
+
18
+ let {
19
+ title,
20
+ description,
21
+ actions,
22
+ children,
23
+ class: className = '',
24
+ state = 'ready',
25
+ message,
26
+ retry,
27
+ }: Props = $props();
28
+ </script>
29
+
30
+ <AuditSection {title} {description} {actions} {state} {message} {retry} accent="info" class={cn('svadmin-chart-container', className)}>
31
+ <div class="svadmin-chart-container__body">{@render children?.()}</div>
32
+ </AuditSection>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { AuditDataState } from './audit-state.js';
3
+ interface Props {
4
+ title: string;
5
+ description?: string;
6
+ actions?: Snippet;
7
+ children?: Snippet;
8
+ class?: string;
9
+ state?: AuditDataState;
10
+ message?: string | undefined;
11
+ retry?: (() => void) | undefined;
12
+ }
13
+ declare const ChartContainer: import("svelte").Component<Props, {}, "">;
14
+ type ChartContainer = ReturnType<typeof ChartContainer>;
15
+ export default ChartContainer;
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils.js';
3
+ import type { AuditAccent } from './AuditSection.svelte';
4
+ import AuditContent from './AuditContent.svelte';
5
+ import type { AuditDataState } from './audit-state.js';
6
+
7
+ export interface CoverageMatrixColumn {
8
+ key: string;
9
+ label: string;
10
+ }
11
+
12
+ export interface CoverageMatrixRow {
13
+ id: string;
14
+ title: string;
15
+ cells: Readonly<Record<string, string | undefined>>;
16
+ accent?: AuditAccent;
17
+ }
18
+
19
+ interface Props {
20
+ columns: readonly CoverageMatrixColumn[];
21
+ rows: readonly CoverageMatrixRow[];
22
+ class?: string;
23
+ caption: string;
24
+ rowLabel?: string;
25
+ state?: AuditDataState;
26
+ message?: string | undefined;
27
+ retry?: (() => void) | undefined;
28
+ }
29
+
30
+ let { columns, rows, caption, rowLabel = '', state = 'ready', message, retry, class: className = '' }: Props = $props();
31
+ </script>
32
+
33
+ <AuditContent state={state === 'ready' && rows.length === 0 ? 'empty' : state} {message} {retry}>
34
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex (命名滚动区域需要键盘访问。) -->
35
+ <div class={cn('svadmin-coverage-matrix', className)} data-svadmin-coverage-matrix role="region" aria-label={caption} tabindex="0">
36
+ <table>
37
+ <caption>{caption}</caption>
38
+ <thead><tr><th scope="col">{rowLabel}</th>{#each columns as column (column.key)}<th scope="col">{column.label}</th>{/each}</tr></thead>
39
+ <tbody>
40
+ {#each rows as row (row.id)}
41
+ <tr data-accent={row.accent ?? 'neutral'}>
42
+ <th scope="row">{row.title}</th>
43
+ {#each columns as column (column.key)}<td>{row.cells[column.key] ?? '—'}</td>{/each}
44
+ </tr>
45
+ {/each}
46
+ </tbody>
47
+ </table>
48
+ </div>
49
+ </AuditContent>
@@ -0,0 +1,25 @@
1
+ import type { AuditAccent } from './AuditSection.svelte';
2
+ import type { AuditDataState } from './audit-state.js';
3
+ export interface CoverageMatrixColumn {
4
+ key: string;
5
+ label: string;
6
+ }
7
+ export interface CoverageMatrixRow {
8
+ id: string;
9
+ title: string;
10
+ cells: Readonly<Record<string, string | undefined>>;
11
+ accent?: AuditAccent;
12
+ }
13
+ interface Props {
14
+ columns: readonly CoverageMatrixColumn[];
15
+ rows: readonly CoverageMatrixRow[];
16
+ class?: string;
17
+ caption: string;
18
+ rowLabel?: string;
19
+ state?: AuditDataState;
20
+ message?: string | undefined;
21
+ retry?: (() => void) | undefined;
22
+ }
23
+ declare const CoverageMatrix: import("svelte").Component<Props, {}, "">;
24
+ type CoverageMatrix = ReturnType<typeof CoverageMatrix>;
25
+ export default CoverageMatrix;
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../../utils.js';
4
+ import StatusPill, { type StatusPillTone } from './StatusPill.svelte';
5
+
6
+ interface Props {
7
+ title: string;
8
+ status: string;
9
+ statusTone?: StatusPillTone;
10
+ description?: string;
11
+ source?: string;
12
+ children?: Snippet;
13
+ class?: string;
14
+ }
15
+
16
+ let {
17
+ title,
18
+ status,
19
+ statusTone = 'neutral',
20
+ description,
21
+ source,
22
+ children,
23
+ class: className = '',
24
+ }: Props = $props();
25
+ </script>
26
+
27
+ <article aria-label={title} class={cn('svadmin-evidence-card', className)} data-svadmin-evidence-card data-accent={statusTone}>
28
+ <div class="svadmin-evidence-card__header">
29
+ <h3>{title}</h3>
30
+ <StatusPill label={status} tone={statusTone} />
31
+ </div>
32
+ {#if description}<p class="svadmin-evidence-card__description">{description}</p>{/if}
33
+ {#if children}<div class="svadmin-evidence-card__body">{@render children()}</div>{/if}
34
+ {#if source}<p class="svadmin-evidence-card__source">{source}</p>{/if}
35
+ </article>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type StatusPillTone } from './StatusPill.svelte';
3
+ interface Props {
4
+ title: string;
5
+ status: string;
6
+ statusTone?: StatusPillTone;
7
+ description?: string;
8
+ source?: string;
9
+ children?: Snippet;
10
+ class?: string;
11
+ }
12
+ declare const EvidenceCard: import("svelte").Component<Props, {}, "">;
13
+ type EvidenceCard = ReturnType<typeof EvidenceCard>;
14
+ export default EvidenceCard;
@@ -1,5 +1,7 @@
1
1
  <script lang="ts">
2
- import type { Snippet } from 'svelte';
2
+ import { onMount, type Snippet } from 'svelte';
3
+ import { useTranslation } from '@svadmin/core/i18n';
4
+ import * as Sheet from '../ui/sheet/index.js';
3
5
  import { Search, X, SlidersHorizontal, ChevronDown, ChevronUp } from '@lucide/svelte';
4
6
  import { Button } from '../ui/button/index.js';
5
7
  import { Input } from '../ui/input/index.js';
@@ -40,6 +42,15 @@
40
42
 
41
43
  const isCompact = $derived(density === 'compact');
42
44
  const advancedPanelId = `svadmin-filter-toolbar-advanced-${uid}`;
45
+ const i18n = useTranslation();
46
+ let mobile = $state(false);
47
+ onMount(() => {
48
+ const media = window.matchMedia('(max-width: 767px)');
49
+ const update = () => { mobile = media.matches; };
50
+ update();
51
+ media.addEventListener('change', update);
52
+ return () => media.removeEventListener('change', update);
53
+ });
43
54
  </script>
44
55
 
45
56
  <div class={cn('svadmin-filter-toolbar', className)} data-density={density} data-svadmin-filter-toolbar data-advanced-open={advanced && advancedOpen ? 'true' : 'false'}>
@@ -67,12 +78,12 @@
67
78
  {/if}
68
79
  </div>
69
80
  {/if}
70
- {#if filters}
81
+ {#if filters && !mobile}
71
82
  <div class="svadmin-filter-toolbar__filters">
72
83
  {@render filters()}
73
84
  </div>
74
85
  {/if}
75
- {#if advanced}
86
+ {#if advanced || (mobile && filters)}
76
87
  <Button
77
88
  variant={advancedOpen ? 'secondary' : 'outline'}
78
89
  size={isCompact ? 'sm' : 'default'}
@@ -101,7 +112,16 @@
101
112
  </div>
102
113
  {/if}
103
114
  </div>
104
- {#if advanced && advancedOpen}
115
+ {#if mobile && (advanced || filters)}
116
+ <Sheet.Root bind:open={advancedOpen} class="svadmin-filter-bottom-sheet" role="dialog" aria-modal="true"
117
+ aria-label={advancedLabel} id={advancedPanelId} closeLabel={i18n.t('common.close')}>
118
+ <Sheet.Header><Sheet.Title>{advancedLabel}</Sheet.Title></Sheet.Header>
119
+ <Sheet.Content>
120
+ {@render filters?.()}
121
+ {@render advanced?.()}
122
+ </Sheet.Content>
123
+ </Sheet.Root>
124
+ {:else if advanced && advancedOpen}
105
125
  <div id={advancedPanelId} role="region" aria-label={advancedLabel} class="svadmin-filter-toolbar-advanced" aria-hidden="false">
106
126
  {@render advanced()}
107
127
  </div>
@@ -1,4 +1,4 @@
1
- import type { Snippet } from 'svelte';
1
+ import { type Snippet } from 'svelte';
2
2
  interface Props {
3
3
  query?: string;
4
4
  placeholder?: string;
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils.js';
3
+ import StatusBadge from './StatusBadge.svelte';
4
+
5
+ export type StatusPillTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral';
6
+
7
+ interface Props {
8
+ label: string;
9
+ tone?: StatusPillTone;
10
+ class?: string;
11
+ }
12
+
13
+ let { label, tone = 'neutral', class: className = '' }: Props = $props();
14
+ </script>
15
+
16
+ <StatusBadge status={tone} {label} class={cn('svadmin-status-pill', className)} />
@@ -0,0 +1,9 @@
1
+ export type StatusPillTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral';
2
+ interface Props {
3
+ label: string;
4
+ tone?: StatusPillTone;
5
+ class?: string;
6
+ }
7
+ declare const StatusPill: import("svelte").Component<Props, {}, "">;
8
+ type StatusPill = ReturnType<typeof StatusPill>;
9
+ export default StatusPill;
@@ -0,0 +1 @@
1
+ export type AuditDataState = 'ready' | 'loading' | 'empty' | 'partial' | 'error' | 'forbidden' | 'readonly';
@@ -0,0 +1 @@
1
+ export {};
@@ -48,5 +48,14 @@ export { default as FeedbackNotice } from './FeedbackNotice.svelte';
48
48
  export type { FeedbackNoticeTone, FeedbackNoticePriority } from './FeedbackNotice.svelte';
49
49
  export { default as AuditTimeline } from './AuditTimeline.svelte';
50
50
  export type { TimelineItem } from './AuditTimeline.svelte';
51
+ export { default as AuditSection } from './AuditSection.svelte';
52
+ export type { AuditAccent } from './AuditSection.svelte';
53
+ export type { AuditDataState } from './audit-state.js';
54
+ export { default as ChartContainer } from './ChartContainer.svelte';
55
+ export { default as CoverageMatrix } from './CoverageMatrix.svelte';
56
+ export type { CoverageMatrixColumn, CoverageMatrixRow } from './CoverageMatrix.svelte';
57
+ export { default as EvidenceCard } from './EvidenceCard.svelte';
58
+ export { default as StatusPill } from './StatusPill.svelte';
59
+ export type { StatusPillTone } from './StatusPill.svelte';
51
60
  export { default as MediaThumbnail } from './MediaThumbnail.svelte';
52
61
  export type { MediaThumbnailSize, MediaThumbnailFit } from './MediaThumbnail.svelte';
@@ -33,4 +33,9 @@ export { default as TwoFactorStepper } from './TwoFactorStepper.svelte';
33
33
  export { default as SystemErrorState } from './SystemErrorState.svelte';
34
34
  export { default as FeedbackNotice } from './FeedbackNotice.svelte';
35
35
  export { default as AuditTimeline } from './AuditTimeline.svelte';
36
+ export { default as AuditSection } from './AuditSection.svelte';
37
+ export { default as ChartContainer } from './ChartContainer.svelte';
38
+ export { default as CoverageMatrix } from './CoverageMatrix.svelte';
39
+ export { default as EvidenceCard } from './EvidenceCard.svelte';
40
+ export { default as StatusPill } from './StatusPill.svelte';
36
41
  export { default as MediaThumbnail } from './MediaThumbnail.svelte';
package/dist/index.d.ts CHANGED
@@ -330,6 +330,15 @@ export type { RowActionItem } from './components/RowActions.svelte';
330
330
  export { default as DetailDrawer } from './components/DetailDrawer.svelte';
331
331
  export { default as AuditTimeline } from './components/content/AuditTimeline.svelte';
332
332
  export type { TimelineItem } from './components/content/AuditTimeline.svelte';
333
+ export { default as AuditSection } from './components/content/AuditSection.svelte';
334
+ export type { AuditAccent } from './components/content/AuditSection.svelte';
335
+ export type { AuditDataState } from './components/content/audit-state.js';
336
+ export { default as ChartContainer } from './components/content/ChartContainer.svelte';
337
+ export { default as CoverageMatrix } from './components/content/CoverageMatrix.svelte';
338
+ export type { CoverageMatrixColumn, CoverageMatrixRow } from './components/content/CoverageMatrix.svelte';
339
+ export { default as EvidenceCard } from './components/content/EvidenceCard.svelte';
340
+ export { default as StatusPill } from './components/content/StatusPill.svelte';
341
+ export type { StatusPillTone } from './components/content/StatusPill.svelte';
333
342
  export type { TagTone } from './components/fields/TagField.svelte';
334
343
  export type { BooleanTone } from './components/fields/BooleanField.svelte';
335
344
  export type { DateFieldFormat } from './components/fields/DateField.svelte';
package/dist/index.js CHANGED
@@ -279,6 +279,11 @@ export { referenceDemoData } from './reference-data.js';
279
279
  export { default as RowActions } from './components/RowActions.svelte';
280
280
  export { default as DetailDrawer } from './components/DetailDrawer.svelte';
281
281
  export { default as AuditTimeline } from './components/content/AuditTimeline.svelte';
282
+ export { default as AuditSection } from './components/content/AuditSection.svelte';
283
+ export { default as ChartContainer } from './components/content/ChartContainer.svelte';
284
+ export { default as CoverageMatrix } from './components/content/CoverageMatrix.svelte';
285
+ export { default as EvidenceCard } from './components/content/EvidenceCard.svelte';
286
+ export { default as StatusPill } from './components/content/StatusPill.svelte';
282
287
  export { default as FilterDropdown } from './components/FilterDropdown.svelte';
283
288
  export { default as BatchActionBar } from './components/BatchActionBar.svelte';
284
289
  export { default as MediaThumbnail } from './components/content/MediaThumbnail.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/ui",
3
- "version": "0.78.2",
3
+ "version": "0.79.0",
4
4
  "description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
5
5
  "type": "module",
6
6
  "sideEffects": [