@jogak/ui 0.1.0-alpha.4 → 0.1.0-alpha.5

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.
@@ -1,8 +1,14 @@
1
1
  import { useState, useEffect } from 'react'
2
- import type { ReactElement } from 'react'
2
+ import type { CSSProperties, ReactElement } from 'react'
3
+ import clsx from 'clsx'
3
4
  import type { CategoryMetaTree, RegistryEntryMeta } from '@jogak/core'
4
5
  import { useRegistryMeta } from '@jogak/react'
5
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
+
6
12
  export interface SidebarProps {
7
13
  readonly selectedEntryId: string | null
8
14
  readonly selectedJogakName: string | null
@@ -26,15 +32,9 @@ export function Sidebar({
26
32
  return (
27
33
  <aside
28
34
  data-testid="sidebar"
29
- style={{
30
- borderRight: '1px solid #e5e7eb',
31
- height: '100%',
32
- overflow: 'auto',
33
- display: 'flex',
34
- flexDirection: 'column',
35
- }}
35
+ className="jogak:flex jogak:flex-col jogak:h-full jogak:overflow-auto jogak:border-r jogak:border-[var(--jogak-color-border)]"
36
36
  >
37
- <div style={{ padding: '12px', borderBottom: '1px solid #e5e7eb' }}>
37
+ <div className="jogak:p-3 jogak:border-b jogak:border-[var(--jogak-color-border)]">
38
38
  <input
39
39
  type="search"
40
40
  placeholder="Search components..."
@@ -42,16 +42,11 @@ export function Sidebar({
42
42
  onChange={(e) => {
43
43
  setQuery(e.target.value)
44
44
  }}
45
- style={{
46
- width: '100%',
47
- padding: '6px 8px',
48
- border: '1px solid #d1d5db',
49
- borderRadius: 4,
50
- }}
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)]"
51
46
  aria-label="Search components"
52
47
  />
53
48
  </div>
54
- <nav style={{ flex: 1, overflow: 'auto', padding: '8px 0' }}>
49
+ <nav className="jogak:flex-1 jogak:overflow-auto jogak:py-2">
55
50
  {filtered !== null ? (
56
51
  <FlatList
57
52
  metas={filtered}
@@ -87,13 +82,13 @@ function FlatList({
87
82
  }: FlatListProps): ReactElement {
88
83
  if (metas.length === 0) {
89
84
  return (
90
- <p style={{ padding: '0 12px', color: '#9ca3af', fontSize: 13 }}>
85
+ <p className="jogak:px-3 jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px]">
91
86
  No results
92
87
  </p>
93
88
  )
94
89
  }
95
90
  return (
96
- <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
91
+ <ul className="jogak:list-none jogak:m-0 jogak:p-0">
97
92
  {metas.map((meta) => (
98
93
  <li key={meta.id}>
99
94
  <EntryGroup
@@ -126,11 +121,9 @@ function TreeView({
126
121
  }: TreeViewProps): ReactElement {
127
122
  return (
128
123
  <ul
129
- style={{
130
- listStyle: 'none',
131
- margin: 0,
132
- padding: `0 0 0 ${depth * 12}px`,
133
- }}
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}
134
127
  >
135
128
  {Object.entries(node).map(([key, child]) => (
136
129
  <li key={key}>
@@ -183,21 +176,7 @@ function CategoryGroup({
183
176
  onClick={() => {
184
177
  setOpen((v) => !v)
185
178
  }}
186
- style={{
187
- display: 'flex',
188
- alignItems: 'center',
189
- gap: 4,
190
- width: '100%',
191
- padding: '4px 12px',
192
- background: 'none',
193
- border: 'none',
194
- cursor: 'pointer',
195
- fontSize: 12,
196
- fontWeight: 600,
197
- color: '#6b7280',
198
- textTransform: 'uppercase',
199
- letterSpacing: '0.05em',
200
- }}
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"
201
180
  aria-expanded={open}
202
181
  >
203
182
  <span>{open ? '▾' : '▸'}</span>
@@ -254,29 +233,25 @@ function EntryGroup({
254
233
  setOpen((v) => !v)
255
234
  }
256
235
  }}
257
- style={{
258
- display: 'flex',
259
- alignItems: 'center',
260
- gap: 6,
261
- width: '100%',
262
- padding: `5px 12px 5px ${paddingLeft}px`,
263
- background: isCurrentEntry ? '#eff6ff' : 'none',
264
- border: 'none',
265
- cursor: 'pointer',
266
- fontSize: 13,
267
- color: isCurrentEntry ? '#2563eb' : '#374151',
268
- fontWeight: isCurrentEntry ? 500 : 400,
269
- textAlign: 'left',
270
- }}
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}
271
246
  aria-expanded={open}
272
247
  >
273
- <span style={{ fontSize: 10, flexShrink: 0, lineHeight: 1 }}>
248
+ <span className="jogak:text-[10px] jogak:shrink-0 jogak:leading-none">
274
249
  {open ? '▾' : '▸'}
275
250
  </span>
276
251
  {label}
277
252
  </button>
278
253
  {open && (
279
- <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
254
+ <ul className="jogak:list-none jogak:m-0 jogak:p-0">
280
255
  {meta.jogakNames.map((jogakName) => {
281
256
  const isSelected = isCurrentEntry && jogakName === selectedJogakName
282
257
  return (
@@ -286,18 +261,16 @@ function EntryGroup({
286
261
  onClick={() => {
287
262
  onSelect(meta.id, jogakName)
288
263
  }}
289
- style={{
290
- display: 'block',
291
- width: '100%',
292
- textAlign: 'left',
293
- padding: `4px 12px 4px ${paddingLeft + 18}px`,
294
- background: isSelected ? '#dbeafe' : 'none',
295
- border: 'none',
296
- cursor: 'pointer',
297
- fontSize: 12,
298
- color: isSelected ? '#1d4ed8' : '#6b7280',
299
- fontWeight: isSelected ? 500 : 400,
300
- }}
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}
301
274
  aria-current={isSelected ? 'true' : undefined}
302
275
  >
303
276
  {jogakName}
@@ -76,13 +76,33 @@
76
76
  /* typography */
77
77
  --jogak-font-sans: system-ui, sans-serif;
78
78
  --jogak-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
79
- --jogak-text-xs: 10px;
80
- --jogak-text-sm: 11px;
81
- --jogak-text-base: 12px;
82
- --jogak-text-md: 13px;
83
- --jogak-text-lg: 14px;
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
+ @layer components {
88
+ /*
89
+ * 알파.5 PR 4: skeleton 애니메이션을 inline <style>(Preview L250-252)에서 jogak.css로
90
+ * 이동. inline style forbid rule (no-restricted-syntax) 위반 1건 + inline <style> 1건
91
+ * 동시 제거. VR 영향 zero (선택자/타이밍/그라디언트 픽셀 동일).
92
+ */
93
+ @keyframes jogakSkeleton {
94
+ 0% { background-position: 200% 0; }
95
+ 100% { background-position: -200% 0; }
96
+ }
84
97
 
85
- /* layout 상수 */
86
- --jogak-sidebar-width: 260px;
98
+ .jogak-skeleton-shimmer {
99
+ background: linear-gradient(
100
+ 90deg,
101
+ rgba(229, 231, 235, 0) 0%,
102
+ rgba(229, 231, 235, 0.45) 50%,
103
+ rgba(229, 231, 235, 0) 100%
104
+ );
105
+ background-size: 200% 100%;
106
+ animation: jogakSkeleton 1.4s ease-in-out infinite;
87
107
  }
88
108
  }