@happyvertical/smrt-ui 0.30.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/AGENTS.md +50 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/dist/actions/__tests__/ripple.test.js +28 -0
- package/dist/actions/permission.d.ts +34 -0
- package/dist/actions/permission.d.ts.map +1 -0
- package/dist/actions/permission.js +70 -0
- package/dist/actions/ripple.d.ts +7 -0
- package/dist/actions/ripple.d.ts.map +1 -0
- package/dist/actions/ripple.js +65 -0
- package/dist/components/calendar/Calendar.svelte +520 -0
- package/dist/components/calendar/Calendar.svelte.d.ts +17 -0
- package/dist/components/calendar/Calendar.svelte.d.ts.map +1 -0
- package/dist/components/calendar/DayView.svelte +389 -0
- package/dist/components/calendar/DayView.svelte.d.ts +13 -0
- package/dist/components/calendar/DayView.svelte.d.ts.map +1 -0
- package/dist/components/calendar/index.d.ts +6 -0
- package/dist/components/calendar/index.d.ts.map +1 -0
- package/dist/components/calendar/index.js +5 -0
- package/dist/components/chat/MessageBubble.svelte +126 -0
- package/dist/components/chat/MessageBubble.svelte.d.ts +30 -0
- package/dist/components/chat/MessageBubble.svelte.d.ts.map +1 -0
- package/dist/components/chat/ReactionPicker.svelte +89 -0
- package/dist/components/chat/ReactionPicker.svelte.d.ts +19 -0
- package/dist/components/chat/ReactionPicker.svelte.d.ts.map +1 -0
- package/dist/components/chat/TypingIndicator.svelte +90 -0
- package/dist/components/chat/TypingIndicator.svelte.d.ts +17 -0
- package/dist/components/chat/TypingIndicator.svelte.d.ts.map +1 -0
- package/dist/components/chat/__tests__/chat-primitives.test.js +67 -0
- package/dist/components/chat/index.d.ts +10 -0
- package/dist/components/chat/index.d.ts.map +1 -0
- package/dist/components/chat/index.js +9 -0
- package/dist/components/data/DataTable.svelte +519 -0
- package/dist/components/data/DataTable.svelte.d.ts +49 -0
- package/dist/components/data/DataTable.svelte.d.ts.map +1 -0
- package/dist/components/data/__tests__/DataTable.test.js +48 -0
- package/dist/components/data/__tests__/data-table-helpers.test.js +36 -0
- package/dist/components/data/index.d.ts +6 -0
- package/dist/components/data/index.d.ts.map +1 -0
- package/dist/components/data/index.js +5 -0
- package/dist/components/data/types.d.ts +104 -0
- package/dist/components/data/types.d.ts.map +1 -0
- package/dist/components/data/types.js +45 -0
- package/dist/components/display/ConfidenceBadge.svelte +142 -0
- package/dist/components/display/ConfidenceBadge.svelte.d.ts +25 -0
- package/dist/components/display/ConfidenceBadge.svelte.d.ts.map +1 -0
- package/dist/components/display/CurrencyDisplay.svelte +106 -0
- package/dist/components/display/CurrencyDisplay.svelte.d.ts +30 -0
- package/dist/components/display/CurrencyDisplay.svelte.d.ts.map +1 -0
- package/dist/components/display/DateDisplay.svelte +122 -0
- package/dist/components/display/DateDisplay.svelte.d.ts +24 -0
- package/dist/components/display/DateDisplay.svelte.d.ts.map +1 -0
- package/dist/components/display/Icon.svelte +77 -0
- package/dist/components/display/Icon.svelte.d.ts +28 -0
- package/dist/components/display/Icon.svelte.d.ts.map +1 -0
- package/dist/components/display/StatusBadge.svelte +256 -0
- package/dist/components/display/StatusBadge.svelte.d.ts +24 -0
- package/dist/components/display/StatusBadge.svelte.d.ts.map +1 -0
- package/dist/components/display/__tests__/ConfidenceBadge.test.js +96 -0
- package/dist/components/display/__tests__/CurrencyDisplay.test.js +114 -0
- package/dist/components/display/__tests__/DateDisplay.test.js +114 -0
- package/dist/components/display/__tests__/Icon.test.js +93 -0
- package/dist/components/display/__tests__/StatusBadge.test.js +98 -0
- package/dist/components/display/index.d.ts +10 -0
- package/dist/components/display/index.d.ts.map +1 -0
- package/dist/components/display/index.js +9 -0
- package/dist/components/display/types.d.ts +5 -0
- package/dist/components/display/types.d.ts.map +1 -0
- package/dist/components/display/types.js +4 -0
- package/dist/components/feedback/ConfirmDialog.svelte +226 -0
- package/dist/components/feedback/ConfirmDialog.svelte.d.ts +25 -0
- package/dist/components/feedback/ConfirmDialog.svelte.d.ts.map +1 -0
- package/dist/components/feedback/LoadingOverlay.svelte +281 -0
- package/dist/components/feedback/LoadingOverlay.svelte.d.ts +31 -0
- package/dist/components/feedback/LoadingOverlay.svelte.d.ts.map +1 -0
- package/dist/components/feedback/Modal.svelte +393 -0
- package/dist/components/feedback/Modal.svelte.d.ts +46 -0
- package/dist/components/feedback/Modal.svelte.d.ts.map +1 -0
- package/dist/components/feedback/ProgressBar.svelte +162 -0
- package/dist/components/feedback/ProgressBar.svelte.d.ts +21 -0
- package/dist/components/feedback/ProgressBar.svelte.d.ts.map +1 -0
- package/dist/components/feedback/__tests__/ConfirmDialog.test.js +111 -0
- package/dist/components/feedback/__tests__/LoadingOverlay.test.js +99 -0
- package/dist/components/feedback/__tests__/Modal.test.js +72 -0
- package/dist/components/feedback/__tests__/ProgressBar.test.js +89 -0
- package/dist/components/feedback/index.d.ts +8 -0
- package/dist/components/feedback/index.d.ts.map +1 -0
- package/dist/components/feedback/index.js +10 -0
- package/dist/components/layout/Container.svelte +53 -0
- package/dist/components/layout/Container.svelte.d.ts +11 -0
- package/dist/components/layout/Container.svelte.d.ts.map +1 -0
- package/dist/components/layout/EmptyState.svelte +187 -0
- package/dist/components/layout/EmptyState.svelte.d.ts +28 -0
- package/dist/components/layout/EmptyState.svelte.d.ts.map +1 -0
- package/dist/components/layout/Footer.svelte +63 -0
- package/dist/components/layout/Footer.svelte.d.ts +8 -0
- package/dist/components/layout/Footer.svelte.d.ts.map +1 -0
- package/dist/components/layout/Grid.svelte +241 -0
- package/dist/components/layout/Grid.svelte.d.ts +56 -0
- package/dist/components/layout/Grid.svelte.d.ts.map +1 -0
- package/dist/components/layout/Header.svelte +86 -0
- package/dist/components/layout/Header.svelte.d.ts +9 -0
- package/dist/components/layout/Header.svelte.d.ts.map +1 -0
- package/dist/components/layout/Masthead.svelte +219 -0
- package/dist/components/layout/Masthead.svelte.d.ts +13 -0
- package/dist/components/layout/Masthead.svelte.d.ts.map +1 -0
- package/dist/components/layout/PageHeader.svelte +131 -0
- package/dist/components/layout/PageHeader.svelte.d.ts +26 -0
- package/dist/components/layout/PageHeader.svelte.d.ts.map +1 -0
- package/dist/components/layout/SummaryCard.svelte +203 -0
- package/dist/components/layout/SummaryCard.svelte.d.ts +20 -0
- package/dist/components/layout/SummaryCard.svelte.d.ts.map +1 -0
- package/dist/components/layout/__tests__/Container.test.js +62 -0
- package/dist/components/layout/__tests__/EmptyState.test.js +83 -0
- package/dist/components/layout/__tests__/Footer.test.js +50 -0
- package/dist/components/layout/__tests__/Grid.test.js +121 -0
- package/dist/components/layout/__tests__/Header.test.js +48 -0
- package/dist/components/layout/__tests__/Masthead.test.js +93 -0
- package/dist/components/layout/__tests__/PageHeader.test.js +80 -0
- package/dist/components/layout/__tests__/SummaryCard.test.js +82 -0
- package/dist/components/layout/index.d.ts +12 -0
- package/dist/components/layout/index.d.ts.map +1 -0
- package/dist/components/layout/index.js +11 -0
- package/dist/components/memberships/MembershipCard.svelte +163 -0
- package/dist/components/memberships/MembershipCard.svelte.d.ts +12 -0
- package/dist/components/memberships/MembershipCard.svelte.d.ts.map +1 -0
- package/dist/components/memberships/MembershipList.svelte +98 -0
- package/dist/components/memberships/MembershipList.svelte.d.ts +19 -0
- package/dist/components/memberships/MembershipList.svelte.d.ts.map +1 -0
- package/dist/components/nav/FilterChips.svelte +152 -0
- package/dist/components/nav/FilterChips.svelte.d.ts +19 -0
- package/dist/components/nav/FilterChips.svelte.d.ts.map +1 -0
- package/dist/components/nav/Tabs.svelte +252 -0
- package/dist/components/nav/Tabs.svelte.d.ts +34 -0
- package/dist/components/nav/Tabs.svelte.d.ts.map +1 -0
- package/dist/components/nav/__tests__/FilterChips.test.js +94 -0
- package/dist/components/nav/__tests__/Tabs.test.js +128 -0
- package/dist/components/nav/index.d.ts +7 -0
- package/dist/components/nav/index.d.ts.map +1 -0
- package/dist/components/nav/index.js +6 -0
- package/dist/components/nav/types.d.ts +24 -0
- package/dist/components/nav/types.d.ts.map +1 -0
- package/dist/components/nav/types.js +4 -0
- package/dist/components/permissions/PermissionCheck.svelte +45 -0
- package/dist/components/permissions/PermissionCheck.svelte.d.ts +19 -0
- package/dist/components/permissions/PermissionCheck.svelte.d.ts.map +1 -0
- package/dist/components/roles/RoleBadge.svelte +84 -0
- package/dist/components/roles/RoleBadge.svelte.d.ts +13 -0
- package/dist/components/roles/RoleBadge.svelte.d.ts.map +1 -0
- package/dist/components/roles/RoleSelector.svelte +216 -0
- package/dist/components/roles/RoleSelector.svelte.d.ts +13 -0
- package/dist/components/roles/RoleSelector.svelte.d.ts.map +1 -0
- package/dist/components/theme/ThemeProvider.svelte +71 -0
- package/dist/components/theme/ThemeProvider.svelte.d.ts +10 -0
- package/dist/components/theme/ThemeProvider.svelte.d.ts.map +1 -0
- package/dist/components/theme/context.svelte.d.ts +15 -0
- package/dist/components/theme/context.svelte.d.ts.map +1 -0
- package/dist/components/theme/context.svelte.js +42 -0
- package/dist/components/theme/index.d.ts +3 -0
- package/dist/components/theme/index.d.ts.map +1 -0
- package/dist/components/theme/index.js +2 -0
- package/dist/components/ui/Avatar.svelte +167 -0
- package/dist/components/ui/Avatar.svelte.d.ts +26 -0
- package/dist/components/ui/Avatar.svelte.d.ts.map +1 -0
- package/dist/components/ui/Badge.svelte +70 -0
- package/dist/components/ui/Badge.svelte.d.ts +12 -0
- package/dist/components/ui/Badge.svelte.d.ts.map +1 -0
- package/dist/components/ui/Button.svelte +226 -0
- package/dist/components/ui/Button.svelte.d.ts +28 -0
- package/dist/components/ui/Button.svelte.d.ts.map +1 -0
- package/dist/components/ui/Card.svelte +122 -0
- package/dist/components/ui/Card.svelte.d.ts +15 -0
- package/dist/components/ui/Card.svelte.d.ts.map +1 -0
- package/dist/components/ui/Chip.svelte +167 -0
- package/dist/components/ui/Chip.svelte.d.ts +33 -0
- package/dist/components/ui/Chip.svelte.d.ts.map +1 -0
- package/dist/components/ui/Dropdown.svelte +250 -0
- package/dist/components/ui/Dropdown.svelte.d.ts +20 -0
- package/dist/components/ui/Dropdown.svelte.d.ts.map +1 -0
- package/dist/components/ui/Pagination.svelte +294 -0
- package/dist/components/ui/Pagination.svelte.d.ts +21 -0
- package/dist/components/ui/Pagination.svelte.d.ts.map +1 -0
- package/dist/components/ui/Skeleton.svelte +113 -0
- package/dist/components/ui/Skeleton.svelte.d.ts +24 -0
- package/dist/components/ui/Skeleton.svelte.d.ts.map +1 -0
- package/dist/components/ui/Tooltip.svelte +120 -0
- package/dist/components/ui/Tooltip.svelte.d.ts +24 -0
- package/dist/components/ui/Tooltip.svelte.d.ts.map +1 -0
- package/dist/components/ui/Tree.svelte +209 -0
- package/dist/components/ui/Tree.svelte.d.ts +17 -0
- package/dist/components/ui/Tree.svelte.d.ts.map +1 -0
- package/dist/components/ui/__tests__/Badge.test.js +76 -0
- package/dist/components/ui/__tests__/Button.test.js +69 -0
- package/dist/components/ui/__tests__/Card.test.js +103 -0
- package/dist/components/ui/__tests__/Pagination.test.js +99 -0
- package/dist/components/ui/__tests__/gap-primitives-interactive.test.js +112 -0
- package/dist/components/ui/__tests__/gap-primitives.test.js +84 -0
- package/dist/components/ui/index.d.ts +14 -0
- package/dist/components/ui/index.d.ts.map +1 -0
- package/dist/components/ui/index.js +18 -0
- package/dist/i18n/Trans.svelte +29 -0
- package/dist/i18n/Trans.svelte.d.ts +24 -0
- package/dist/i18n/Trans.svelte.d.ts.map +1 -0
- package/dist/i18n/__tests__/i18n.test.js +74 -0
- package/dist/i18n/__tests__/render-parity.spec.js +37 -0
- package/dist/i18n/context.svelte.d.ts +43 -0
- package/dist/i18n/context.svelte.d.ts.map +1 -0
- package/dist/i18n/context.svelte.js +69 -0
- package/dist/i18n/index.d.ts +17 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +24 -0
- package/dist/i18n/registry.d.ts +44 -0
- package/dist/i18n/registry.d.ts.map +1 -0
- package/dist/i18n/registry.js +60 -0
- package/dist/i18n/render.d.ts +22 -0
- package/dist/i18n/render.d.ts.map +1 -0
- package/dist/i18n/render.js +44 -0
- package/dist/i18n/strings.d.ts +7 -0
- package/dist/i18n/strings.d.ts.map +1 -0
- package/dist/i18n/strings.js +19 -0
- package/dist/i18n/strings.ui.d.ts +34 -0
- package/dist/i18n/strings.ui.d.ts.map +1 -0
- package/dist/i18n/strings.ui.js +44 -0
- package/dist/i18n/use-i18n.d.ts +20 -0
- package/dist/i18n/use-i18n.d.ts.map +1 -0
- package/dist/i18n/use-i18n.js +21 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/registry/index.d.ts +6 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/module-registry.d.ts +58 -0
- package/dist/registry/module-registry.d.ts.map +1 -0
- package/dist/registry/module-registry.js +94 -0
- package/dist/styles/index.d.ts +4 -0
- package/dist/styles/index.d.ts.map +1 -0
- package/dist/styles/index.js +6 -0
- package/dist/styles/tokens.css +76 -0
- package/dist/test-support/a11y.d.ts +16 -0
- package/dist/test-support/a11y.d.ts.map +1 -0
- package/dist/test-support/a11y.js +32 -0
- package/dist/test-support/setup.d.ts +11 -0
- package/dist/test-support/setup.d.ts.map +1 -0
- package/dist/test-support/setup.js +33 -0
- package/dist/theme/ThemeProvider.svelte +207 -0
- package/dist/theme/ThemeProvider.svelte.d.ts +22 -0
- package/dist/theme/ThemeProvider.svelte.d.ts.map +1 -0
- package/dist/theme/context.d.ts +49 -0
- package/dist/theme/context.d.ts.map +1 -0
- package/dist/theme/context.js +32 -0
- package/dist/theme/index.d.ts +7 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +9 -0
- package/dist/theme/tokens.d.ts +309 -0
- package/dist/theme/tokens.d.ts.map +1 -0
- package/dist/theme/tokens.js +418 -0
- package/dist/themes/CUSTOM_THEME_GUIDE.md +341 -0
- package/dist/themes/README.md +675 -0
- package/dist/themes/ThemeProvider.svelte +275 -0
- package/dist/themes/ThemeProvider.svelte.d.ts +24 -0
- package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -0
- package/dist/themes/__tests__/css-generator.test.js +32 -0
- package/dist/themes/__tests__/registry.test.js +43 -0
- package/dist/themes/__tests__/token-aliases.test.js +176 -0
- package/dist/themes/components/ColorSchemeToggle.svelte +205 -0
- package/dist/themes/components/ColorSchemeToggle.svelte.d.ts +14 -0
- package/dist/themes/components/ColorSchemeToggle.svelte.d.ts.map +1 -0
- package/dist/themes/components/ThemeSwitcher.svelte +188 -0
- package/dist/themes/components/ThemeSwitcher.svelte.d.ts +14 -0
- package/dist/themes/components/ThemeSwitcher.svelte.d.ts.map +1 -0
- package/dist/themes/components/index.d.ts +8 -0
- package/dist/themes/components/index.d.ts.map +1 -0
- package/dist/themes/components/index.js +7 -0
- package/dist/themes/context.svelte.d.ts +30 -0
- package/dist/themes/context.svelte.d.ts.map +1 -0
- package/dist/themes/context.svelte.js +42 -0
- package/dist/themes/create-theme.d.ts +99 -0
- package/dist/themes/create-theme.d.ts.map +1 -0
- package/dist/themes/create-theme.js +389 -0
- package/dist/themes/css-generator.d.ts +44 -0
- package/dist/themes/css-generator.d.ts.map +1 -0
- package/dist/themes/css-generator.js +226 -0
- package/dist/themes/glass/index.d.ts +14 -0
- package/dist/themes/glass/index.d.ts.map +1 -0
- package/dist/themes/glass/index.js +286 -0
- package/dist/themes/index.d.ts +31 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +37 -0
- package/dist/themes/material/index.d.ts +13 -0
- package/dist/themes/material/index.d.ts.map +1 -0
- package/dist/themes/material/index.js +269 -0
- package/dist/themes/registry.d.ts +64 -0
- package/dist/themes/registry.d.ts.map +1 -0
- package/dist/themes/registry.js +122 -0
- package/dist/themes/shared.d.ts +78 -0
- package/dist/themes/shared.d.ts.map +1 -0
- package/dist/themes/shared.js +179 -0
- package/dist/themes/studio/index.d.ts +14 -0
- package/dist/themes/studio/index.d.ts.map +1 -0
- package/dist/themes/studio/index.js +270 -0
- package/dist/themes/styles/all.css +12 -0
- package/dist/themes/styles/glass.css +432 -0
- package/dist/themes/styles/index.d.ts +22 -0
- package/dist/themes/styles/index.d.ts.map +1 -0
- package/dist/themes/styles/index.js +23 -0
- package/dist/themes/styles/material.css +364 -0
- package/dist/themes/styles/studio.css +416 -0
- package/dist/themes/types.d.ts +273 -0
- package/dist/themes/types.d.ts.map +1 -0
- package/dist/themes/types.js +15 -0
- package/dist/types-generic.d.ts +75 -0
- package/dist/types-generic.d.ts.map +1 -0
- package/dist/types-generic.js +1 -0
- package/dist/utils/forms/__tests__/formatters.test.js +27 -0
- package/dist/utils/forms/formatters.d.ts +14 -0
- package/dist/utils/forms/formatters.d.ts.map +1 -0
- package/dist/utils/forms/formatters.js +77 -0
- package/dist/utils/import-optional.d.ts +5 -0
- package/dist/utils/import-optional.d.ts.map +1 -0
- package/dist/utils/import-optional.js +7 -0
- package/dist/utils/theme/__tests__/color.test.js +72 -0
- package/dist/utils/theme/__tests__/typography.test.js +11 -0
- package/dist/utils/theme/color.d.ts +70 -0
- package/dist/utils/theme/color.d.ts.map +1 -0
- package/dist/utils/theme/color.js +221 -0
- package/dist/utils/theme/typography.d.ts +27 -0
- package/dist/utils/theme/typography.d.ts.map +1 -0
- package/dist/utils/theme/typography.js +30 -0
- package/package.json +143 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component tests for DateDisplay (Sweep S11, #1416).
|
|
3
|
+
*
|
|
4
|
+
* DateDisplay parses a Date/ISO/timestamp and renders a <time datetime> element
|
|
5
|
+
* (or a fallback <span> for null/invalid input). Expected formatted strings are
|
|
6
|
+
* derived via Intl.DateTimeFormat with the SAME options the component uses, so
|
|
7
|
+
* assertions stay locale/timezone-robust. Relative mode is exercised with
|
|
8
|
+
* offsets from `now`. Tests assert the real API from DateDisplay.svelte.
|
|
9
|
+
*/
|
|
10
|
+
import { render, screen } from '@testing-library/svelte';
|
|
11
|
+
import { describe, expect, it } from 'vitest';
|
|
12
|
+
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
13
|
+
import DateDisplay from '../DateDisplay.svelte';
|
|
14
|
+
const LOCALE = 'en-CA';
|
|
15
|
+
/** Build the component's expected output for an absolute format. */
|
|
16
|
+
function expected(date, options, locale = LOCALE) {
|
|
17
|
+
return new Intl.DateTimeFormat(locale, options).format(date);
|
|
18
|
+
}
|
|
19
|
+
const SHORT = { month: 'short', day: 'numeric' };
|
|
20
|
+
const MEDIUM = { year: 'numeric', month: 'short', day: 'numeric' };
|
|
21
|
+
const LONG = {
|
|
22
|
+
weekday: 'long',
|
|
23
|
+
year: 'numeric',
|
|
24
|
+
month: 'long',
|
|
25
|
+
day: 'numeric',
|
|
26
|
+
};
|
|
27
|
+
describe('DateDisplay', () => {
|
|
28
|
+
// Use a fixed local-noon date so date-part is timezone-stable.
|
|
29
|
+
const sample = new Date(2024, 0, 15, 12, 0, 0); // 15 Jan 2024, local noon
|
|
30
|
+
it('renders a <time> element with the ISO datetime attribute', () => {
|
|
31
|
+
const { container } = render(DateDisplay, { props: { date: sample } });
|
|
32
|
+
const time = container.querySelector('time');
|
|
33
|
+
expect(time).toBeInTheDocument();
|
|
34
|
+
expect(time).toHaveAttribute('datetime', sample.toISOString());
|
|
35
|
+
});
|
|
36
|
+
it('formats with medium options by default', () => {
|
|
37
|
+
render(DateDisplay, { props: { date: sample } });
|
|
38
|
+
expect(screen.getByText(expected(sample, MEDIUM))).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
it('formats with short options', () => {
|
|
41
|
+
render(DateDisplay, { props: { date: sample, format: 'short' } });
|
|
42
|
+
expect(screen.getByText(expected(sample, SHORT))).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
it('formats with long options', () => {
|
|
45
|
+
render(DateDisplay, { props: { date: sample, format: 'long' } });
|
|
46
|
+
expect(screen.getByText(expected(sample, LONG))).toBeInTheDocument();
|
|
47
|
+
});
|
|
48
|
+
it('includes time when showTime is set', () => {
|
|
49
|
+
render(DateDisplay, { props: { date: sample, showTime: true } });
|
|
50
|
+
const withTime = expected(sample, {
|
|
51
|
+
...MEDIUM,
|
|
52
|
+
hour: 'numeric',
|
|
53
|
+
minute: '2-digit',
|
|
54
|
+
});
|
|
55
|
+
expect(screen.getByText(withTime)).toBeInTheDocument();
|
|
56
|
+
});
|
|
57
|
+
it('respects a custom locale', () => {
|
|
58
|
+
render(DateDisplay, {
|
|
59
|
+
props: { date: sample, format: 'long', locale: 'fr-CA' },
|
|
60
|
+
});
|
|
61
|
+
expect(screen.getByText(expected(sample, LONG, 'fr-CA'))).toBeInTheDocument();
|
|
62
|
+
});
|
|
63
|
+
it('parses an ISO string input', () => {
|
|
64
|
+
const iso = '2024-01-15T12:00:00.000Z';
|
|
65
|
+
const d = new Date(iso);
|
|
66
|
+
const { container } = render(DateDisplay, { props: { date: iso } });
|
|
67
|
+
expect(container.querySelector('time')).toHaveAttribute('datetime', d.toISOString());
|
|
68
|
+
});
|
|
69
|
+
it('parses a numeric timestamp input', () => {
|
|
70
|
+
const ts = sample.getTime();
|
|
71
|
+
const { container } = render(DateDisplay, { props: { date: ts } });
|
|
72
|
+
expect(container.querySelector('time')).toHaveAttribute('datetime', new Date(ts).toISOString());
|
|
73
|
+
});
|
|
74
|
+
it('renders the fallback span for null input', () => {
|
|
75
|
+
const { container } = render(DateDisplay, {
|
|
76
|
+
props: { date: null, fallback: 'No date' },
|
|
77
|
+
});
|
|
78
|
+
expect(container.querySelector('time')).not.toBeInTheDocument();
|
|
79
|
+
const span = screen.getByText('No date');
|
|
80
|
+
expect(span).toHaveClass('date-fallback');
|
|
81
|
+
});
|
|
82
|
+
it('renders the default fallback for an invalid date string', () => {
|
|
83
|
+
render(DateDisplay, { props: { date: 'not-a-date' } });
|
|
84
|
+
expect(screen.getByText('N/A')).toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
it('renders "just now" for the current time in relative mode', () => {
|
|
87
|
+
render(DateDisplay, { props: { date: new Date(), format: 'relative' } });
|
|
88
|
+
expect(screen.getByText('just now')).toBeInTheDocument();
|
|
89
|
+
});
|
|
90
|
+
it('renders "yesterday" for ~1 day ago in relative mode', () => {
|
|
91
|
+
const d = new Date(Date.now() - 25 * 60 * 60 * 1000); // 25h ago → 1 day
|
|
92
|
+
render(DateDisplay, { props: { date: d, format: 'relative' } });
|
|
93
|
+
expect(screen.getByText('yesterday')).toBeInTheDocument();
|
|
94
|
+
});
|
|
95
|
+
it('renders "tomorrow" for a near-future date in relative mode', () => {
|
|
96
|
+
const d = new Date(Date.now() + 23 * 60 * 60 * 1000); // 23h ahead → tomorrow
|
|
97
|
+
render(DateDisplay, { props: { date: d, format: 'relative' } });
|
|
98
|
+
expect(screen.getByText('tomorrow')).toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
it('appends a custom class to the time element', () => {
|
|
101
|
+
const { container } = render(DateDisplay, {
|
|
102
|
+
props: { date: sample, class: 'my-date' },
|
|
103
|
+
});
|
|
104
|
+
expect(container.querySelector('time')).toHaveClass('my-date');
|
|
105
|
+
});
|
|
106
|
+
it('is axe-clean with a date', async () => {
|
|
107
|
+
const { container } = render(DateDisplay, { props: { date: sample } });
|
|
108
|
+
await expectNoA11yViolations(container);
|
|
109
|
+
});
|
|
110
|
+
it('is axe-clean with the fallback', async () => {
|
|
111
|
+
const { container } = render(DateDisplay, { props: { date: null } });
|
|
112
|
+
await expectNoA11yViolations(container);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component tests for Icon (Sweep S11, #1416).
|
|
3
|
+
*
|
|
4
|
+
* Icon is a presentational SVG primitive: decorative by default (aria-hidden),
|
|
5
|
+
* informative when given an aria-label (role="img"). Tests assert the real API
|
|
6
|
+
* from Icon.svelte — preset/path resolution, size/color, and axe-cleanliness in
|
|
7
|
+
* both decorative and labelled modes.
|
|
8
|
+
*/
|
|
9
|
+
import { render } from '@testing-library/svelte';
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
12
|
+
import Icon from '../Icon.svelte';
|
|
13
|
+
/** The 'check' preset path, used to assert preset resolution. */
|
|
14
|
+
const CHECK_PATH = 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z';
|
|
15
|
+
describe('Icon', () => {
|
|
16
|
+
it('renders an svg that is decorative (aria-hidden) by default', () => {
|
|
17
|
+
const { container } = render(Icon, { props: { name: 'check' } });
|
|
18
|
+
const svg = container.querySelector('svg');
|
|
19
|
+
expect(svg).toBeInTheDocument();
|
|
20
|
+
expect(svg).toHaveAttribute('aria-hidden', 'true');
|
|
21
|
+
expect(svg).not.toHaveAttribute('role');
|
|
22
|
+
});
|
|
23
|
+
it('resolves a named preset to its path', () => {
|
|
24
|
+
const { container } = render(Icon, { props: { name: 'check' } });
|
|
25
|
+
const path = container.querySelector('svg path');
|
|
26
|
+
expect(path).toHaveAttribute('d', CHECK_PATH);
|
|
27
|
+
});
|
|
28
|
+
it('renders a custom path verbatim, taking precedence over name', () => {
|
|
29
|
+
const custom = 'M0 0h10v10H0z';
|
|
30
|
+
const { container } = render(Icon, {
|
|
31
|
+
props: { name: 'check', path: custom },
|
|
32
|
+
});
|
|
33
|
+
const path = container.querySelector('svg path');
|
|
34
|
+
expect(path).toHaveAttribute('d', custom);
|
|
35
|
+
});
|
|
36
|
+
it('renders a path with no shape for an unknown preset name', () => {
|
|
37
|
+
// finalPath resolves to '' for an unknown preset; Svelte omits the empty
|
|
38
|
+
// `d` attribute entirely, so the path element renders without a shape.
|
|
39
|
+
const { container } = render(Icon, { props: { name: 'does-not-exist' } });
|
|
40
|
+
const path = container.querySelector('svg path');
|
|
41
|
+
expect(path).toBeInTheDocument();
|
|
42
|
+
expect(path).not.toHaveAttribute('d');
|
|
43
|
+
});
|
|
44
|
+
it('becomes informative (role=img + aria-label) when given an aria-label', () => {
|
|
45
|
+
const { container } = render(Icon, {
|
|
46
|
+
props: { name: 'search', 'aria-label': 'Search' },
|
|
47
|
+
});
|
|
48
|
+
const svg = container.querySelector('svg');
|
|
49
|
+
expect(svg).toHaveAttribute('role', 'img');
|
|
50
|
+
expect(svg).toHaveAttribute('aria-label', 'Search');
|
|
51
|
+
expect(svg).toHaveAttribute('aria-hidden', 'false');
|
|
52
|
+
});
|
|
53
|
+
it('converts a numeric size to a px width/height', () => {
|
|
54
|
+
const { container } = render(Icon, { props: { name: 'menu', size: 32 } });
|
|
55
|
+
const svg = container.querySelector('svg');
|
|
56
|
+
expect(svg).toHaveAttribute('width', '32px');
|
|
57
|
+
expect(svg).toHaveAttribute('height', '32px');
|
|
58
|
+
});
|
|
59
|
+
it('passes a string size through unchanged', () => {
|
|
60
|
+
const { container } = render(Icon, {
|
|
61
|
+
props: { name: 'menu', size: '1.5em' },
|
|
62
|
+
});
|
|
63
|
+
const svg = container.querySelector('svg');
|
|
64
|
+
expect(svg).toHaveAttribute('width', '1.5em');
|
|
65
|
+
expect(svg).toHaveAttribute('height', '1.5em');
|
|
66
|
+
});
|
|
67
|
+
it('applies the color prop as the svg fill', () => {
|
|
68
|
+
const { container } = render(Icon, {
|
|
69
|
+
props: { name: 'menu', color: '#ff0000' },
|
|
70
|
+
});
|
|
71
|
+
expect(container.querySelector('svg')).toHaveAttribute('fill', '#ff0000');
|
|
72
|
+
});
|
|
73
|
+
it('uses currentColor as the default fill', () => {
|
|
74
|
+
const { container } = render(Icon, { props: { name: 'menu' } });
|
|
75
|
+
expect(container.querySelector('svg')).toHaveAttribute('fill', 'currentColor');
|
|
76
|
+
});
|
|
77
|
+
it('applies a custom viewBox', () => {
|
|
78
|
+
const { container } = render(Icon, {
|
|
79
|
+
props: { name: 'menu', viewBox: '0 0 48 48' },
|
|
80
|
+
});
|
|
81
|
+
expect(container.querySelector('svg')).toHaveAttribute('viewBox', '0 0 48 48');
|
|
82
|
+
});
|
|
83
|
+
it('is axe-clean when decorative', async () => {
|
|
84
|
+
const { container } = render(Icon, { props: { name: 'close' } });
|
|
85
|
+
await expectNoA11yViolations(container);
|
|
86
|
+
});
|
|
87
|
+
it('is axe-clean when labelled', async () => {
|
|
88
|
+
const { container } = render(Icon, {
|
|
89
|
+
props: { name: 'close', 'aria-label': 'Close dialog' },
|
|
90
|
+
});
|
|
91
|
+
await expectNoA11yViolations(container);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component tests for StatusBadge (Sweep S11, #1416).
|
|
3
|
+
*
|
|
4
|
+
* StatusBadge is a presentational chip: it normalizes a status string, maps it
|
|
5
|
+
* to a color scheme per domain `type`, and renders a humanized label. Tests
|
|
6
|
+
* assert the real API from StatusBadge.svelte — label formatting, custom label
|
|
7
|
+
* override, size/variant classes, scheme-driven CSS custom properties, and
|
|
8
|
+
* axe-cleanliness across variants.
|
|
9
|
+
*/
|
|
10
|
+
import { render, screen } from '@testing-library/svelte';
|
|
11
|
+
import { describe, expect, it } from 'vitest';
|
|
12
|
+
import { expectNoA11yViolations } from '../../../test-support/a11y';
|
|
13
|
+
import StatusBadge from '../StatusBadge.svelte';
|
|
14
|
+
describe('StatusBadge', () => {
|
|
15
|
+
it('renders the status value as its label', () => {
|
|
16
|
+
render(StatusBadge, { props: { status: 'active' } });
|
|
17
|
+
expect(screen.getByText('active')).toBeInTheDocument();
|
|
18
|
+
});
|
|
19
|
+
it('humanizes underscores in the status into spaces', () => {
|
|
20
|
+
render(StatusBadge, { props: { status: 'on_hold', type: 'project' } });
|
|
21
|
+
expect(screen.getByText('on hold')).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
it('prefers an explicit label over the status value', () => {
|
|
24
|
+
render(StatusBadge, {
|
|
25
|
+
props: { status: 'paid', type: 'invoice', label: 'Paid in full' },
|
|
26
|
+
});
|
|
27
|
+
expect(screen.getByText('Paid in full')).toBeInTheDocument();
|
|
28
|
+
expect(screen.queryByText('paid')).not.toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
it('always carries the base status-badge class', () => {
|
|
31
|
+
const { container } = render(StatusBadge, { props: { status: 'active' } });
|
|
32
|
+
expect(container.querySelector('span')).toHaveClass('status-badge');
|
|
33
|
+
});
|
|
34
|
+
it.each([
|
|
35
|
+
['sm', 'sm'],
|
|
36
|
+
['lg', 'lg'],
|
|
37
|
+
])('applies the %s size class', (size, cls) => {
|
|
38
|
+
const { container } = render(StatusBadge, {
|
|
39
|
+
props: { status: 'active', size },
|
|
40
|
+
});
|
|
41
|
+
expect(container.querySelector('span')).toHaveClass(cls);
|
|
42
|
+
});
|
|
43
|
+
it('omits a size class for the default md size', () => {
|
|
44
|
+
const { container } = render(StatusBadge, { props: { status: 'active' } });
|
|
45
|
+
const span = container.querySelector('span');
|
|
46
|
+
expect(span).not.toHaveClass('sm');
|
|
47
|
+
expect(span).not.toHaveClass('lg');
|
|
48
|
+
});
|
|
49
|
+
it('applies the outline class for the outline variant', () => {
|
|
50
|
+
const { container } = render(StatusBadge, {
|
|
51
|
+
props: { status: 'active', variant: 'outline' },
|
|
52
|
+
});
|
|
53
|
+
expect(container.querySelector('span')).toHaveClass('outline');
|
|
54
|
+
});
|
|
55
|
+
it('maps a known status to its scheme colors via CSS custom properties', () => {
|
|
56
|
+
const { container } = render(StatusBadge, {
|
|
57
|
+
props: { status: 'overdue', type: 'invoice' },
|
|
58
|
+
});
|
|
59
|
+
const span = container.querySelector('span');
|
|
60
|
+
// invoice/overdue → error container scheme
|
|
61
|
+
expect(span.style.getPropertyValue('--badge-bg')).toContain('smrt-color-error-container');
|
|
62
|
+
expect(span.style.getPropertyValue('--badge-text')).toContain('smrt-color-on-error-container');
|
|
63
|
+
});
|
|
64
|
+
it('falls back to a neutral scheme for an unknown status', () => {
|
|
65
|
+
const { container } = render(StatusBadge, {
|
|
66
|
+
props: { status: 'totally-unknown', type: 'invoice' },
|
|
67
|
+
});
|
|
68
|
+
const span = container.querySelector('span');
|
|
69
|
+
expect(span.style.getPropertyValue('--badge-bg')).toContain('#e5e7eb');
|
|
70
|
+
expect(span.style.getPropertyValue('--badge-text')).toContain('#374151');
|
|
71
|
+
});
|
|
72
|
+
it('normalizes spaces and hyphens when matching a scheme key', () => {
|
|
73
|
+
// 'On-Hold' normalizes to 'on_hold', a known project status.
|
|
74
|
+
const { container } = render(StatusBadge, {
|
|
75
|
+
props: { status: 'On-Hold', type: 'project' },
|
|
76
|
+
});
|
|
77
|
+
const span = container.querySelector('span');
|
|
78
|
+
expect(span.style.getPropertyValue('--badge-bg')).toContain('smrt-color-secondary-container');
|
|
79
|
+
});
|
|
80
|
+
it.each([
|
|
81
|
+
['default', 'active'],
|
|
82
|
+
['invoice', 'paid'],
|
|
83
|
+
['project', 'completed'],
|
|
84
|
+
['expense', 'reimbursed'],
|
|
85
|
+
['time', 'approved'],
|
|
86
|
+
['compliance', 'valid'],
|
|
87
|
+
['estimate', 'accepted'],
|
|
88
|
+
])('is axe-clean for type=%s status=%s', async (type, status) => {
|
|
89
|
+
const { container } = render(StatusBadge, { props: { status, type } });
|
|
90
|
+
await expectNoA11yViolations(container);
|
|
91
|
+
});
|
|
92
|
+
it('is axe-clean for the outline variant', async () => {
|
|
93
|
+
const { container } = render(StatusBadge, {
|
|
94
|
+
props: { status: 'active', variant: 'outline' },
|
|
95
|
+
});
|
|
96
|
+
await expectNoA11yViolations(container);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display components - Read-only presentation components
|
|
3
|
+
*/
|
|
4
|
+
export { default as ConfidenceBadge } from './ConfidenceBadge.svelte';
|
|
5
|
+
export { default as CurrencyDisplay } from './CurrencyDisplay.svelte';
|
|
6
|
+
export { default as DateDisplay } from './DateDisplay.svelte';
|
|
7
|
+
export { default as Icon } from './Icon.svelte';
|
|
8
|
+
export { default as StatusBadge } from './StatusBadge.svelte';
|
|
9
|
+
export type { StatusType } from './types.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/display/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAO9D,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display components - Read-only presentation components
|
|
3
|
+
*/
|
|
4
|
+
// Export components
|
|
5
|
+
export { default as ConfidenceBadge } from './ConfidenceBadge.svelte';
|
|
6
|
+
export { default as CurrencyDisplay } from './CurrencyDisplay.svelte';
|
|
7
|
+
export { default as DateDisplay } from './DateDisplay.svelte';
|
|
8
|
+
export { default as Icon } from './Icon.svelte';
|
|
9
|
+
export { default as StatusBadge } from './StatusBadge.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/display/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,SAAS,GACT,SAAS,GACT,MAAM,GACN,YAAY,GACZ,UAAU,CAAC"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* ConfirmDialog - Modal confirmation dialog
|
|
4
|
+
* refactored for Material 3
|
|
5
|
+
*
|
|
6
|
+
* Provides a consistent confirmation dialog for destructive actions
|
|
7
|
+
* or important decisions.
|
|
8
|
+
*/
|
|
9
|
+
import { ripple } from '../../actions/ripple.js';
|
|
10
|
+
|
|
11
|
+
/** Props for ConfirmDialog component */
|
|
12
|
+
export interface Props {
|
|
13
|
+
/** Whether the dialog is open */
|
|
14
|
+
open: boolean;
|
|
15
|
+
/** Dialog title */
|
|
16
|
+
title: string;
|
|
17
|
+
/** Dialog message */
|
|
18
|
+
message: string;
|
|
19
|
+
/** Confirm button label */
|
|
20
|
+
confirmLabel?: string;
|
|
21
|
+
/** Cancel button label */
|
|
22
|
+
cancelLabel?: string;
|
|
23
|
+
/** Use destructive (red) styling for confirm */
|
|
24
|
+
destructive?: boolean;
|
|
25
|
+
/** Show loading state on confirm */
|
|
26
|
+
loading?: boolean;
|
|
27
|
+
/** Called when confirm is clicked */
|
|
28
|
+
onconfirm?: () => void;
|
|
29
|
+
/** Called when cancel is clicked or dialog closed */
|
|
30
|
+
oncancel?: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const {
|
|
34
|
+
open,
|
|
35
|
+
title,
|
|
36
|
+
message,
|
|
37
|
+
confirmLabel = 'Confirm',
|
|
38
|
+
cancelLabel = 'Cancel',
|
|
39
|
+
destructive = false,
|
|
40
|
+
loading = false,
|
|
41
|
+
onconfirm,
|
|
42
|
+
oncancel,
|
|
43
|
+
}: Props = $props();
|
|
44
|
+
|
|
45
|
+
function handleBackdropClick(e: MouseEvent) {
|
|
46
|
+
if (e.target === e.currentTarget) {
|
|
47
|
+
oncancel?.();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
52
|
+
if (e.key === 'Escape') {
|
|
53
|
+
oncancel?.();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
{#if open}
|
|
59
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
60
|
+
<div
|
|
61
|
+
class="dialog-backdrop"
|
|
62
|
+
role="dialog"
|
|
63
|
+
aria-modal="true"
|
|
64
|
+
aria-labelledby="dialog-title"
|
|
65
|
+
tabindex="-1"
|
|
66
|
+
onclick={handleBackdropClick}
|
|
67
|
+
onkeydown={handleKeydown}
|
|
68
|
+
>
|
|
69
|
+
<div class="dialog-content">
|
|
70
|
+
<h2 id="dialog-title" class="dialog-title">{title}</h2>
|
|
71
|
+
<p class="dialog-message">{message}</p>
|
|
72
|
+
|
|
73
|
+
<div class="dialog-actions">
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
class="btn btn-text"
|
|
77
|
+
onclick={oncancel}
|
|
78
|
+
disabled={loading}
|
|
79
|
+
use:ripple
|
|
80
|
+
>
|
|
81
|
+
{cancelLabel}
|
|
82
|
+
</button>
|
|
83
|
+
<button
|
|
84
|
+
type="button"
|
|
85
|
+
class="btn btn-filled"
|
|
86
|
+
class:destructive
|
|
87
|
+
onclick={onconfirm}
|
|
88
|
+
disabled={loading}
|
|
89
|
+
use:ripple
|
|
90
|
+
>
|
|
91
|
+
{#if loading}
|
|
92
|
+
<span class="spinner"></span>
|
|
93
|
+
{/if}
|
|
94
|
+
{confirmLabel}
|
|
95
|
+
</button>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
{/if}
|
|
100
|
+
|
|
101
|
+
<style>
|
|
102
|
+
.dialog-backdrop {
|
|
103
|
+
position: fixed;
|
|
104
|
+
inset: 0;
|
|
105
|
+
display: flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
justify-content: center;
|
|
108
|
+
background-color: var(--smrt-color-scrim, rgba(0, 0, 0, 0.4));
|
|
109
|
+
z-index: var(--smrt-z-index-dialog, 1000);
|
|
110
|
+
padding: 1rem;
|
|
111
|
+
backdrop-filter: blur(2px);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.dialog-content {
|
|
115
|
+
background-color: var(--smrt-color-surface-container-high);
|
|
116
|
+
border-radius: var(--smrt-radius-3xl, 32px);
|
|
117
|
+
padding: var(--smrt-spacing-6, 24px);
|
|
118
|
+
max-width: 400px;
|
|
119
|
+
width: 100%;
|
|
120
|
+
box-shadow: var(--smrt-elevation-3);
|
|
121
|
+
animation: dialogEnter 300ms cubic-bezier(0.2, 0, 0, 1);
|
|
122
|
+
display: flex;
|
|
123
|
+
flex-direction: column;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@keyframes dialogEnter {
|
|
127
|
+
from {
|
|
128
|
+
opacity: 0;
|
|
129
|
+
transform: translateY(20px) scale(0.9);
|
|
130
|
+
}
|
|
131
|
+
to {
|
|
132
|
+
opacity: 1;
|
|
133
|
+
transform: translateY(0) scale(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.dialog-title {
|
|
138
|
+
font: var(--smrt-typography-headline-small-font);
|
|
139
|
+
color: var(--smrt-color-on-surface);
|
|
140
|
+
margin: 0 0 var(--smrt-spacing-4, 16px);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.dialog-message {
|
|
144
|
+
font: var(--smrt-typography-body-medium-font);
|
|
145
|
+
color: var(--smrt-color-on-surface-variant);
|
|
146
|
+
margin: 0 0 var(--smrt-spacing-6, 24px);
|
|
147
|
+
line-height: 1.5;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.dialog-actions {
|
|
151
|
+
display: flex;
|
|
152
|
+
justify-content: flex-end;
|
|
153
|
+
gap: var(--smrt-spacing-2, 8px);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.btn {
|
|
157
|
+
display: inline-flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
justify-content: center;
|
|
160
|
+
gap: var(--smrt-spacing-2, 8px);
|
|
161
|
+
height: 40px;
|
|
162
|
+
padding: 0 var(--smrt-spacing-6, 24px);
|
|
163
|
+
font: var(--smrt-typography-label-large-font);
|
|
164
|
+
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
165
|
+
border-radius: var(--smrt-radius-2xl, 24px);
|
|
166
|
+
cursor: pointer;
|
|
167
|
+
transition: all var(--smrt-duration-short3, 200ms) var(--smrt-easing-standard, ease);
|
|
168
|
+
border: none;
|
|
169
|
+
position: relative;
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.btn:disabled {
|
|
174
|
+
opacity: 0.38;
|
|
175
|
+
cursor: not-allowed;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.btn-text {
|
|
179
|
+
background: transparent;
|
|
180
|
+
color: var(--smrt-color-primary);
|
|
181
|
+
padding: 0 var(--smrt-spacing-3, 12px);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.btn-text:hover:not(:disabled) {
|
|
185
|
+
background-color: var(--smrt-color-surface-container-highest);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.btn-filled {
|
|
189
|
+
background-color: var(--smrt-color-primary);
|
|
190
|
+
color: var(--smrt-color-on-primary);
|
|
191
|
+
box-shadow: var(--smrt-elevation-1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.btn-filled:hover:not(:disabled) {
|
|
195
|
+
box-shadow: var(--smrt-elevation-2);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.btn-filled.destructive {
|
|
199
|
+
background-color: var(--smrt-color-error);
|
|
200
|
+
color: var(--smrt-color-on-error);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.spinner {
|
|
204
|
+
width: 18px;
|
|
205
|
+
height: 18px;
|
|
206
|
+
border: 2px solid transparent;
|
|
207
|
+
border-top-color: currentColor;
|
|
208
|
+
border-radius: var(--smrt-radius-full, 9999px);
|
|
209
|
+
animation: spin 0.8s linear infinite;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@keyframes spin {
|
|
213
|
+
to {
|
|
214
|
+
transform: rotate(360deg);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
@media (prefers-reduced-motion: reduce) {
|
|
219
|
+
.dialog-content {
|
|
220
|
+
animation: none;
|
|
221
|
+
}
|
|
222
|
+
.spinner {
|
|
223
|
+
animation: none;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Props for ConfirmDialog component */
|
|
2
|
+
export interface Props {
|
|
3
|
+
/** Whether the dialog is open */
|
|
4
|
+
open: boolean;
|
|
5
|
+
/** Dialog title */
|
|
6
|
+
title: string;
|
|
7
|
+
/** Dialog message */
|
|
8
|
+
message: string;
|
|
9
|
+
/** Confirm button label */
|
|
10
|
+
confirmLabel?: string;
|
|
11
|
+
/** Cancel button label */
|
|
12
|
+
cancelLabel?: string;
|
|
13
|
+
/** Use destructive (red) styling for confirm */
|
|
14
|
+
destructive?: boolean;
|
|
15
|
+
/** Show loading state on confirm */
|
|
16
|
+
loading?: boolean;
|
|
17
|
+
/** Called when confirm is clicked */
|
|
18
|
+
onconfirm?: () => void;
|
|
19
|
+
/** Called when cancel is clicked or dialog closed */
|
|
20
|
+
oncancel?: () => void;
|
|
21
|
+
}
|
|
22
|
+
declare const ConfirmDialog: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type ConfirmDialog = ReturnType<typeof ConfirmDialog>;
|
|
24
|
+
export default ConfirmDialog;
|
|
25
|
+
//# sourceMappingURL=ConfirmDialog.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfirmDialog.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/feedback/ConfirmDialog.svelte.ts"],"names":[],"mappings":"AAaA,wCAAwC;AACxC,MAAM,WAAW,KAAK;IACpB,iCAAiC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAwDD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|