@jogak/ui 0.1.0-alpha.0 → 0.1.0-alpha.10.2
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/CHANGELOG.md +118 -0
- package/README.md +201 -15
- package/dist/app/App.d.ts +24 -1
- package/dist/components/Preview/IframeMount.d.ts +35 -0
- package/dist/components/Preview/ShadowMount.d.ts +25 -0
- package/dist/components/Preview/index.d.ts +19 -2
- package/dist/host/index.cjs +1 -0
- package/dist/host/index.d.ts +38 -1
- package/dist/host/index.mjs +40 -34
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +726 -901
- package/package.json +15 -6
- package/preview-frame.html +17 -0
- package/src/app/App.tsx +189 -0
- package/src/app/main.tsx +31 -0
- package/src/app/preview-frame.tsx +61 -0
- package/src/components/Actions/index.tsx +92 -0
- package/src/components/Controls/index.tsx +190 -0
- package/src/components/Preview/IframeMount.tsx +101 -0
- package/src/components/Preview/ShadowMount.tsx +57 -0
- package/src/components/Preview/index.tsx +766 -0
- package/src/components/Sidebar/index.tsx +285 -0
- package/src/hooks/useRegistry.ts +22 -0
- package/src/index.ts +12 -0
- package/src/styles/jogak.css +128 -0
- package/src/vite-env.d.ts +30 -0
- package/dist/host/index.js +0 -1
- package/dist/index.js +0 -1
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
import type { CSSProperties, ReactElement } from 'react'
|
|
3
|
+
import clsx from 'clsx'
|
|
4
|
+
import type { CategoryMetaTree, RegistryEntryMeta } from '@jogak/core'
|
|
5
|
+
import { useRegistryMeta } from '@jogak/core/renderers/react'
|
|
6
|
+
|
|
7
|
+
// CSS custom property를 React style prop에 주입하기 위한 헬퍼 타입.
|
|
8
|
+
// React 18+는 string-keyed `--` prefix를 인식하나 TS는 명시적 cast 필요.
|
|
9
|
+
// spec §6.1 참조.
|
|
10
|
+
type CSSVarStyle = CSSProperties & Record<`--${string}`, string | number>
|
|
11
|
+
|
|
12
|
+
export interface SidebarProps {
|
|
13
|
+
readonly selectedEntryId: string | null
|
|
14
|
+
readonly selectedJogakName: string | null
|
|
15
|
+
readonly onSelect: (entryId: string, jogakName: string) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Sidebar — `useRegistryMeta`로 전환되어 lazy 모드에서도 모든 entry의 메타가 즉시 보인다.
|
|
20
|
+
* 계약 §10: hydrated 여부를 표시하지 않는다 — 사용자에겐 모든 entry가 동일하게 보임.
|
|
21
|
+
*/
|
|
22
|
+
export function Sidebar({
|
|
23
|
+
selectedEntryId,
|
|
24
|
+
selectedJogakName,
|
|
25
|
+
onSelect,
|
|
26
|
+
}: SidebarProps): ReactElement {
|
|
27
|
+
const [query, setQuery] = useState('')
|
|
28
|
+
const { metaTree, searchMeta } = useRegistryMeta()
|
|
29
|
+
|
|
30
|
+
const filtered = query.trim().length > 0 ? searchMeta(query) : null
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<aside
|
|
34
|
+
data-testid="sidebar"
|
|
35
|
+
className="jogak:flex jogak:flex-col jogak:h-full jogak:overflow-auto jogak:border-r jogak:border-[var(--jogak-color-border)]"
|
|
36
|
+
>
|
|
37
|
+
<div className="jogak:p-3 jogak:border-b jogak:border-[var(--jogak-color-border)]">
|
|
38
|
+
<input
|
|
39
|
+
type="search"
|
|
40
|
+
placeholder="Search components..."
|
|
41
|
+
value={query}
|
|
42
|
+
onChange={(e) => {
|
|
43
|
+
setQuery(e.target.value)
|
|
44
|
+
}}
|
|
45
|
+
className="jogak:w-full jogak:px-2 jogak:py-1.5 jogak:border jogak:border-[var(--jogak-color-border-strong)] jogak:rounded-[var(--jogak-radius-md)]"
|
|
46
|
+
aria-label="Search components"
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
<nav className="jogak:flex-1 jogak:overflow-auto jogak:py-2">
|
|
50
|
+
{filtered !== null ? (
|
|
51
|
+
<FlatList
|
|
52
|
+
metas={filtered}
|
|
53
|
+
selectedEntryId={selectedEntryId}
|
|
54
|
+
selectedJogakName={selectedJogakName}
|
|
55
|
+
onSelect={onSelect}
|
|
56
|
+
/>
|
|
57
|
+
) : (
|
|
58
|
+
<TreeView
|
|
59
|
+
node={metaTree}
|
|
60
|
+
selectedEntryId={selectedEntryId}
|
|
61
|
+
selectedJogakName={selectedJogakName}
|
|
62
|
+
onSelect={onSelect}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
</nav>
|
|
66
|
+
</aside>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface FlatListProps {
|
|
71
|
+
readonly metas: readonly RegistryEntryMeta[]
|
|
72
|
+
readonly selectedEntryId: string | null
|
|
73
|
+
readonly selectedJogakName: string | null
|
|
74
|
+
readonly onSelect: (entryId: string, jogakName: string) => void
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function FlatList({
|
|
78
|
+
metas,
|
|
79
|
+
selectedEntryId,
|
|
80
|
+
selectedJogakName,
|
|
81
|
+
onSelect,
|
|
82
|
+
}: FlatListProps): ReactElement {
|
|
83
|
+
if (metas.length === 0) {
|
|
84
|
+
return (
|
|
85
|
+
<p className="jogak:px-3 jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px]">
|
|
86
|
+
No results
|
|
87
|
+
</p>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
return (
|
|
91
|
+
<ul className="jogak:list-none jogak:m-0 jogak:p-0">
|
|
92
|
+
{metas.map((meta) => (
|
|
93
|
+
<li key={meta.id}>
|
|
94
|
+
<EntryGroup
|
|
95
|
+
meta={meta}
|
|
96
|
+
selectedEntryId={selectedEntryId}
|
|
97
|
+
selectedJogakName={selectedJogakName}
|
|
98
|
+
onSelect={onSelect}
|
|
99
|
+
indent={0}
|
|
100
|
+
/>
|
|
101
|
+
</li>
|
|
102
|
+
))}
|
|
103
|
+
</ul>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface TreeViewProps {
|
|
108
|
+
readonly node: CategoryMetaTree
|
|
109
|
+
readonly selectedEntryId: string | null
|
|
110
|
+
readonly selectedJogakName: string | null
|
|
111
|
+
readonly onSelect: (entryId: string, jogakName: string) => void
|
|
112
|
+
readonly depth?: number
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function TreeView({
|
|
116
|
+
node,
|
|
117
|
+
selectedEntryId,
|
|
118
|
+
selectedJogakName,
|
|
119
|
+
onSelect,
|
|
120
|
+
depth = 0,
|
|
121
|
+
}: TreeViewProps): ReactElement {
|
|
122
|
+
return (
|
|
123
|
+
<ul
|
|
124
|
+
className="jogak:list-none jogak:m-0 jogak:pr-0 jogak:py-0 jogak:pl-[var(--jogak-tree-pl)]"
|
|
125
|
+
// eslint-disable-next-line no-restricted-syntax -- jogak: CSS var inject (--jogak-tree-pl)
|
|
126
|
+
style={{ '--jogak-tree-pl': `${depth * 12}px` } as CSSVarStyle}
|
|
127
|
+
>
|
|
128
|
+
{Object.entries(node).map(([key, child]) => (
|
|
129
|
+
<li key={key}>
|
|
130
|
+
{'id' in child ? (
|
|
131
|
+
<EntryGroup
|
|
132
|
+
meta={child as RegistryEntryMeta}
|
|
133
|
+
selectedEntryId={selectedEntryId}
|
|
134
|
+
selectedJogakName={selectedJogakName}
|
|
135
|
+
onSelect={onSelect}
|
|
136
|
+
indent={0}
|
|
137
|
+
/>
|
|
138
|
+
) : (
|
|
139
|
+
<CategoryGroup
|
|
140
|
+
label={key}
|
|
141
|
+
node={child as CategoryMetaTree}
|
|
142
|
+
selectedEntryId={selectedEntryId}
|
|
143
|
+
selectedJogakName={selectedJogakName}
|
|
144
|
+
onSelect={onSelect}
|
|
145
|
+
depth={depth + 1}
|
|
146
|
+
/>
|
|
147
|
+
)}
|
|
148
|
+
</li>
|
|
149
|
+
))}
|
|
150
|
+
</ul>
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
interface CategoryGroupProps {
|
|
155
|
+
readonly label: string
|
|
156
|
+
readonly node: CategoryMetaTree
|
|
157
|
+
readonly selectedEntryId: string | null
|
|
158
|
+
readonly selectedJogakName: string | null
|
|
159
|
+
readonly onSelect: (entryId: string, jogakName: string) => void
|
|
160
|
+
readonly depth: number
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function CategoryGroup({
|
|
164
|
+
label,
|
|
165
|
+
node,
|
|
166
|
+
selectedEntryId,
|
|
167
|
+
selectedJogakName,
|
|
168
|
+
onSelect,
|
|
169
|
+
depth,
|
|
170
|
+
}: CategoryGroupProps): ReactElement {
|
|
171
|
+
const [open, setOpen] = useState(true)
|
|
172
|
+
return (
|
|
173
|
+
<div>
|
|
174
|
+
<button
|
|
175
|
+
type="button"
|
|
176
|
+
onClick={() => {
|
|
177
|
+
setOpen((v) => !v)
|
|
178
|
+
}}
|
|
179
|
+
className="jogak:flex jogak:items-center jogak:gap-1 jogak:w-full jogak:px-3 jogak:py-1 jogak:bg-transparent jogak:border-none jogak:cursor-pointer jogak:text-[12px] jogak:font-semibold jogak:text-[var(--jogak-color-fg-muted)] jogak:uppercase jogak:tracking-wider"
|
|
180
|
+
aria-expanded={open}
|
|
181
|
+
>
|
|
182
|
+
<span>{open ? '▾' : '▸'}</span>
|
|
183
|
+
{label}
|
|
184
|
+
</button>
|
|
185
|
+
{open && (
|
|
186
|
+
<TreeView
|
|
187
|
+
node={node}
|
|
188
|
+
selectedEntryId={selectedEntryId}
|
|
189
|
+
selectedJogakName={selectedJogakName}
|
|
190
|
+
onSelect={onSelect}
|
|
191
|
+
depth={depth}
|
|
192
|
+
/>
|
|
193
|
+
)}
|
|
194
|
+
</div>
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface EntryGroupProps {
|
|
199
|
+
readonly meta: RegistryEntryMeta
|
|
200
|
+
readonly selectedEntryId: string | null
|
|
201
|
+
readonly selectedJogakName: string | null
|
|
202
|
+
readonly onSelect: (entryId: string, jogakName: string) => void
|
|
203
|
+
readonly indent: number
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function EntryGroup({
|
|
207
|
+
meta,
|
|
208
|
+
selectedEntryId,
|
|
209
|
+
selectedJogakName,
|
|
210
|
+
onSelect,
|
|
211
|
+
indent,
|
|
212
|
+
}: EntryGroupProps): ReactElement {
|
|
213
|
+
const isCurrentEntry = meta.id === selectedEntryId
|
|
214
|
+
const [open, setOpen] = useState(isCurrentEntry)
|
|
215
|
+
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
if (isCurrentEntry) setOpen(true)
|
|
218
|
+
}, [isCurrentEntry])
|
|
219
|
+
|
|
220
|
+
const label = meta.title.split('/').pop() ?? meta.title
|
|
221
|
+
const paddingLeft = 16 + indent * 12
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<div>
|
|
225
|
+
<button
|
|
226
|
+
type="button"
|
|
227
|
+
onClick={() => {
|
|
228
|
+
if (!isCurrentEntry) {
|
|
229
|
+
setOpen(true)
|
|
230
|
+
const first = meta.jogakNames[0]
|
|
231
|
+
if (first !== undefined) onSelect(meta.id, first)
|
|
232
|
+
} else {
|
|
233
|
+
setOpen((v) => !v)
|
|
234
|
+
}
|
|
235
|
+
}}
|
|
236
|
+
className={clsx(
|
|
237
|
+
'jogak:flex jogak:items-center jogak:gap-1.5 jogak:w-full jogak:pr-3 jogak:py-[5px]',
|
|
238
|
+
'jogak:pl-[var(--jogak-entry-pl)]',
|
|
239
|
+
'jogak:border-none jogak:cursor-pointer jogak:text-left jogak:text-[13px]',
|
|
240
|
+
isCurrentEntry
|
|
241
|
+
? 'jogak:bg-[var(--jogak-color-accent-bg)] jogak:text-[var(--jogak-color-accent)] jogak:font-medium'
|
|
242
|
+
: 'jogak:bg-transparent jogak:text-[var(--jogak-color-fg)] jogak:font-normal',
|
|
243
|
+
)}
|
|
244
|
+
// eslint-disable-next-line no-restricted-syntax -- jogak: CSS var inject (--jogak-entry-pl)
|
|
245
|
+
style={{ '--jogak-entry-pl': `${paddingLeft}px` } as CSSVarStyle}
|
|
246
|
+
aria-expanded={open}
|
|
247
|
+
>
|
|
248
|
+
<span className="jogak:text-[10px] jogak:shrink-0 jogak:leading-none">
|
|
249
|
+
{open ? '▾' : '▸'}
|
|
250
|
+
</span>
|
|
251
|
+
{label}
|
|
252
|
+
</button>
|
|
253
|
+
{open && (
|
|
254
|
+
<ul className="jogak:list-none jogak:m-0 jogak:p-0">
|
|
255
|
+
{meta.jogakNames.map((jogakName) => {
|
|
256
|
+
const isSelected = isCurrentEntry && jogakName === selectedJogakName
|
|
257
|
+
return (
|
|
258
|
+
<li key={jogakName}>
|
|
259
|
+
<button
|
|
260
|
+
type="button"
|
|
261
|
+
onClick={() => {
|
|
262
|
+
onSelect(meta.id, jogakName)
|
|
263
|
+
}}
|
|
264
|
+
className={clsx(
|
|
265
|
+
'jogak:block jogak:w-full jogak:text-left jogak:pr-3 jogak:py-1',
|
|
266
|
+
'jogak:pl-[var(--jogak-jogak-pl)]',
|
|
267
|
+
'jogak:border-none jogak:cursor-pointer jogak:text-[12px]',
|
|
268
|
+
isSelected
|
|
269
|
+
? 'jogak:bg-[var(--jogak-color-accent-bg-soft)] jogak:text-[var(--jogak-color-accent-fg)] jogak:font-medium'
|
|
270
|
+
: 'jogak:bg-transparent jogak:text-[var(--jogak-color-fg-muted)] jogak:font-normal',
|
|
271
|
+
)}
|
|
272
|
+
// eslint-disable-next-line no-restricted-syntax -- jogak: CSS var inject (--jogak-jogak-pl)
|
|
273
|
+
style={{ '--jogak-jogak-pl': `${paddingLeft + 18}px` } as CSSVarStyle}
|
|
274
|
+
aria-current={isSelected ? 'true' : undefined}
|
|
275
|
+
>
|
|
276
|
+
{jogakName}
|
|
277
|
+
</button>
|
|
278
|
+
</li>
|
|
279
|
+
)
|
|
280
|
+
})}
|
|
281
|
+
</ul>
|
|
282
|
+
)}
|
|
283
|
+
</div>
|
|
284
|
+
)
|
|
285
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useRegistry as useRegistryFromAdapter } from '@jogak/core/renderers/react'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
import type { CategoryTree, RegistryEntry } from '@jogak/core'
|
|
4
|
+
|
|
5
|
+
export interface UseRegistryReturn {
|
|
6
|
+
readonly entries: readonly RegistryEntry[]
|
|
7
|
+
readonly tree: CategoryTree
|
|
8
|
+
readonly search: (query: string) => readonly RegistryEntry[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function useRegistry(): UseRegistryReturn {
|
|
12
|
+
const registry = useRegistryFromAdapter()
|
|
13
|
+
|
|
14
|
+
const entries = useMemo(() => registry.getAll(), [registry])
|
|
15
|
+
const tree = useMemo(() => registry.getTree(), [registry])
|
|
16
|
+
const search = useMemo(
|
|
17
|
+
() => (query: string) => registry.search(query),
|
|
18
|
+
[registry],
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
return { entries, tree, search }
|
|
22
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { JogakApp } from './app/App.js'
|
|
2
|
+
export { Sidebar } from './components/Sidebar/index.js'
|
|
3
|
+
export { Preview } from './components/Preview/index.js'
|
|
4
|
+
export { Controls } from './components/Controls/index.js'
|
|
5
|
+
export { Actions } from './components/Actions/index.js'
|
|
6
|
+
export { useRegistry } from './hooks/useRegistry.js'
|
|
7
|
+
|
|
8
|
+
export type { JogakAppProps } from './app/App.js'
|
|
9
|
+
export type { SidebarProps } from './components/Sidebar/index.js'
|
|
10
|
+
export type { PreviewProps } from './components/Preview/index.js'
|
|
11
|
+
export type { ControlsProps } from './components/Controls/index.js'
|
|
12
|
+
export type { UseRegistryReturn } from './hooks/useRegistry.js'
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* jogak SPA 전용 스타일.
|
|
3
|
+
*
|
|
4
|
+
* 본 파일은 `packages/ui/src/app/main.tsx`에서만 import된다.
|
|
5
|
+
* lib mode build entry(`src/index.ts`, `src/host/index.ts`)에는 import되지 않으므로
|
|
6
|
+
* publish되는 `dist/index.{mjs,js}` / `dist/host/index.{mjs,js}` 산출물에 css 흔적이
|
|
7
|
+
* 들어가지 않는다 (사용자 번들 영향 zero — api-contracts §2.4, §7).
|
|
8
|
+
*
|
|
9
|
+
* Tailwind v4: preflight를 의도적으로 import하지 않고 `theme.css` + `utilities.css`만
|
|
10
|
+
* 분리 import한다. preflight의 globals(`*`, `html`, `body`)는 사용자 css와 prefix
|
|
11
|
+
* 무관하게 충돌 가능 — jogak SPA는 `index.html`의 inline reset(`box-sizing`,
|
|
12
|
+
* `body margin:0`)으로 충분하다 (api-contracts §6.2).
|
|
13
|
+
*
|
|
14
|
+
* `prefix(jogak)` — 모든 utility/variant/theme variable에 `jogak:` prefix가 적용된다.
|
|
15
|
+
* 사용자가 v4(no prefix)나 v3(`tw-` prefix)를 써도 selector 충돌 zero.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
@import "tailwindcss/theme.css" layer(theme) prefix(jogak);
|
|
19
|
+
@import "tailwindcss/utilities.css" layer(utilities) prefix(jogak);
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* jogak 디자인 토큰 (`--jogak-*`).
|
|
23
|
+
*
|
|
24
|
+
* 정의 위치는 `:root` — preview 내부의 사용자 컴포넌트도 var() 참조 가능 (원하면).
|
|
25
|
+
* `--jogak-*` prefix가 충분한 namespace를 제공하므로 사용자 globalCss(알파.6 예정)
|
|
26
|
+
* 와 충돌 zero.
|
|
27
|
+
*
|
|
28
|
+
* 알파.4에서는 변수만 정의하고 컴포넌트는 inline style을 그대로 둔다.
|
|
29
|
+
* 알파.5에서 `style={{ color: '#374151' }}` → `className="jogak:text-[var(--jogak-color-fg)]"`
|
|
30
|
+
* 형태로 마이그레이션한다 (api-contracts §4.4).
|
|
31
|
+
*/
|
|
32
|
+
@layer base {
|
|
33
|
+
:root {
|
|
34
|
+
/* color */
|
|
35
|
+
--jogak-color-bg: #ffffff;
|
|
36
|
+
--jogak-color-fg: #374151;
|
|
37
|
+
--jogak-color-fg-muted: #6b7280;
|
|
38
|
+
--jogak-color-fg-subtle: #9ca3af;
|
|
39
|
+
--jogak-color-fg-strong: #111827;
|
|
40
|
+
--jogak-color-bg-subtle: #f9fafb;
|
|
41
|
+
--jogak-color-bg-elevated: #ffffff;
|
|
42
|
+
--jogak-color-bg-error: #fef2f2;
|
|
43
|
+
|
|
44
|
+
--jogak-color-accent: #2563eb;
|
|
45
|
+
--jogak-color-accent-bg: #eff6ff;
|
|
46
|
+
--jogak-color-accent-fg: #1d4ed8;
|
|
47
|
+
--jogak-color-accent-bg-soft: #dbeafe;
|
|
48
|
+
|
|
49
|
+
--jogak-color-violet: #7c3aed;
|
|
50
|
+
--jogak-color-violet-bg: #f5f3ff;
|
|
51
|
+
--jogak-color-violet-border:#ddd6fe;
|
|
52
|
+
|
|
53
|
+
--jogak-color-error: #ef4444;
|
|
54
|
+
--jogak-color-error-fg: #b91c1c;
|
|
55
|
+
--jogak-color-error-border: #fecaca;
|
|
56
|
+
|
|
57
|
+
--jogak-color-border: #e5e7eb;
|
|
58
|
+
--jogak-color-border-muted: #f3f4f6;
|
|
59
|
+
--jogak-color-border-strong:#d1d5db;
|
|
60
|
+
--jogak-color-checker: #e2e8f0;
|
|
61
|
+
|
|
62
|
+
/* spacing */
|
|
63
|
+
--jogak-space-1: 4px;
|
|
64
|
+
--jogak-space-2: 8px;
|
|
65
|
+
--jogak-space-3: 12px;
|
|
66
|
+
--jogak-space-4: 16px;
|
|
67
|
+
--jogak-space-5: 20px;
|
|
68
|
+
--jogak-space-6: 24px;
|
|
69
|
+
|
|
70
|
+
/* radius */
|
|
71
|
+
--jogak-radius-sm: 3px;
|
|
72
|
+
--jogak-radius-md: 4px;
|
|
73
|
+
--jogak-radius-lg: 6px;
|
|
74
|
+
--jogak-radius-xl: 8px;
|
|
75
|
+
|
|
76
|
+
/* typography */
|
|
77
|
+
--jogak-font-sans: system-ui, sans-serif;
|
|
78
|
+
--jogak-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
79
|
+
/*
|
|
80
|
+
* NOTE 알파.5 PR 4: --jogak-text-{xs,sm,base,md,lg} 5개 변수 + --jogak-sidebar-width
|
|
81
|
+
* 1개 삭제. 사용처 zero (PR 1/3에서 픽셀 literal 채택). v4 함정 1 (font-size 페어링)
|
|
82
|
+
* 회귀 자체 차단 + 알파.6 사용자 globalCss 충돌 가능성 zero.
|
|
83
|
+
*/
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/*
|
|
87
|
+
* 알파.6 wrapper 보호 rule (api-contracts §8 옵션 C).
|
|
88
|
+
*
|
|
89
|
+
* 사용자 globalCss(예: shadcn `@layer base { * { @apply border-border ... } }`)가
|
|
90
|
+
* jogak chrome의 form element를 침범하는 것을 차단한다. `:where(...)` specificity 0
|
|
91
|
+
* 으로 사용자가 명시적으로 `[data-jogak-shell] button { ... }`를 작성하면 자연스럽게
|
|
92
|
+
* 이긴다.
|
|
93
|
+
*
|
|
94
|
+
* 적용 범위: chrome의 모든 form element. preview(`[data-jogak-content]`) 안의
|
|
95
|
+
* 사용자 컴포넌트는 영향 받지 않음 — `:not([data-jogak-content] *)`로 제외.
|
|
96
|
+
*
|
|
97
|
+
* 알파.5 baseline 영향: zero. chrome 컴포넌트는 본 rule이 차단하는 사용자 reset이
|
|
98
|
+
* 없을 때 동일 시각 (revert-layer는 해당 layer가 없으면 no-op).
|
|
99
|
+
*/
|
|
100
|
+
[data-jogak-shell] :where(button, input, select, textarea):not([data-jogak-content] *) {
|
|
101
|
+
border-color: revert-layer;
|
|
102
|
+
background-color: revert-layer;
|
|
103
|
+
color: revert-layer;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@layer components {
|
|
108
|
+
/*
|
|
109
|
+
* 알파.5 PR 4: skeleton 애니메이션을 inline <style>(Preview L250-252)에서 jogak.css로
|
|
110
|
+
* 이동. inline style forbid rule (no-restricted-syntax) 위반 1건 + inline <style> 1건
|
|
111
|
+
* 동시 제거. VR 영향 zero (선택자/타이밍/그라디언트 픽셀 동일).
|
|
112
|
+
*/
|
|
113
|
+
@keyframes jogakSkeleton {
|
|
114
|
+
0% { background-position: 200% 0; }
|
|
115
|
+
100% { background-position: -200% 0; }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.jogak-skeleton-shimmer {
|
|
119
|
+
background: linear-gradient(
|
|
120
|
+
90deg,
|
|
121
|
+
rgba(229, 231, 235, 0) 0%,
|
|
122
|
+
rgba(229, 231, 235, 0.45) 50%,
|
|
123
|
+
rgba(229, 231, 235, 0) 100%
|
|
124
|
+
);
|
|
125
|
+
background-size: 200% 100%;
|
|
126
|
+
animation: jogakSkeleton 1.4s ease-in-out infinite;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
|
|
3
|
+
declare module 'virtual:jogak' {
|
|
4
|
+
/** 플러그인 설정에서 지정한 prism-react-renderer 테마 이름 */
|
|
5
|
+
export const _jogakCodeTheme: string
|
|
6
|
+
/**
|
|
7
|
+
* 알파.8: Preview 영역 격리 모드 ('none' | 'shadow' | 'iframe').
|
|
8
|
+
* `JogakPluginOptions.previewIsolation` (default 'iframe')의 literal emit.
|
|
9
|
+
*/
|
|
10
|
+
export const _jogakPreviewIsolation: 'none' | 'shadow' | 'iframe'
|
|
11
|
+
/**
|
|
12
|
+
* 알파.9: 어댑터 dev URL. iframe `src` base로 사용 (예: `http://localhost:5174`).
|
|
13
|
+
* 빈 문자열 시 fallback (jogak SPA Vite scope의 preview-frame.tsx).
|
|
14
|
+
*/
|
|
15
|
+
export const _jogakUserPreviewUrl: string
|
|
16
|
+
/**
|
|
17
|
+
* 알파.9: iframe entry path (`BuilderAdapter.previewEntryMeta.devEntryPath`).
|
|
18
|
+
* 어댑터별 routing (vite: `/__jogak_preview__/index.html`).
|
|
19
|
+
*/
|
|
20
|
+
export const _jogakPreviewEntryPath: string
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated 알파.10 제거 예정. `_jogakUserPreviewUrl` 사용.
|
|
23
|
+
* 알파.8 호환 alias.
|
|
24
|
+
*/
|
|
25
|
+
export const _jogakUserViteUrl: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare module 'virtual:jogak/global-css' {
|
|
29
|
+
// empty — side-effect only
|
|
30
|
+
}
|
package/dist/host/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";var L=Object.create;var g=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var k=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty;var B=(t,e,o,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of S(e))!A.call(t,n)&&n!==o&&g(t,n,{get:()=>e[n],enumerable:!(r=M(e,n))||r.enumerable});return t};var a=(t,e,o)=>(o=t!=null?L(k(t)):{},B(e||!t||!t.__esModule?g(o,"default",{value:t,enumerable:!0}):o,t));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const H=require("node:url"),i=require("node:path");var l=typeof document<"u"?document.currentScript:null;const N=H.fileURLToPath(typeof document>"u"?require("url").pathToFileURL(__filename).href:l&&l.tagName.toUpperCase()==="SCRIPT"&&l.src||new URL("host/index.js",document.baseURI).href),u=i.resolve(i.dirname(N),"..",".."),x=i.resolve(u,"index.html"),F=i.resolve(u,"src/app/main.tsx");async function E(t){const e=await import("vite"),o=await import("@vitejs/plugin-react"),r=await import("@jogak/core/vite"),{createServer:n,build:h}=e,b=o.default,{jogak:v}=r,_=t.codeTheme??"vsDark",f={patterns:t.patterns,codeTheme:_,cwd:t.userRoot};t.tsConfigFilePath!==void 0&&(f.tsConfigFilePath=t.tsConfigFilePath);const R=t.extraPlugins??[],d={root:u,configFile:!1,plugins:[b(),v(f),...R],optimizeDeps:{include:["react","react-dom/client","@jogak/core","@jogak/react"]}};if(t.mode==="dev"){const O={port:t.port??5173,host:t.host??"localhost",open:t.open??!1,fs:{allow:[u,t.userRoot]}},j={...d,server:O},s=await n(j);await s.listen();const p=s.config.server.port??t.port??5173,c=t.host??"localhost",D=`http://${typeof c=="boolean"?c?"0.0.0.0":"localhost":c}:${p.toString()}/`;let y=!1;return{url:D,port:p,close:async()=>{y||(y=!0,await s.close())},printUrls:()=>{s.printUrls()}}}const w={...d,base:t.base??"./",build:{outDir:t.outDir,emptyOutDir:!0,sourcemap:t.sourcemap??!1,minify:t.minify??"esbuild"}},C=Date.now(),T=await h(w),U=Date.now()-C,{assetCount:P,totalBytes:I}=Y(T);return{outDir:t.outDir,elapsedMs:U,assetCount:P,totalBytes:I}}function Y(t){const e=q(t);if(e===void 0)return{assetCount:0,totalBytes:0};let o=0,r=0;for(const n of e)o+=1,r+=z(n);return{assetCount:o,totalBytes:r}}function m(t){return typeof t=="object"&&t!==null&&Array.isArray(t.output)}function q(t){if(Array.isArray(t)){const e=[];for(const o of t)m(o)&&e.push(...o.output);return e}if(m(t))return t.output}function z(t){if(typeof t!="object"||t===null)return 0;const e=t;if(e.type==="chunk"&&typeof e.code=="string")return Buffer.byteLength(e.code,"utf8");if(e.type==="asset"){const o=e.source;if(typeof o=="string")return Buffer.byteLength(o,"utf8");if(o instanceof Uint8Array)return o.byteLength}return 0}exports.UI_HTML_ENTRY=x;exports.UI_MAIN_ENTRY=F;exports.runHost=E;
|
package/dist/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),c=require("react"),k=require("@jogak/core"),j=require("@jogak/react"),z=require("prism-react-renderer");function I({selectedEntryId:t,selectedJogakName:n,onSelect:o}){const[i,s]=c.useState(""),{metaTree:r,searchMeta:l}=j.useRegistryMeta(),d=i.trim().length>0?l(i):null;return e.jsxs("aside",{"data-testid":"sidebar",style:{borderRight:"1px solid #e5e7eb",height:"100%",overflow:"auto",display:"flex",flexDirection:"column"},children:[e.jsx("div",{style:{padding:"12px",borderBottom:"1px solid #e5e7eb"},children:e.jsx("input",{type:"search",placeholder:"Search components...",value:i,onChange:a=>{s(a.target.value)},style:{width:"100%",padding:"6px 8px",border:"1px solid #d1d5db",borderRadius:4},"aria-label":"Search components"})}),e.jsx("nav",{style:{flex:1,overflow:"auto",padding:"8px 0"},children:d!==null?e.jsx(J,{metas:d,selectedEntryId:t,selectedJogakName:n,onSelect:o}):e.jsx(O,{node:r,selectedEntryId:t,selectedJogakName:n,onSelect:o})})]})}function J({metas:t,selectedEntryId:n,selectedJogakName:o,onSelect:i}){return t.length===0?e.jsx("p",{style:{padding:"0 12px",color:"#9ca3af",fontSize:13},children:"No results"}):e.jsx("ul",{style:{listStyle:"none",margin:0,padding:0},children:t.map(s=>e.jsx("li",{children:e.jsx($,{meta:s,selectedEntryId:n,selectedJogakName:o,onSelect:i,indent:0})},s.id))})}function O({node:t,selectedEntryId:n,selectedJogakName:o,onSelect:i,depth:s=0}){return e.jsx("ul",{style:{listStyle:"none",margin:0,padding:`0 0 0 ${s*12}px`},children:Object.entries(t).map(([r,l])=>e.jsx("li",{children:"id"in l?e.jsx($,{meta:l,selectedEntryId:n,selectedJogakName:o,onSelect:i,indent:0}):e.jsx(V,{label:r,node:l,selectedEntryId:n,selectedJogakName:o,onSelect:i,depth:s+1})},r))})}function V({label:t,node:n,selectedEntryId:o,selectedJogakName:i,onSelect:s,depth:r}){const[l,d]=c.useState(!0);return e.jsxs("div",{children:[e.jsxs("button",{type:"button",onClick:()=>{d(a=>!a)},style:{display:"flex",alignItems:"center",gap:4,width:"100%",padding:"4px 12px",background:"none",border:"none",cursor:"pointer",fontSize:12,fontWeight:600,color:"#6b7280",textTransform:"uppercase",letterSpacing:"0.05em"},"aria-expanded":l,children:[e.jsx("span",{children:l?"▾":"▸"}),t]}),l&&e.jsx(O,{node:n,selectedEntryId:o,selectedJogakName:i,onSelect:s,depth:r})]})}function $({meta:t,selectedEntryId:n,selectedJogakName:o,onSelect:i,indent:s}){const r=t.id===n,[l,d]=c.useState(r);c.useEffect(()=>{r&&d(!0)},[r]);const a=t.title.split("/").pop()??t.title,b=16+s*12;return e.jsxs("div",{children:[e.jsxs("button",{type:"button",onClick:()=>{if(r)d(p=>!p);else{d(!0);const p=t.jogakNames[0];p!==void 0&&i(t.id,p)}},style:{display:"flex",alignItems:"center",gap:6,width:"100%",padding:`5px 12px 5px ${b}px`,background:r?"#eff6ff":"none",border:"none",cursor:"pointer",fontSize:13,color:r?"#2563eb":"#374151",fontWeight:r?500:400,textAlign:"left"},"aria-expanded":l,children:[e.jsx("span",{style:{fontSize:10,flexShrink:0,lineHeight:1},children:l?"▾":"▸"}),a]}),l&&e.jsx("ul",{style:{listStyle:"none",margin:0,padding:0},children:t.jogakNames.map(p=>{const x=r&&p===o;return e.jsx("li",{children:e.jsx("button",{type:"button",onClick:()=>{i(t.id,p)},style:{display:"block",width:"100%",textAlign:"left",padding:`4px 12px 4px ${b+18}px`,background:x?"#dbeafe":"none",border:"none",cursor:"pointer",fontSize:12,color:x?"#1d4ed8":"#6b7280",fontWeight:x?500:400},"aria-current":x?"true":void 0,children:p})},p)})})]})}function q(t,n){const o=n==null?void 0:n.control,i=(n==null?void 0:n.action)!==void 0&&n.action!==!1,s=(n==null?void 0:n.type)==="function"||typeof t=="function";return i||s?"action":o==="boolean"||typeof t=="boolean"?"boolean":o==="number"||o==="range"||typeof t=="number"?"number":o==="select"||o==="radio"||(n==null?void 0:n.options)!==void 0&&n.options.length>0?"select":o==="text"||o==="color"||typeof t=="string"?"text":"json"}function U({argKey:t,value:n,argType:o,onArgChange:i}){switch(q(n,o)){case"boolean":return e.jsx("input",{type:"checkbox",checked:n===!0,onChange:r=>{i(t,r.target.checked)},style:{cursor:"pointer",width:16,height:16,accentColor:"#2563eb"}});case"number":return e.jsx("input",{type:"number",value:typeof n=="number"?n:"",onChange:r=>{i(t,r.target.valueAsNumber)},style:w});case"select":{const r=(o==null?void 0:o.options)??[];return e.jsx("select",{value:String(n??""),onChange:l=>{i(t,l.target.value)},style:w,children:r.map(l=>e.jsx("option",{value:String(l),children:String(l)},String(l)))})}case"text":return e.jsx("input",{type:"text",value:typeof n=="string"?n:String(n??""),onChange:r=>{i(t,r.target.value)},style:w});case"action":return e.jsx("span",{style:{display:"inline-block",padding:"2px 8px",fontSize:11,fontWeight:600,color:"#7c3aed",background:"#f5f3ff",border:"1px solid #ddd6fe",borderRadius:4,fontFamily:"monospace"},children:"(action)"});case"json":return e.jsx("code",{style:{fontSize:12,color:"#6b7280",fontFamily:"monospace"},children:JSON.stringify(n)})}}const w={padding:"4px 8px",border:"1px solid #d1d5db",borderRadius:4,fontSize:13,width:"100%",maxWidth:280},C={padding:"6px 20px",textAlign:"left",color:"#6b7280",fontWeight:500,fontSize:12,borderBottom:"1px solid #e5e7eb"},R={padding:"8px 20px",verticalAlign:"middle",borderBottom:"1px solid #f3f4f6"};function F({args:t,argTypes:n,onArgChange:o}){const s=Array.from(new Set([...Object.keys(t),...Object.keys(n)])).map(r=>[r,t[r]]);return e.jsxs("div",{style:{borderTop:"2px solid #e5e7eb"},children:[e.jsx("div",{style:{padding:"6px 20px",fontSize:11,fontWeight:700,color:"#9ca3af",textTransform:"uppercase",letterSpacing:"0.08em",borderBottom:"1px solid #e5e7eb",background:"#f9fafb"},children:"Controls"}),s.length===0?e.jsx("div",{style:{padding:"12px 20px",color:"#9ca3af",fontSize:13},children:"No args defined"}):e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13},children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{style:C,children:"Name"}),e.jsx("th",{style:C,children:"Control"}),e.jsx("th",{style:C,children:"Description"})]})}),e.jsx("tbody",{children:s.map(([r,l])=>{const d=n[r];return e.jsxs("tr",{children:[e.jsx("td",{style:{...R,fontFamily:"monospace",fontSize:12,color:"#374151",whiteSpace:"nowrap"},children:r}),e.jsx("td",{style:R,children:e.jsx(U,{argKey:r,value:l,argType:d,onArgChange:o})}),e.jsx("td",{style:{...R,color:"#9ca3af"},children:(d==null?void 0:d.description)??""})]},r)})})]})]})}function _(t){if(t.length===0)return"()";try{return t.map(n=>{var o;if(n===null)return"null";if(n===void 0)return"undefined";if(typeof n=="function")return"[Function]";if(typeof n=="object"){const i=((o=n.constructor)==null?void 0:o.name)??"Object";return i!=="Object"&&i!=="Array"?`[${i}]`:JSON.stringify(n)}return JSON.stringify(n)}).join(", ")}catch{return"[unserializable]"}}function G(t){const n=new Date(t),o=n.getHours().toString().padStart(2,"0"),i=n.getMinutes().toString().padStart(2,"0"),s=n.getSeconds().toString().padStart(2,"0"),r=n.getMilliseconds().toString().padStart(3,"0");return`${o}:${i}:${s}.${r}`}function L(){const[t,n]=c.useState(()=>k.defaultActionChannel.getLogs());return c.useEffect(()=>k.defaultActionChannel.subscribe(n),[]),e.jsxs("div",{style:{height:"100%",display:"flex",flexDirection:"column"},children:[e.jsxs("div",{style:{padding:"6px 20px",fontSize:11,fontWeight:700,color:"#9ca3af",textTransform:"uppercase",letterSpacing:"0.08em",borderBottom:"1px solid #e5e7eb",background:"#f9fafb",display:"flex",alignItems:"center",justifyContent:"space-between",flexShrink:0},children:[e.jsxs("span",{children:["Actions ",t.length>0&&`(${t.length.toString()})`]}),e.jsx("button",{type:"button",onClick:()=>{k.defaultActionChannel.clear()},disabled:t.length===0,style:{fontSize:10,fontWeight:600,padding:"2px 8px",border:"1px solid #d1d5db",borderRadius:3,background:"#fff",color:t.length===0?"#9ca3af":"#374151",cursor:t.length===0?"default":"pointer",textTransform:"none",letterSpacing:0},children:"Clear"})]}),e.jsx("div",{style:{flex:1,overflow:"auto"},children:t.length===0?e.jsx("div",{style:{padding:"12px 20px",color:"#9ca3af",fontSize:13},children:"함수 prop이 호출되면 여기에 기록됩니다"}):e.jsx("ul",{style:{listStyle:"none",margin:0,padding:0,fontFamily:"monospace",fontSize:12},children:t.map(o=>e.jsxs("li",{style:{display:"flex",alignItems:"baseline",gap:10,padding:"6px 20px",borderBottom:"1px solid #f3f4f6"},children:[e.jsx("span",{style:{color:"#9ca3af",fontSize:11,minWidth:92},children:G(o.timestamp)}),e.jsx("span",{style:{color:"#7c3aed",fontWeight:600},children:o.name}),e.jsxs("span",{style:{color:"#374151",wordBreak:"break-all",flex:1},children:["(",_(o.args),")"]})]},o.id))})})]})}const H={mobile:375,tablet:768,desktop:"none"},T={mobile:"Mobile",tablet:"Tablet",desktop:"Desktop"},W={white:{background:"#ffffff"},dark:{background:"#1f2937"},transparent:{backgroundImage:["linear-gradient(45deg, #e2e8f0 25%, transparent 25%)","linear-gradient(-45deg, #e2e8f0 25%, transparent 25%)","linear-gradient(45deg, transparent 75%, #e2e8f0 75%)","linear-gradient(-45deg, transparent 75%, #e2e8f0 75%)"].join(", "),backgroundSize:"16px 16px",backgroundPosition:"0 0, 0 8px, 8px -8px, -8px 0px",backgroundColor:"#ffffff"}},A=320;function Q(t){return z.themes[t]??z.themes.vsDark}function D({entryId:t,jogakName:n,overrideArgs:o,onArgChange:i,onReset:s,codeTheme:r,onResolveJogak:l}){const d=j.useEntry(t),[a,b]=c.useState("desktop"),[p,x]=c.useState("white"),[y,h]=c.useState("controls"),m=Q(r);return d.status==="unknown"?e.jsxs("div",{"data-testid":"preview-not-found",style:{padding:24,color:"#ef4444"},children:["Entry not found: ",t]}):d.status==="error"?e.jsxs("div",{"data-testid":"preview-error",style:{padding:24,color:"#b91c1c",background:"#fef2f2",height:"100%",display:"flex",flexDirection:"column",gap:12,alignItems:"flex-start"},children:[e.jsxs("div",{style:{fontWeight:600},children:["Failed to load entry: ",t]}),e.jsx("pre",{style:{margin:0,padding:12,background:"#fff",border:"1px solid #fecaca",borderRadius:6,fontSize:12,whiteSpace:"pre-wrap",maxWidth:"100%"},children:d.error.message})]}):d.status==="loading"?e.jsx(Y,{meta:d.meta,jogakName:n,viewport:a,bgMode:p,onViewportChange:b,onBgModeChange:x}):e.jsx(X,{entry:d.entry,jogakName:n,overrideArgs:o,onArgChange:i,onReset:s,onResolveJogak:l,viewport:a,bgMode:p,bottomTab:y,onViewportChange:b,onBgModeChange:x,onBottomTabChange:h,prismTheme:m})}function Y({meta:t,jogakName:n,viewport:o,bgMode:i,onViewportChange:s,onBgModeChange:r}){const l=n??t.jogakNames[0]??"...",d=H[o];return e.jsxs("div",{"data-testid":"preview-loading",style:{display:"flex",flexDirection:"column",height:"100%"},children:[e.jsx(N,{title:t.title,jogakName:l,viewport:o,bgMode:i,onViewportChange:s,onBgModeChange:r,showReset:!1,onReset:()=>{}}),e.jsx("div",{style:{flex:1,minHeight:A,overflow:"auto",...W[i]},children:e.jsx("div",{style:{maxWidth:d==="none"?"100%":d,margin:"0 auto",padding:24},children:e.jsxs("div",{style:{border:"1px dashed #e5e7eb",borderRadius:8,padding:16,minHeight:A-64,display:"flex",alignItems:"center",justifyContent:"center",color:"#9ca3af",fontSize:13,background:"linear-gradient(90deg, rgba(229,231,235,0) 0%, rgba(229,231,235,0.45) 50%, rgba(229,231,235,0) 100%)",backgroundSize:"200% 100%",animation:"jogakSkeleton 1.4s ease-in-out infinite"},children:["Loading ",t.title,"…"]})})}),e.jsx("style",{children:"@keyframes jogakSkeleton { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }"})]})}function X({entry:t,jogakName:n,overrideArgs:o,onArgChange:i,onReset:s,onResolveJogak:r,viewport:l,bgMode:d,bottomTab:a,onViewportChange:b,onBgModeChange:p,onBottomTabChange:x,prismTheme:y}){var B;const h=n??((B=t.jogaks[0])==null?void 0:B.name)??null;if(c.useEffect(()=>{n===null&&h!==null&&r!==void 0&&r(t.id,h)},[n,h,t.id,r]),h===null)return e.jsxs("div",{style:{padding:24,color:"#ef4444"},children:["Entry has no jogaks: ",t.id]});const m=t.jogaks.find(S=>S.name===h);if(m===void 0)return e.jsxs("div",{style:{padding:24,color:"#ef4444"},children:["Jogak not found: ",h]});const f={...m.args??{},...o},g={...t.meta.argTypes??{},...m.argTypes??{}},P=Object.keys(o).length>0,M=H[l];return e.jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100%"},children:[e.jsx(N,{title:t.title,jogakName:m.name,viewport:l,bgMode:d,onViewportChange:b,onBgModeChange:p,showReset:P,onReset:s}),e.jsx("div",{style:{flex:1,minHeight:A,overflow:"auto",...W[d]},children:e.jsx("div",{style:{maxWidth:M==="none"?"100%":M,margin:"0 auto",padding:24},children:e.jsx(Z,{entry:t,args:f,source:t.source,theme:y},`${t.id}/${m.name}`)})}),e.jsxs("div",{style:{height:260,flexShrink:0,display:"flex",flexDirection:"column",borderTop:"2px solid #e5e7eb"},children:[e.jsx("div",{role:"tablist",style:{display:"flex",gap:4,padding:"4px 12px 0",background:"#fff",borderBottom:"1px solid #e5e7eb",flexShrink:0},children:["controls","actions"].map(S=>{const v=a===S;return e.jsx("button",{type:"button",role:"tab","aria-selected":v,onClick:()=>{x(S)},style:{padding:"6px 14px",fontSize:12,fontWeight:v?600:500,color:v?"#111827":"#6b7280",background:"transparent",border:"none",borderBottom:v?"2px solid #2563eb":"2px solid transparent",marginBottom:-1,cursor:"pointer",textTransform:"capitalize"},children:S},S)})}),e.jsx("div",{style:{flex:1,minHeight:0,overflow:"auto"},children:a==="controls"?e.jsx(F,{args:f,argTypes:g,onArgChange:i}):e.jsx(L,{})})]})]})}function N({title:t,jogakName:n,viewport:o,bgMode:i,onViewportChange:s,onBgModeChange:r,showReset:l,onReset:d}){return e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,padding:"7px 14px",borderBottom:"1px solid #e5e7eb",background:"#fff",flexShrink:0},children:[e.jsxs("div",{style:{flex:1,fontSize:13},children:[e.jsx("span",{style:{color:"#9ca3af"},children:t}),e.jsx("span",{style:{color:"#d1d5db",margin:"0 6px"},children:"/"}),e.jsx("span",{style:{color:"#111827",fontWeight:600},children:n})]}),e.jsx("div",{style:{display:"flex",gap:2,background:"#f3f4f6",borderRadius:6,padding:2},children:["mobile","tablet","desktop"].map(a=>e.jsx("button",{type:"button",onClick:()=>{s(a)},"aria-pressed":o===a,style:{padding:"3px 9px",fontSize:12,border:"none",borderRadius:4,cursor:"pointer",background:o===a?"#fff":"transparent",color:o===a?"#111827":"#6b7280",fontWeight:o===a?600:400,boxShadow:o===a?"0 1px 2px rgba(0,0,0,0.08)":"none",transition:"all 0.1s"},children:T[a]},a))}),e.jsx("div",{style:{display:"flex",gap:4,alignItems:"center"},children:["white","dark","transparent"].map(a=>e.jsx("button",{type:"button",onClick:()=>{r(a)},"aria-pressed":i===a,"aria-label":`${a} background`,style:{width:20,height:20,borderRadius:4,border:i===a?"2px solid #2563eb":"2px solid #d1d5db",cursor:"pointer",padding:0,flexShrink:0,...W[a]}},a))}),l&&e.jsx("button",{type:"button",onClick:d,style:{padding:"3px 10px",fontSize:12,border:"1px solid #d1d5db",borderRadius:4,background:"#fff",cursor:"pointer",color:"#374151"},children:"Reset"})]})}function Z({entry:t,args:n,source:o,theme:i}){const s=c.useRef(null),[r,l]=c.useState(!1);return c.useEffect(()=>{const d=s.current;if(d!==null)return j.reactAdapter.render(t,n,d),()=>{j.reactAdapter.unmount(d)}},[t]),c.useEffect(()=>{const d=s.current;d!==null&&j.reactAdapter.render(t,n,d)},[t,n]),e.jsxs("div",{children:[e.jsxs("div",{style:{position:"relative"},children:[e.jsx("div",{ref:s,"data-testid":"preview-content",style:{border:"1px dashed #e5e7eb",borderRadius:8,padding:16,paddingBottom:36}}),e.jsx("button",{type:"button",onClick:()=>{l(d=>!d)},"aria-pressed":r,"aria-label":r?"Hide source code":"Show source code",style:{position:"absolute",bottom:8,right:8,padding:"4px 9px",fontSize:11,fontFamily:"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",fontWeight:600,letterSpacing:"0.02em",background:r?"#2563eb":"#1e293b",color:"#fff",border:"none",borderRadius:5,cursor:"pointer",boxShadow:"0 1px 4px rgba(0,0,0,0.2)",transition:"background 0.15s"},children:"</>"})]}),r&&e.jsx("div",{style:{marginTop:8,borderRadius:8,overflow:"hidden",height:320,boxShadow:"0 0 0 1px rgba(0,0,0,0.08), 0 4px 16px rgba(0,0,0,0.12)"},children:e.jsx(K,{source:o,theme:i})})]})}function K({source:t,theme:n}){const[o,i]=c.useState(!1),s=n.plain.backgroundColor??"#1e293b";if(t===void 0)return e.jsx("div",{style:{height:"100%",display:"flex",alignItems:"center",justifyContent:"center",background:s,color:"#94a3b8",fontSize:13},children:"Source not available"});const r=()=>{navigator.clipboard.writeText(t).then(()=>{i(!0),setTimeout(()=>{i(!1)},2e3)})};return e.jsxs("div",{style:{position:"relative",height:"100%"},children:[e.jsx("button",{type:"button",onClick:r,style:{position:"absolute",top:10,right:12,zIndex:1,padding:"3px 9px",fontSize:11,background:"rgba(255,255,255,0.1)",color:"#e2e8f0",border:"1px solid rgba(255,255,255,0.18)",borderRadius:4,cursor:"pointer"},children:o?"✓ Copied":"Copy"}),e.jsx(z.Highlight,{code:t.trim(),language:"tsx",theme:n,children:({style:l,tokens:d,getLineProps:a,getTokenProps:b})=>e.jsx("pre",{style:{...l,margin:0,padding:"12px 0",fontSize:12.5,lineHeight:1.7,fontFamily:"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",height:"100%",boxSizing:"border-box",overflow:"auto"},children:d.map((p,x)=>e.jsxs("div",{...a({line:p}),style:{...a({line:p}).style,display:"flex",paddingRight:24},children:[e.jsx("span",{style:{userSelect:"none",minWidth:40,paddingLeft:14,paddingRight:14,textAlign:"right",color:"rgba(148,163,184,0.45)",flexShrink:0},children:x+1}),e.jsx("span",{children:p.map((y,h)=>e.jsx("span",{...b({token:y})},h))})]},x))})})]})}function E(){if(typeof window>"u")return null;const t=new URLSearchParams(window.location.search),n=t.get("entry");if(n===null)return null;const o=t.get("jogak");return{entryId:n,jogakName:o}}function ee(t,n){const o=new URLSearchParams;o.set("entry",t),o.set("jogak",n),window.history.pushState({},"",`?${o.toString()}`)}function te({entries:t,metas:n,codeTheme:o="vsDark"}={}){const i=c.useMemo(()=>{if(t!==void 0){n!==void 0&&console.warn("[jogak] JogakApp received both `entries` and `metas` — `entries` (eager) takes precedence.");const u=new k.ComponentRegistry;for(const f of t)u.register(f);return u}if(n!==void 0)for(const u of n)k.defaultRegistry.registerMeta(u);return k.defaultRegistry},[t,n]),s=c.useMemo(()=>E(),[]),[r,l]=c.useState((s==null?void 0:s.entryId)??null),[d,a]=c.useState((s==null?void 0:s.jogakName)??null),[b,p]=c.useState({});c.useEffect(()=>{const u=()=>{const f=E();f!==null?(l(f.entryId),a(f.jogakName),p({})):(l(null),a(null))};return window.addEventListener("popstate",u),()=>{window.removeEventListener("popstate",u)}},[]);const x=c.useCallback((u,f)=>{l(u),a(f),p({}),ee(u,f)},[]),y=c.useCallback((u,f)=>{if(l(g=>g===u?u:g),a(g=>g??f),typeof window<"u"){const g=new URLSearchParams(window.location.search);g.get("entry")===u&&g.get("jogak")===null&&(g.set("jogak",f),window.history.replaceState({},"",`?${g.toString()}`))}},[]),h=c.useCallback((u,f)=>{p(g=>({...g,[u]:f}))},[]),m=c.useCallback(()=>{p({})},[]);return e.jsx(j.JogakProvider,{registry:i,children:e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"260px 1fr",height:"100dvh",overflow:"hidden"},children:[e.jsx(I,{selectedEntryId:r,selectedJogakName:d,onSelect:x}),e.jsx("main",{style:{overflow:"hidden",minHeight:0},children:r!==null?e.jsx(D,{entryId:r,jogakName:d,overrideArgs:b,onArgChange:h,onReset:m,codeTheme:o,onResolveJogak:y}):e.jsx("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",height:"100%",color:"#9ca3af"},children:"Select a component from the sidebar"})})]})})}function ne(){const t=j.useRegistry(),n=c.useMemo(()=>t.getAll(),[t]),o=c.useMemo(()=>t.getTree(),[t]),i=c.useMemo(()=>s=>t.search(s),[t]);return{entries:n,tree:o,search:i}}exports.Actions=L;exports.Controls=F;exports.JogakApp=te;exports.Preview=D;exports.Sidebar=I;exports.useRegistry=ne;
|