@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.
- package/CHANGELOG.md +33 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +595 -912
- package/package.json +4 -3
- package/src/app/App.tsx +3 -16
- package/src/components/Actions/index.tsx +20 -50
- package/src/components/Controls/index.tsx +49 -70
- package/src/components/Preview/index.tsx +140 -250
- package/src/components/Sidebar/index.tsx +39 -66
- package/src/styles/jogak.css +27 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jogak/ui",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.5",
|
|
4
4
|
"description": "Showcase viewer UI for Jogak — Sidebar / Preview / Controls / Actions and the JogakApp shell.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jogak",
|
|
@@ -60,11 +60,12 @@
|
|
|
60
60
|
"registry": "https://registry.npmjs.org/"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
+
"clsx": "^2.1.1",
|
|
63
64
|
"prism-react-renderer": "^2.4.1",
|
|
64
65
|
"tailwindcss": "^4.0.0",
|
|
65
66
|
"@tailwindcss/vite": "^4.0.0",
|
|
66
|
-
"@jogak/core": "0.1.0-alpha.
|
|
67
|
-
"@jogak/react": "0.1.0-alpha.
|
|
67
|
+
"@jogak/core": "0.1.0-alpha.5",
|
|
68
|
+
"@jogak/react": "0.1.0-alpha.5"
|
|
68
69
|
},
|
|
69
70
|
"devDependencies": {
|
|
70
71
|
"@types/node": "^20.14.0",
|
package/src/app/App.tsx
CHANGED
|
@@ -127,19 +127,14 @@ export function JogakApp({
|
|
|
127
127
|
<JogakProvider registry={registry}>
|
|
128
128
|
<div
|
|
129
129
|
data-jogak-shell
|
|
130
|
-
|
|
131
|
-
display: 'grid',
|
|
132
|
-
gridTemplateColumns: '260px 1fr',
|
|
133
|
-
height: '100dvh',
|
|
134
|
-
overflow: 'hidden',
|
|
135
|
-
}}
|
|
130
|
+
className="jogak:grid jogak:grid-cols-[260px_1fr] jogak:h-dvh jogak:overflow-hidden"
|
|
136
131
|
>
|
|
137
132
|
<Sidebar
|
|
138
133
|
selectedEntryId={selectedEntryId}
|
|
139
134
|
selectedJogakName={selectedJogakName}
|
|
140
135
|
onSelect={handleSelect}
|
|
141
136
|
/>
|
|
142
|
-
<main
|
|
137
|
+
<main className="jogak:overflow-hidden jogak:min-h-0">
|
|
143
138
|
{selectedEntryId !== null ? (
|
|
144
139
|
<Preview
|
|
145
140
|
entryId={selectedEntryId}
|
|
@@ -151,15 +146,7 @@ export function JogakApp({
|
|
|
151
146
|
onResolveJogak={handleResolveJogak}
|
|
152
147
|
/>
|
|
153
148
|
) : (
|
|
154
|
-
<div
|
|
155
|
-
style={{
|
|
156
|
-
display: 'flex',
|
|
157
|
-
alignItems: 'center',
|
|
158
|
-
justifyContent: 'center',
|
|
159
|
-
height: '100%',
|
|
160
|
-
color: '#9ca3af',
|
|
161
|
-
}}
|
|
162
|
-
>
|
|
149
|
+
<div className="jogak:flex jogak:items-center jogak:justify-center jogak:h-full jogak:text-[var(--jogak-color-fg-subtle)]">
|
|
163
150
|
Select a component from the sidebar
|
|
164
151
|
</div>
|
|
165
152
|
)}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
2
|
import type { ReactElement } from 'react'
|
|
3
|
+
import clsx from 'clsx'
|
|
3
4
|
import { defaultActionChannel } from '@jogak/core'
|
|
4
5
|
import type { ActionLog } from '@jogak/core'
|
|
5
6
|
|
|
@@ -41,75 +42,44 @@ export function Actions(): ReactElement {
|
|
|
41
42
|
return defaultActionChannel.subscribe(setLogs)
|
|
42
43
|
}, [])
|
|
43
44
|
|
|
45
|
+
const isEmpty = logs.length === 0
|
|
46
|
+
|
|
44
47
|
return (
|
|
45
|
-
<div
|
|
46
|
-
<div
|
|
47
|
-
style={{
|
|
48
|
-
padding: '6px 20px',
|
|
49
|
-
fontSize: 11,
|
|
50
|
-
fontWeight: 700,
|
|
51
|
-
color: '#9ca3af',
|
|
52
|
-
textTransform: 'uppercase',
|
|
53
|
-
letterSpacing: '0.08em',
|
|
54
|
-
borderBottom: '1px solid #e5e7eb',
|
|
55
|
-
background: '#f9fafb',
|
|
56
|
-
display: 'flex',
|
|
57
|
-
alignItems: 'center',
|
|
58
|
-
justifyContent: 'space-between',
|
|
59
|
-
flexShrink: 0,
|
|
60
|
-
}}
|
|
61
|
-
>
|
|
48
|
+
<div className="jogak:h-full jogak:flex jogak:flex-col">
|
|
49
|
+
<div className="jogak:px-5 jogak:py-1.5 jogak:text-[11px] jogak:font-bold jogak:text-[var(--jogak-color-fg-subtle)] jogak:uppercase jogak:tracking-[0.08em] jogak:border-b jogak:border-[var(--jogak-color-border)] jogak:bg-[var(--jogak-color-bg-subtle)] jogak:flex jogak:items-center jogak:justify-between jogak:shrink-0">
|
|
62
50
|
<span>Actions {logs.length > 0 && `(${logs.length.toString()})`}</span>
|
|
63
51
|
<button
|
|
64
52
|
type="button"
|
|
65
53
|
onClick={() => { defaultActionChannel.clear() }}
|
|
66
|
-
disabled={
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
background: '#fff',
|
|
74
|
-
color: logs.length === 0 ? '#9ca3af' : '#374151',
|
|
75
|
-
cursor: logs.length === 0 ? 'default' : 'pointer',
|
|
76
|
-
textTransform: 'none',
|
|
77
|
-
letterSpacing: 0,
|
|
78
|
-
}}
|
|
54
|
+
disabled={isEmpty}
|
|
55
|
+
className={clsx(
|
|
56
|
+
'jogak:text-[10px] jogak:font-semibold jogak:px-2 jogak:py-0.5 jogak:border jogak:border-[var(--jogak-color-border-strong)] jogak:rounded-[var(--jogak-radius-sm)] jogak:bg-[var(--jogak-color-bg)] jogak:normal-case jogak:tracking-normal',
|
|
57
|
+
isEmpty
|
|
58
|
+
? 'jogak:text-[var(--jogak-color-fg-subtle)] jogak:cursor-default'
|
|
59
|
+
: 'jogak:text-[var(--jogak-color-fg)] jogak:cursor-pointer',
|
|
60
|
+
)}
|
|
79
61
|
>
|
|
80
62
|
Clear
|
|
81
63
|
</button>
|
|
82
64
|
</div>
|
|
83
65
|
|
|
84
|
-
<div
|
|
85
|
-
{
|
|
86
|
-
<div
|
|
87
|
-
style={{
|
|
88
|
-
padding: '12px 20px',
|
|
89
|
-
color: '#9ca3af',
|
|
90
|
-
fontSize: 13,
|
|
91
|
-
}}
|
|
92
|
-
>
|
|
66
|
+
<div className="jogak:flex-1 jogak:overflow-auto">
|
|
67
|
+
{isEmpty ? (
|
|
68
|
+
<div className="jogak:px-5 jogak:py-3 jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px] jogak:leading-none">
|
|
93
69
|
함수 prop이 호출되면 여기에 기록됩니다
|
|
94
70
|
</div>
|
|
95
71
|
) : (
|
|
96
|
-
<ul
|
|
72
|
+
<ul className="jogak:list-none jogak:m-0 jogak:p-0 jogak:font-[family-name:var(--jogak-font-mono)] jogak:text-[12px]">
|
|
97
73
|
{logs.map((log) => (
|
|
98
74
|
<li
|
|
99
75
|
key={log.id}
|
|
100
|
-
|
|
101
|
-
display: 'flex',
|
|
102
|
-
alignItems: 'baseline',
|
|
103
|
-
gap: 10,
|
|
104
|
-
padding: '6px 20px',
|
|
105
|
-
borderBottom: '1px solid #f3f4f6',
|
|
106
|
-
}}
|
|
76
|
+
className="jogak:flex jogak:items-baseline jogak:gap-[10px] jogak:px-5 jogak:py-1.5 jogak:border-b jogak:border-[var(--jogak-color-border-muted)]"
|
|
107
77
|
>
|
|
108
|
-
<span
|
|
78
|
+
<span className="jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[11px] jogak:min-w-[92px]">
|
|
109
79
|
{formatTime(log.timestamp)}
|
|
110
80
|
</span>
|
|
111
|
-
<span
|
|
112
|
-
<span
|
|
81
|
+
<span className="jogak:text-[var(--jogak-color-violet)] jogak:font-semibold">{log.name}</span>
|
|
82
|
+
<span className="jogak:text-[var(--jogak-color-fg)] jogak:break-all jogak:flex-1">
|
|
113
83
|
({formatArgs(log.args)})
|
|
114
84
|
</span>
|
|
115
85
|
</li>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ReactElement, ChangeEvent
|
|
1
|
+
import type { ReactElement, ChangeEvent } from 'react'
|
|
2
|
+
import clsx from 'clsx'
|
|
2
3
|
import type { ArgType } from '@jogak/core'
|
|
3
4
|
|
|
4
5
|
export interface ControlsProps {
|
|
@@ -34,6 +35,34 @@ interface ControlInputProps {
|
|
|
34
35
|
readonly onArgChange: (key: string, value: unknown) => void
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* input/select 공용 className — §6.10 의 inputClass 상수.
|
|
40
|
+
*
|
|
41
|
+
* `<select>` 에 `appearance-none` 을 일부러 적용하지 않는다 (UA dropdown 화살표 보존, §3.5).
|
|
42
|
+
* alpha.4 baseline 이 UA 기본 select 화살표를 캡처한 상태라 픽셀 동등을 위해 유지.
|
|
43
|
+
*/
|
|
44
|
+
const inputClass =
|
|
45
|
+
'jogak:px-2 jogak:py-1 ' +
|
|
46
|
+
'jogak:border jogak:border-[var(--jogak-color-border-strong)] ' +
|
|
47
|
+
'jogak:rounded-[var(--jogak-radius-md)] ' +
|
|
48
|
+
'jogak:text-[13px] ' +
|
|
49
|
+
'jogak:w-full jogak:max-w-[280px]'
|
|
50
|
+
|
|
51
|
+
/** th className 상수 — Controls table header cell. */
|
|
52
|
+
const thClass =
|
|
53
|
+
'jogak:px-5 jogak:py-1.5 ' +
|
|
54
|
+
'jogak:text-left ' +
|
|
55
|
+
'jogak:text-[var(--jogak-color-fg-muted)] ' +
|
|
56
|
+
'jogak:font-medium ' +
|
|
57
|
+
'jogak:text-[12px] ' +
|
|
58
|
+
'jogak:border-b jogak:border-[var(--jogak-color-border)]'
|
|
59
|
+
|
|
60
|
+
/** td className 상수 — Controls table body cell. */
|
|
61
|
+
const tdClass =
|
|
62
|
+
'jogak:px-5 jogak:py-2 ' +
|
|
63
|
+
'jogak:align-middle ' +
|
|
64
|
+
'jogak:border-b jogak:border-[var(--jogak-color-border-muted)]'
|
|
65
|
+
|
|
37
66
|
function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps): ReactElement {
|
|
38
67
|
const kind = resolveControlKind(value, argType)
|
|
39
68
|
|
|
@@ -46,7 +75,7 @@ function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps
|
|
|
46
75
|
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
47
76
|
onArgChange(argKey, e.target.checked)
|
|
48
77
|
}}
|
|
49
|
-
|
|
78
|
+
className="jogak:cursor-pointer jogak:w-4 jogak:h-4 jogak:accent-[var(--jogak-color-accent)]"
|
|
50
79
|
/>
|
|
51
80
|
)
|
|
52
81
|
case 'number':
|
|
@@ -57,7 +86,7 @@ function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps
|
|
|
57
86
|
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
58
87
|
onArgChange(argKey, e.target.valueAsNumber)
|
|
59
88
|
}}
|
|
60
|
-
|
|
89
|
+
className={inputClass}
|
|
61
90
|
/>
|
|
62
91
|
)
|
|
63
92
|
case 'select': {
|
|
@@ -68,7 +97,7 @@ function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps
|
|
|
68
97
|
onChange={(e: ChangeEvent<HTMLSelectElement>) => {
|
|
69
98
|
onArgChange(argKey, e.target.value)
|
|
70
99
|
}}
|
|
71
|
-
|
|
100
|
+
className={inputClass}
|
|
72
101
|
>
|
|
73
102
|
{options.map((opt) => (
|
|
74
103
|
<option key={String(opt)} value={String(opt)}>
|
|
@@ -86,91 +115,44 @@ function ControlInput({ argKey, value, argType, onArgChange }: ControlInputProps
|
|
|
86
115
|
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
87
116
|
onArgChange(argKey, e.target.value)
|
|
88
117
|
}}
|
|
89
|
-
|
|
118
|
+
className={inputClass}
|
|
90
119
|
/>
|
|
91
120
|
)
|
|
92
121
|
case 'action':
|
|
93
122
|
return (
|
|
94
|
-
<span
|
|
95
|
-
style={{
|
|
96
|
-
display: 'inline-block',
|
|
97
|
-
padding: '2px 8px',
|
|
98
|
-
fontSize: 11,
|
|
99
|
-
fontWeight: 600,
|
|
100
|
-
color: '#7c3aed',
|
|
101
|
-
background: '#f5f3ff',
|
|
102
|
-
border: '1px solid #ddd6fe',
|
|
103
|
-
borderRadius: 4,
|
|
104
|
-
fontFamily: 'monospace',
|
|
105
|
-
}}
|
|
106
|
-
>
|
|
123
|
+
<span className="jogak:inline-block jogak:px-2 jogak:py-0.5 jogak:text-[11px] jogak:font-semibold jogak:text-[var(--jogak-color-violet)] jogak:bg-[var(--jogak-color-violet-bg)] jogak:border jogak:border-[var(--jogak-color-violet-border)] jogak:rounded-[var(--jogak-radius-md)] jogak:font-[family-name:var(--jogak-font-mono)] jogak:leading-none">
|
|
107
124
|
(action)
|
|
108
125
|
</span>
|
|
109
126
|
)
|
|
110
127
|
case 'json':
|
|
111
128
|
return (
|
|
112
|
-
<code
|
|
129
|
+
<code className="jogak:text-[12px] jogak:text-[var(--jogak-color-fg-muted)] jogak:font-[family-name:var(--jogak-font-mono)]">
|
|
113
130
|
{JSON.stringify(value)}
|
|
114
131
|
</code>
|
|
115
132
|
)
|
|
116
133
|
}
|
|
117
134
|
}
|
|
118
135
|
|
|
119
|
-
const inputStyle: CSSProperties = {
|
|
120
|
-
padding: '4px 8px',
|
|
121
|
-
border: '1px solid #d1d5db',
|
|
122
|
-
borderRadius: 4,
|
|
123
|
-
fontSize: 13,
|
|
124
|
-
width: '100%',
|
|
125
|
-
maxWidth: 280,
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const thStyle: CSSProperties = {
|
|
129
|
-
padding: '6px 20px',
|
|
130
|
-
textAlign: 'left',
|
|
131
|
-
color: '#6b7280',
|
|
132
|
-
fontWeight: 500,
|
|
133
|
-
fontSize: 12,
|
|
134
|
-
borderBottom: '1px solid #e5e7eb',
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const tdStyle: CSSProperties = {
|
|
138
|
-
padding: '8px 20px',
|
|
139
|
-
verticalAlign: 'middle',
|
|
140
|
-
borderBottom: '1px solid #f3f4f6',
|
|
141
|
-
}
|
|
142
|
-
|
|
143
136
|
export function Controls({ args, argTypes, onArgChange }: ControlsProps): ReactElement {
|
|
144
137
|
const keys = Array.from(new Set([...Object.keys(args), ...Object.keys(argTypes)]))
|
|
145
138
|
const entries = keys.map((k) => [k, args[k]] as const)
|
|
146
139
|
|
|
147
140
|
return (
|
|
148
|
-
<div
|
|
149
|
-
<div
|
|
150
|
-
style={{
|
|
151
|
-
padding: '6px 20px',
|
|
152
|
-
fontSize: 11,
|
|
153
|
-
fontWeight: 700,
|
|
154
|
-
color: '#9ca3af',
|
|
155
|
-
textTransform: 'uppercase',
|
|
156
|
-
letterSpacing: '0.08em',
|
|
157
|
-
borderBottom: '1px solid #e5e7eb',
|
|
158
|
-
background: '#f9fafb',
|
|
159
|
-
}}
|
|
160
|
-
>
|
|
141
|
+
<div className="jogak:border-t-2 jogak:border-[var(--jogak-color-border)]">
|
|
142
|
+
<div className="jogak:px-5 jogak:py-1.5 jogak:text-[11px] jogak:font-bold jogak:text-[var(--jogak-color-fg-subtle)] jogak:uppercase jogak:tracking-[0.08em] jogak:border-b jogak:border-[var(--jogak-color-border)] jogak:bg-[var(--jogak-color-bg-subtle)]">
|
|
161
143
|
Controls
|
|
162
144
|
</div>
|
|
163
145
|
{entries.length === 0 ? (
|
|
164
|
-
<div
|
|
146
|
+
<div className="jogak:px-5 jogak:py-3 jogak:text-[var(--jogak-color-fg-subtle)] jogak:text-[13px]">
|
|
165
147
|
No args defined
|
|
166
148
|
</div>
|
|
167
149
|
) : (
|
|
168
|
-
<table
|
|
150
|
+
<table className="jogak:w-full jogak:border-collapse jogak:text-[13px]">
|
|
169
151
|
<thead>
|
|
170
152
|
<tr>
|
|
171
|
-
<th
|
|
172
|
-
<th
|
|
173
|
-
<th
|
|
153
|
+
<th className={thClass}>Name</th>
|
|
154
|
+
<th className={thClass}>Control</th>
|
|
155
|
+
<th className={thClass}>Description</th>
|
|
174
156
|
</tr>
|
|
175
157
|
</thead>
|
|
176
158
|
<tbody>
|
|
@@ -179,17 +161,14 @@ export function Controls({ args, argTypes, onArgChange }: ControlsProps): ReactE
|
|
|
179
161
|
return (
|
|
180
162
|
<tr key={key}>
|
|
181
163
|
<td
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
color: '#374151',
|
|
187
|
-
whiteSpace: 'nowrap',
|
|
188
|
-
}}
|
|
164
|
+
className={clsx(
|
|
165
|
+
tdClass,
|
|
166
|
+
'jogak:font-[family-name:var(--jogak-font-mono)] jogak:text-[12px] jogak:text-[var(--jogak-color-fg)] jogak:whitespace-nowrap',
|
|
167
|
+
)}
|
|
189
168
|
>
|
|
190
169
|
{key}
|
|
191
170
|
</td>
|
|
192
|
-
<td
|
|
171
|
+
<td className={tdClass}>
|
|
193
172
|
<ControlInput
|
|
194
173
|
argKey={key}
|
|
195
174
|
value={value}
|
|
@@ -197,7 +176,7 @@ export function Controls({ args, argTypes, onArgChange }: ControlsProps): ReactE
|
|
|
197
176
|
onArgChange={onArgChange}
|
|
198
177
|
/>
|
|
199
178
|
</td>
|
|
200
|
-
<td
|
|
179
|
+
<td className={clsx(tdClass, 'jogak:text-[var(--jogak-color-fg-subtle)]')}>
|
|
201
180
|
{argType?.description ?? ''}
|
|
202
181
|
</td>
|
|
203
182
|
</tr>
|