@devslab/dds-svelte 0.0.0-stage → 0.29.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/LICENSE ADDED
@@ -0,0 +1,74 @@
1
+ DevsLab Source-Available License 1.0
2
+
3
+ Copyright (c) 2026 DevsLab. All rights reserved.
4
+
5
+ This license governs the DDS software, design tokens, components, icons,
6
+ documentation, and other materials distributed with this license (the
7
+ "Software").
8
+
9
+ 1. Permitted use
10
+
11
+ Subject to this license, DevsLab grants you a limited, worldwide,
12
+ non-exclusive, non-transferable, revocable, royalty-free right to:
13
+
14
+ - download and install unmodified copies of the Software;
15
+ - use and execute the Software for internal or commercial applications;
16
+ - compile, minify, tree-shake, and bundle unmodified Software solely as a
17
+ technical part of building an application; and
18
+ - distribute the resulting object code only as an inseparable part of that
19
+ application and not as a standalone library, design system, component kit,
20
+ template, or asset collection.
21
+
22
+ Normal application configuration through documented public APIs, design-token
23
+ overrides, CSS custom properties, and component properties is permitted and is
24
+ not considered modification of the Software.
25
+
26
+ 2. Restrictions
27
+
28
+ Except where applicable law does not allow the restriction, you may not:
29
+
30
+ - modify, adapt, translate, reverse engineer, or create derivative works of the
31
+ Software itself;
32
+ - publish, republish, sublicense, sell, rent, host, or redistribute the
33
+ Software or any modified version as a standalone package or downloadable
34
+ collection;
35
+ - use the Software or a substantial portion of it to create or operate a
36
+ competing general-purpose design system, UI kit, component library, icon
37
+ library, token library, template marketplace, or hosted equivalent;
38
+ - remove or alter copyright, license, attribution, or provenance notices; or
39
+ - use DevsLab names, logos, product marks, or trade dress except to make a
40
+ truthful statement that an application uses the Software.
41
+
42
+ No patent, trademark, design, or branding rights are granted except the
43
+ copyright permissions stated in Section 1.
44
+
45
+ 3. Ownership and feedback
46
+
47
+ The Software is licensed, not sold. DevsLab and its licensors retain all
48
+ right, title, and interest in the Software. You grant DevsLab a perpetual,
49
+ worldwide, irrevocable, royalty-free right to use feedback you voluntarily
50
+ provide about the Software.
51
+
52
+ 4. Termination
53
+
54
+ This license terminates automatically if you breach it. Upon termination, you
55
+ must stop using and distributing the Software and delete all copies under your
56
+ control. Sections 2, 3, 5, and 6 survive termination.
57
+
58
+ 5. Disclaimer and limitation of liability
59
+
60
+ THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR
62
+ PURPOSE, TITLE, AND NON-INFRINGEMENT. TO THE MAXIMUM EXTENT PERMITTED BY LAW,
63
+ DEVSLAB WILL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
64
+ CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES, OR FOR LOSS OF DATA, PROFITS,
65
+ REVENUE, OR BUSINESS, ARISING FROM OR RELATED TO THE SOFTWARE.
66
+
67
+ 6. Governing law
68
+
69
+ This license is governed by the laws of the Republic of Korea, excluding
70
+ conflict-of-law rules. The Seoul Central District Court has exclusive
71
+ jurisdiction over disputes arising from this license.
72
+
73
+ This is a source-available license, not an open-source license approved by the
74
+ Open Source Initiative.
package/README.ko.md ADDED
@@ -0,0 +1,21 @@
1
+ # @devslab/dds-svelte
2
+
3
+ 공통 DDS 간격·타이포·CSS를 사용하는 Svelte 5 Layout·LeftNav·TopNav·ConsoleShell입니다.
4
+ 현재 이 화면 구성 요소만 제공합니다. 다른 프레임워크의 전체 컨트롤·그리드가 포함되었다는 의미가 아닙니다.
5
+
6
+ `@devslab/dds-svelte/styles.css`를 한 번 import하세요. Layout의 navigationPosition은
7
+ left/top, contentWidth는 workspace/form/settings/full입니다. header/navigation/railStart/
8
+ railEnd/footer와 본문은 Svelte snippet으로 조립합니다. 메뉴는 레이아웃 없이도 사용합니다.
9
+ 아이콘은 장식용 snippet이고 라벨이 접근 가능한 이름을 제공합니다.
10
+
11
+ LeftNav의 expandedGroups/onExpandedGroupsChange는 외부 제어, defaultExpandedGroups는
12
+ 초기값입니다. 빈 배열은 모두 접고 생략하면 활성 그룹만 엽니다. 앱은 권한·라우팅·이동 시
13
+ 그룹 열기·세션 보관을 담당합니다. 브라우저 저장소는 hydration 이후 읽으세요.
14
+ Layout은 모바일 탐색과 초점 복귀를 담당하고 TopNav는 네이티브 disclosure를 사용합니다.
15
+
16
+ [공통 화면 규격](../../docs/layouts.ko.md)을 참고하세요. 배포 전 test/check/build로 검증합니다.
17
+
18
+
19
+ ## 공통 업무 화면
20
+
21
+ Layout·LeftNav·TopNav는 공통 간격과 타이포 규격을 사용합니다. [조립 방법과 수치](../../docs/layouts.ko.md)를 참고하세요.
package/README.md CHANGED
@@ -1,3 +1,26 @@
1
- # Temporary Holding Version
1
+ # @devslab/dds-svelte
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Svelte 5 Layout, LeftNav, TopNav and ConsoleShell using the shared DDS geometry,
4
+ typography and CSS. This package currently provides these application shell
5
+ components; it does not claim the React/Solid control or grid inventory.
6
+
7
+ Import `@devslab/dds-svelte/styles.css` once. Use `Layout` with
8
+ `navigationPosition="left"` or `"top"`, `contentWidth="workspace"|"form"|"settings"|"full"`,
9
+ and header/navigation/railStart/railEnd/footer snippets plus children.
10
+ `LeftNav` and `TopNav` work independently of Layout. Icons are decorative snippets.
11
+
12
+ LeftNav supports nav groups with stable IDs and optional collapsible disclosure.
13
+ Use expandedGroups/onExpandedGroupsChange for parent control, or
14
+ defaultExpandedGroups for initial state. Empty defaults close all groups;
15
+ otherwise active groups initially open. The app owns routing, permissions,
16
+ route-change expansion and storage; read browser storage after hydration.
17
+ Layout owns the shared mobile navigation and focus restoration. TopNav uses
18
+ native disclosure navigation, not a partial application menubar.
19
+
20
+ See [shared layout guidance](../../docs/layouts.md) for exact dimensions and
21
+ composition examples. Run package test/check/build before publication.
22
+
23
+
24
+ ## Shared application layouts
25
+
26
+ Layout, LeftNav and TopNav use the common geometry and typography rules. See [composition and exact dimensions](../../docs/layouts.md).
@@ -0,0 +1,57 @@
1
+ <script lang="ts">
2
+ import Layout from "./Layout.svelte";
3
+ import LeftNav from "./LeftNav.svelte";
4
+ import type { ConsoleShellProps } from "./types";
5
+ let {
6
+ surface,
7
+ activePath,
8
+ brand,
9
+ context,
10
+ nav = [],
11
+ navigation: customNavigation,
12
+ labels,
13
+ header: pageHeader,
14
+ foot,
15
+ children,
16
+ expandedGroups,
17
+ defaultExpandedGroups,
18
+ onExpandedGroupsChange,
19
+ }: ConsoleShellProps = $props();
20
+ </script>
21
+
22
+ <Layout {surface} {labels}>
23
+ {#snippet railStart()}<a class="dds-console-rail__brand" href={brand.href}
24
+ ><img src={brand.mark} alt="" width="20" height="20" /><strong
25
+ >{brand.name}</strong
26
+ ></a
27
+ >{#if context?.length}<dl class="dds-console-rail__context">
28
+ {#each context as row, index (index)}<div>
29
+ <dt>{row.label}</dt>
30
+ <dd>{row.value}</dd>
31
+ </div>{/each}
32
+ </dl>{/if}{/snippet}
33
+ {#snippet navigation()}{#if customNavigation}{@render customNavigation()}{:else}<LeftNav
34
+ {surface}
35
+ {activePath}
36
+ {nav}
37
+ {labels}
38
+ {expandedGroups}
39
+ {defaultExpandedGroups}
40
+ {onExpandedGroupsChange}
41
+ />{/if}{/snippet}
42
+ {#snippet railEnd()}{#if foot}<div class="dds-console-rail__foot">
43
+ {@render foot()}
44
+ </div>{/if}{/snippet}
45
+ {#snippet header()}<div class="dds-console-page-header">
46
+ <div class="dds-console-page-header__text">
47
+ {#if pageHeader.eyebrow}<p class="dds-console-page-eyebrow">
48
+ {pageHeader.eyebrow}
49
+ </p>{/if}
50
+ <h1>{pageHeader.title}</h1>
51
+ </div>
52
+ {#if pageHeader.actions}<div class="dds-console-page-header__actions">
53
+ {@render pageHeader.actions()}
54
+ </div>{/if}
55
+ </div>{/snippet}
56
+ {@render children?.()}
57
+ </Layout>
@@ -0,0 +1,4 @@
1
+ import type { ConsoleShellProps } from "./types";
2
+ declare const ConsoleShell: import("svelte").Component<ConsoleShellProps, {}, "">;
3
+ type ConsoleShell = ReturnType<typeof ConsoleShell>;
4
+ export default ConsoleShell;
@@ -0,0 +1,152 @@
1
+ <script lang="ts">
2
+ import { onMount, tick } from "svelte";
3
+ import type { LayoutProps } from "./types";
4
+ let {
5
+ navigationPosition = "left",
6
+ contentWidth = "workspace",
7
+ labels,
8
+ surface,
9
+ header,
10
+ navigation,
11
+ railStart,
12
+ railEnd,
13
+ footer,
14
+ children,
15
+ }: LayoutProps = $props();
16
+ const id = $props.id(),
17
+ railId = `dds-console-rail-${id}`,
18
+ mainId = `dds-console-main-${id}`;
19
+ let open = $state(false),
20
+ hydrated = $state(false),
21
+ opener: HTMLButtonElement,
22
+ rail: HTMLElement;
23
+ function close() {
24
+ open = false;
25
+ opener?.focus();
26
+ }
27
+ function focusables() {
28
+ return [
29
+ opener,
30
+ ...Array.from(
31
+ rail?.querySelectorAll<HTMLElement>(
32
+ 'a[href],button,summary,input,select,textarea,[tabindex="0"]',
33
+ ) ?? [],
34
+ ),
35
+ ].filter(
36
+ (el) =>
37
+ el &&
38
+ !el.closest("[hidden]") &&
39
+ el.getClientRects().length > 0 &&
40
+ getComputedStyle(el).visibility !== "hidden" &&
41
+ !Array.from(rail?.querySelectorAll("details:not([open])") ?? []).some(
42
+ (details) =>
43
+ details.contains(el) &&
44
+ !details.querySelector("summary")?.contains(el),
45
+ ) &&
46
+ !el.hasAttribute("disabled"),
47
+ );
48
+ }
49
+ async function toggle() {
50
+ if (open) close();
51
+ else {
52
+ open = true;
53
+ await tick();
54
+ focusables()[1]?.focus();
55
+ }
56
+ }
57
+ function key(event: KeyboardEvent) {
58
+ if (!open) return;
59
+ if (event.key === "Escape") {
60
+ event.preventDefault();
61
+ close();
62
+ }
63
+ if (event.key === "Tab") {
64
+ const items = focusables(),
65
+ first = items[0],
66
+ last = items.at(-1);
67
+ if (event.shiftKey && document.activeElement === first) {
68
+ event.preventDefault();
69
+ last?.focus();
70
+ } else if (!event.shiftKey && document.activeElement === last) {
71
+ event.preventDefault();
72
+ first?.focus();
73
+ }
74
+ }
75
+ }
76
+ function navigate(event: MouseEvent) {
77
+ if (
78
+ open &&
79
+ event.target instanceof Element &&
80
+ event.target.closest("a[href]")
81
+ )
82
+ close();
83
+ }
84
+ onMount(() => {
85
+ hydrated = true;
86
+ const media =
87
+ typeof window.matchMedia === "function"
88
+ ? window.matchMedia("(max-width: 56.25rem)")
89
+ : undefined;
90
+ const resize = () => {
91
+ if (!media?.matches) open = false;
92
+ };
93
+ media?.addEventListener("change", resize);
94
+ document.addEventListener("keydown", key);
95
+ return () => {
96
+ document.removeEventListener("keydown", key);
97
+ media?.removeEventListener("change", resize);
98
+ };
99
+ });
100
+ </script>
101
+
102
+ <div
103
+ class="dds-console-shell dds-layout"
104
+ data-navigation-position={navigationPosition}
105
+ data-surface={surface}
106
+ data-hydrated={hydrated ? "true" : undefined}
107
+ >
108
+ <a class="dds-console-skip dds-visually-hidden" href={`#${mainId}`}
109
+ >{labels.skip}</a
110
+ >
111
+ <button
112
+ bind:this={opener}
113
+ class="dds-console-rail__toggle"
114
+ type="button"
115
+ data-action="toggle-rail"
116
+ aria-controls={railId}
117
+ aria-expanded={open}
118
+ onclick={toggle}
119
+ ><span aria-hidden="true"></span><span class="dds-visually-hidden"
120
+ >{open ? labels.menuClose : labels.menuOpen}</span
121
+ ></button
122
+ >
123
+ <button
124
+ type="button"
125
+ tabindex="-1"
126
+ aria-label={labels.menuClose}
127
+ class="dds-console-rail__scrim"
128
+ data-open={String(open)}
129
+ onclick={close}
130
+ ></button>
131
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions, a11y_click_events_have_key_events -->
132
+ <aside
133
+ bind:this={rail}
134
+ id={railId}
135
+ class="dds-console-rail dds-layout__navigation"
136
+ data-open={String(open)}
137
+ onclick={navigate}
138
+ >
139
+ {@render railStart?.()}{@render navigation?.()}{@render railEnd?.()}
140
+ </aside>
141
+ <main id={mainId} class="dds-console-main dds-layout__main" tabindex="-1">
142
+ <div class="dds-layout__content" data-width={contentWidth}>
143
+ {#if header}<header class="dds-layout__header">
144
+ {@render header()}
145
+ </header>{/if}{@render children?.()}{#if footer}<footer
146
+ class="dds-layout__footer"
147
+ >
148
+ {@render footer()}
149
+ </footer>{/if}
150
+ </div>
151
+ </main>
152
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { LayoutProps } from "./types";
2
+ declare const Layout: import("svelte").Component<LayoutProps, {}, "">;
3
+ type Layout = ReturnType<typeof Layout>;
4
+ export default Layout;
@@ -0,0 +1,93 @@
1
+ <script lang="ts">
2
+ import { untrack } from "svelte";
3
+ import type { LeftNavProps, ConsoleNavItem } from "./types";
4
+ let {
5
+ surface,
6
+ activePath,
7
+ nav,
8
+ labels,
9
+ expandedGroups,
10
+ defaultExpandedGroups,
11
+ onExpandedGroupsChange,
12
+ onNavigate,
13
+ }: LeftNavProps = $props();
14
+ const id = $props.id();
15
+ function active(item: ConsoleNavItem) {
16
+ return (
17
+ item.active ??
18
+ (activePath === item.href ||
19
+ (item.href.split("/").filter(Boolean).length > 1 &&
20
+ activePath.startsWith(`${item.href}/`)))
21
+ );
22
+ }
23
+ let internal = $state<readonly string[]>(
24
+ untrack(
25
+ () =>
26
+ defaultExpandedGroups ??
27
+ nav.flatMap((group, index) =>
28
+ group.collapsible && group.items.some(active)
29
+ ? [group.id ?? `group-${index}`]
30
+ : [],
31
+ ),
32
+ ),
33
+ );
34
+ let expanded = $derived(expandedGroups ?? internal);
35
+ function toggle(groupId: string) {
36
+ const next = expanded.includes(groupId)
37
+ ? expanded.filter((value) => value !== groupId)
38
+ : [...expanded, groupId];
39
+ if (expandedGroups === undefined) internal = next;
40
+ onExpandedGroupsChange?.(next);
41
+ }
42
+ </script>
43
+
44
+ <nav class="dds-console-rail__nav dds-left-nav" aria-label={labels.nav}>
45
+ {#each nav as group, index (group.id ?? index)}
46
+ <section
47
+ class="dds-console-rail__group"
48
+ aria-labelledby={`${id}-group-${index}`}
49
+ data-collapsible={group.collapsible ? "true" : undefined}
50
+ >
51
+ <h2 id={`${id}-group-${index}`} class="dds-console-rail__group-label">
52
+ {#if group.collapsible}<button
53
+ type="button"
54
+ class="dds-console-rail__group-toggle"
55
+ aria-expanded={expanded.includes(group.id ?? `group-${index}`)}
56
+ aria-controls={`${id}-items-${index}`}
57
+ onclick={() => toggle(group.id ?? `group-${index}`)}
58
+ ><span>{group.label}</span><span
59
+ class="dds-console-rail__chevron"
60
+ aria-hidden="true"
61
+ ></span></button
62
+ >{:else}{group.label}{/if}
63
+ </h2>
64
+ <ul
65
+ id={`${id}-items-${index}`}
66
+ hidden={group.collapsible &&
67
+ !expanded.includes(group.id ?? `group-${index}`)}
68
+ >
69
+ {#each group.items as item (item.id)}<li>
70
+ <a
71
+ class="dds-console-rail__item"
72
+ data-nav={`${surface}.${item.id}`}
73
+ href={item.href}
74
+ aria-current={active(item) ? "page" : undefined}
75
+ onclick={() => onNavigate?.()}
76
+ >
77
+ {#if item.icon}<span class="dds-console-rail__item-content"
78
+ ><span class="dds-console-rail__item-icon" aria-hidden="true"
79
+ >{@render item.icon()}</span
80
+ ><span>{item.label}</span></span
81
+ >{:else}<span>{item.label}</span>{/if}
82
+ {#if item.badge != null && item.badge > 0}<span
83
+ class="dds-console-rail__badge"
84
+ aria-label={labels.badge.replace("{count}", () =>
85
+ String(item.badge),
86
+ )}>{item.badge}</span
87
+ >{/if}
88
+ </a>
89
+ </li>{/each}
90
+ </ul>
91
+ </section>
92
+ {/each}
93
+ </nav>
@@ -0,0 +1,4 @@
1
+ import type { LeftNavProps } from "./types";
2
+ declare const LeftNav: import("svelte").Component<LeftNavProps, {}, "">;
3
+ type LeftNav = ReturnType<typeof LeftNav>;
4
+ export default LeftNav;
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import type { TopNavProps, ConsoleNavItem } from "./types";
4
+ let { surface, activePath, nav, labels, onNavigate }: TopNavProps = $props();
5
+ let root: HTMLElement;
6
+ function active(item: ConsoleNavItem) {
7
+ return (
8
+ item.active ??
9
+ (activePath === item.href ||
10
+ (item.href.split("/").filter(Boolean).length > 1 &&
11
+ activePath.startsWith(`${item.href}/`)))
12
+ );
13
+ }
14
+ function outside(event: PointerEvent) {
15
+ if (!root?.contains(event.target as Node))
16
+ root
17
+ ?.querySelectorAll("details[open]")
18
+ .forEach((el) => el.removeAttribute("open"));
19
+ }
20
+ function key(event: KeyboardEvent) {
21
+ if (event.key !== "Escape") return;
22
+ const details = (event.target as Element).closest("details");
23
+ if (details?.open) {
24
+ event.preventDefault();
25
+ event.stopPropagation();
26
+ details.open = false;
27
+ details.querySelector("summary")?.focus();
28
+ }
29
+ }
30
+ function navigate(event: MouseEvent) {
31
+ (event.currentTarget as Element)
32
+ .closest("details")
33
+ ?.removeAttribute("open");
34
+ onNavigate?.();
35
+ }
36
+ onMount(() => {
37
+ document.addEventListener("pointerdown", outside);
38
+ return () => document.removeEventListener("pointerdown", outside);
39
+ });
40
+ </script>
41
+
42
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
43
+ <nav
44
+ bind:this={root}
45
+ class="dds-top-nav"
46
+ aria-label={labels.nav}
47
+ onkeydown={key}
48
+ >
49
+ {#each nav as group, index (group.id ?? index)}<details
50
+ class="dds-top-nav__group"
51
+ >
52
+ <summary class="dds-top-nav__trigger">{group.label}</summary>
53
+ <ul class="dds-top-nav__items">
54
+ {#each group.items as item (item.id)}<li>
55
+ <a
56
+ class="dds-top-nav__item"
57
+ data-nav={`${surface}.${item.id}`}
58
+ href={item.href}
59
+ aria-current={active(item) ? "page" : undefined}
60
+ onclick={navigate}
61
+ >{#if item.icon}<span
62
+ class="dds-top-nav__item-icon"
63
+ aria-hidden="true">{@render item.icon()}</span
64
+ >{/if}<span>{item.label}</span
65
+ >{#if item.badge != null && item.badge > 0}<span
66
+ class="dds-console-rail__badge"
67
+ aria-label={labels.badge.replace("{count}", () =>
68
+ String(item.badge),
69
+ )}>{item.badge}</span
70
+ >{/if}</a
71
+ >
72
+ </li>{/each}
73
+ </ul>
74
+ </details>{/each}
75
+ </nav>
@@ -0,0 +1,4 @@
1
+ import type { TopNavProps } from "./types";
2
+ declare const TopNav: import("svelte").Component<TopNavProps, {}, "">;
3
+ type TopNav = ReturnType<typeof TopNav>;
4
+ export default TopNav;
@@ -0,0 +1,5 @@
1
+ export { default as ConsoleShell } from "./ConsoleShell.svelte";
2
+ export type * from "./types";
3
+ export { default as Layout } from "./Layout.svelte";
4
+ export { default as LeftNav } from "./LeftNav.svelte";
5
+ export { default as TopNav } from "./TopNav.svelte";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { default as ConsoleShell } from "./ConsoleShell.svelte";
2
+ export { default as Layout } from "./Layout.svelte";
3
+ export { default as LeftNav } from "./LeftNav.svelte";
4
+ export { default as TopNav } from "./TopNav.svelte";
@@ -0,0 +1,72 @@
1
+ import type { Snippet } from "svelte";
2
+ export interface ConsoleNavItem {
3
+ id: string;
4
+ href: string;
5
+ label: string;
6
+ badge?: number;
7
+ active?: boolean;
8
+ icon?: Snippet;
9
+ }
10
+ export interface ConsoleNavGroup {
11
+ id?: string;
12
+ label: string;
13
+ items: readonly ConsoleNavItem[];
14
+ collapsible?: boolean;
15
+ }
16
+ export interface ConsoleShellLabels {
17
+ skip: string;
18
+ menuOpen: string;
19
+ menuClose: string;
20
+ nav: string;
21
+ badge: string;
22
+ }
23
+ export interface ConsoleHeader {
24
+ eyebrow?: string;
25
+ title: string;
26
+ actions?: Snippet;
27
+ }
28
+ export interface ConsoleShellProps {
29
+ surface: string;
30
+ activePath: string;
31
+ brand: {
32
+ href: string;
33
+ name: string;
34
+ mark: string;
35
+ };
36
+ context?: readonly {
37
+ label: string;
38
+ value: string;
39
+ }[];
40
+ nav?: readonly ConsoleNavGroup[];
41
+ navigation?: Snippet;
42
+ labels: ConsoleShellLabels;
43
+ header: ConsoleHeader;
44
+ foot?: Snippet;
45
+ children?: Snippet;
46
+ expandedGroups?: readonly string[];
47
+ defaultExpandedGroups?: readonly string[];
48
+ onExpandedGroupsChange?: (groupIds: string[]) => void;
49
+ }
50
+ export interface LeftNavProps {
51
+ surface: string;
52
+ activePath: string;
53
+ nav: readonly ConsoleNavGroup[];
54
+ labels: Pick<ConsoleShellLabels, "nav" | "badge">;
55
+ expandedGroups?: readonly string[];
56
+ defaultExpandedGroups?: readonly string[];
57
+ onExpandedGroupsChange?: (ids: string[]) => void;
58
+ onNavigate?: () => void;
59
+ }
60
+ export type TopNavProps = Pick<LeftNavProps, "surface" | "activePath" | "nav" | "labels" | "onNavigate">;
61
+ export interface LayoutProps {
62
+ navigationPosition?: "left" | "top";
63
+ contentWidth?: "workspace" | "form" | "settings" | "full";
64
+ labels: Pick<ConsoleShellLabels, "skip" | "menuOpen" | "menuClose">;
65
+ surface?: string;
66
+ header?: Snippet;
67
+ navigation?: Snippet;
68
+ railStart?: Snippet;
69
+ railEnd?: Snippet;
70
+ footer?: Snippet;
71
+ children?: Snippet;
72
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
1
  {
2
2
  "name": "@devslab/dds-svelte",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.29.0",
4
+ "description": "Svelte 5 ConsoleShell for the DevsLab Design System",
5
+ "type": "module",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "sideEffects": [
8
+ "./styles.css"
9
+ ],
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/devslab-kr/dds.git",
13
+ "directory": "packages/dds-svelte"
14
+ },
15
+ "publishConfig": {
16
+ "registry": "https://registry.npmjs.org",
17
+ "access": "public",
18
+ "provenance": true
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "styles.css",
23
+ "README.md",
24
+ "README.ko.md"
25
+ ],
26
+ "svelte": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "svelte": "./dist/index.js",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "./styles.css": "./styles.css"
35
+ },
36
+ "dependencies": {
37
+ "@devslab/dds-css": "0.29.0",
38
+ "@devslab/dds-tokens": "0.29.0"
39
+ },
40
+ "peerDependencies": {
41
+ "svelte": "^5.46.4"
42
+ },
43
+ "devDependencies": {
44
+ "svelte": "5.57.2",
45
+ "@sveltejs/package": "3.0.1",
46
+ "@sveltejs/vite-plugin-svelte": "7.3.1",
47
+ "svelte-check": "4.7.7",
48
+ "typescript": "6.0.3",
49
+ "vite": "8.2.1",
50
+ "vitest": "4.1.1",
51
+ "jsdom": "30.0.1",
52
+ "@testing-library/dom": "10.4.1",
53
+ "@testing-library/user-event": "14.6.1",
54
+ "axe-core": "4.13.0"
55
+ },
56
+ "scripts": {
57
+ "build": "svelte-package",
58
+ "check": "svelte-check --tsconfig ./tsconfig.json",
59
+ "test": "vitest run && vitest run --config vitest.ssr.config.ts"
60
+ }
6
61
  }
package/styles.css ADDED
@@ -0,0 +1,2 @@
1
+ @import "@devslab/dds-tokens/tokens.css";
2
+ @import "@devslab/dds-css/dds.css";