@kidecms/core 0.1.7 → 0.1.9
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/admin/components/DocumentActions.tsx +7 -1
- package/admin/components/FieldControl.astro +211 -248
- package/admin/components/RichTextEditor.tsx +2 -2
- package/admin/layouts/AdminLayout.astro +58 -24
- package/dist/admin.d.ts +33 -0
- package/dist/ai.d.ts +15 -0
- package/dist/api.d.ts +14 -0
- package/dist/assets.d.ts +51 -0
- package/dist/auth.d.ts +30 -0
- package/dist/content.d.ts +3 -0
- package/dist/create-admin.d.ts +5 -0
- package/dist/define.d.ts +254 -0
- package/dist/generator.d.ts +9 -0
- package/dist/image.d.ts +9 -0
- package/dist/index.d.ts +28 -0
- package/dist/integration.d.ts +14 -0
- package/dist/integration.js +16 -4
- package/dist/locks.d.ts +7 -0
- package/dist/richtext.d.ts +1 -0
- package/dist/runtime.d.ts +24 -0
- package/dist/schema.d.ts +17 -0
- package/dist/seed.d.ts +2 -0
- package/dist/values.d.ts +8 -0
- package/dist/webhooks.d.ts +2 -0
- package/dist/webhooks.js +3 -1
- package/package.json +1 -1
- package/routes/pages/admin/[...path].astro +4 -1
- package/routes/pages/admin/assets/index.astro +6 -1
|
@@ -78,7 +78,13 @@ export default function DocumentActions({
|
|
|
78
78
|
|
|
79
79
|
const canDuplicate = !!(showDuplicate && collectionSlug && documentId);
|
|
80
80
|
const hasActions =
|
|
81
|
-
canDuplicate ||
|
|
81
|
+
canDuplicate ||
|
|
82
|
+
showUnpublish ||
|
|
83
|
+
showDiscardDraft ||
|
|
84
|
+
showDelete ||
|
|
85
|
+
showSchedule ||
|
|
86
|
+
showCancelSchedule ||
|
|
87
|
+
versions.length > 0;
|
|
82
88
|
if (!hasActions) return null;
|
|
83
89
|
|
|
84
90
|
const duplicate = async () => {
|
|
@@ -68,7 +68,7 @@ const isTreeEditor =
|
|
|
68
68
|
const builtInComponents = new Set(["radio", "taxonomy-select", "menu-items", "taxonomy-terms"]);
|
|
69
69
|
const CustomComponent =
|
|
70
70
|
field.admin?.component && !builtInComponents.has(field.admin.component)
|
|
71
|
-
? customFields[field.admin.component] ?? null
|
|
71
|
+
? (customFields[field.admin.component] ?? null)
|
|
72
72
|
: null;
|
|
73
73
|
|
|
74
74
|
// Build serializable block types metadata for BlockEditor
|
|
@@ -122,276 +122,239 @@ const blockTypesMeta =
|
|
|
122
122
|
|
|
123
123
|
{
|
|
124
124
|
CustomComponent && (
|
|
125
|
-
<CustomComponent
|
|
126
|
-
client:load
|
|
127
|
-
name={name}
|
|
128
|
-
field={field}
|
|
129
|
-
value={serializedValue}
|
|
130
|
-
readOnly={readOnly}
|
|
131
|
-
/>
|
|
125
|
+
<CustomComponent client:load name={name} field={field} value={serializedValue} readOnly={readOnly} />
|
|
132
126
|
)
|
|
133
127
|
}
|
|
134
128
|
|
|
135
|
-
{!CustomComponent && (
|
|
136
|
-
<Fragment>
|
|
137
|
-
|
|
138
129
|
{
|
|
139
|
-
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
130
|
+
!CustomComponent && (
|
|
131
|
+
<Fragment>
|
|
132
|
+
{field.type === "image" && !readOnly && (
|
|
133
|
+
<ImagePicker client:load name={name} value={serializedValue} placeholder={placeholder} />
|
|
134
|
+
)}
|
|
143
135
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
156
|
-
}
|
|
136
|
+
{field.type === "image" && readOnly && (
|
|
137
|
+
<Input
|
|
138
|
+
className={controlClass}
|
|
139
|
+
type="text"
|
|
140
|
+
id={name}
|
|
141
|
+
name={name}
|
|
142
|
+
defaultValue={serializedValue}
|
|
143
|
+
placeholder={placeholder}
|
|
144
|
+
readOnly={readOnly}
|
|
145
|
+
/>
|
|
146
|
+
)}
|
|
157
147
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
148
|
+
{field.type === "text" && field.admin?.component === "taxonomy-select" && !readOnly && (
|
|
149
|
+
<TaxonomySelect
|
|
150
|
+
client:load
|
|
151
|
+
name={name}
|
|
152
|
+
value={serializedValue}
|
|
153
|
+
taxonomySlug={field.admin?.placeholder ?? ""}
|
|
154
|
+
/>
|
|
155
|
+
)}
|
|
163
156
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
))
|
|
190
|
-
}
|
|
157
|
+
{(field.type === "text" || field.type === "email" || field.type === "date") &&
|
|
158
|
+
field.admin?.component !== "taxonomy-select" &&
|
|
159
|
+
(field.type === "text" && field.admin?.rows ? (
|
|
160
|
+
<Textarea
|
|
161
|
+
className={controlClass}
|
|
162
|
+
id={name}
|
|
163
|
+
name={name}
|
|
164
|
+
rows={field.admin.rows}
|
|
165
|
+
defaultValue={serializedValue}
|
|
166
|
+
placeholder={placeholder}
|
|
167
|
+
readOnly={readOnly}
|
|
168
|
+
required={field.required}
|
|
169
|
+
/>
|
|
170
|
+
) : (
|
|
171
|
+
<Input
|
|
172
|
+
className={controlClass}
|
|
173
|
+
type={field.type === "email" ? "email" : field.type === "date" ? "date" : "text"}
|
|
174
|
+
id={name}
|
|
175
|
+
name={name}
|
|
176
|
+
defaultValue={serializedValue}
|
|
177
|
+
placeholder={placeholder}
|
|
178
|
+
readOnly={readOnly}
|
|
179
|
+
required={field.required}
|
|
180
|
+
/>
|
|
181
|
+
))}
|
|
191
182
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
)
|
|
205
|
-
}
|
|
183
|
+
{field.type === "slug" && (
|
|
184
|
+
<SlugField
|
|
185
|
+
client:load
|
|
186
|
+
name={name}
|
|
187
|
+
value={serializedValue}
|
|
188
|
+
from={field.from}
|
|
189
|
+
readOnly={readOnly}
|
|
190
|
+
required={field.required}
|
|
191
|
+
placeholder={placeholder || "auto-generated-slug"}
|
|
192
|
+
className={controlClass}
|
|
193
|
+
/>
|
|
194
|
+
)}
|
|
206
195
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)
|
|
220
|
-
}
|
|
196
|
+
{field.type === "number" && (
|
|
197
|
+
<Input
|
|
198
|
+
className={controlClass}
|
|
199
|
+
type="number"
|
|
200
|
+
id={name}
|
|
201
|
+
name={name}
|
|
202
|
+
defaultValue={serializedValue}
|
|
203
|
+
inputMode={inputMode}
|
|
204
|
+
readOnly={readOnly}
|
|
205
|
+
required={field.required}
|
|
206
|
+
/>
|
|
207
|
+
)}
|
|
221
208
|
|
|
222
|
-
|
|
209
|
+
{field.type === "boolean" && (
|
|
210
|
+
<CheckboxField client:load name={name} checked={Boolean(value)} disabled={readOnly} />
|
|
211
|
+
)}
|
|
223
212
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
)
|
|
235
|
-
}
|
|
213
|
+
{field.type === "select" && field.admin?.component !== "radio" && (
|
|
214
|
+
<SelectField
|
|
215
|
+
client:load
|
|
216
|
+
name={name}
|
|
217
|
+
value={String(value ?? "")}
|
|
218
|
+
placeholder="Select an option"
|
|
219
|
+
disabled={readOnly}
|
|
220
|
+
items={field.options.map((option) => ({ value: option, label: option }))}
|
|
221
|
+
/>
|
|
222
|
+
)}
|
|
236
223
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
)
|
|
255
|
-
}
|
|
224
|
+
{field.type === "select" && field.admin?.component === "radio" && (
|
|
225
|
+
<div class="flex flex-wrap gap-x-5 gap-y-2 has-disabled:opacity-50">
|
|
226
|
+
{field.options.map((option) => (
|
|
227
|
+
<label class="hover:text-foreground flex items-center gap-2 text-sm transition-colors has-disabled:cursor-not-allowed has-disabled:hover:text-current">
|
|
228
|
+
<input
|
|
229
|
+
class="border-input hover:border-primary/60 checked:border-primary checked:hover:border-primary focus-visible:ring-ring/50 disabled:hover:border-input size-4 shrink-0 appearance-none rounded-full border transition-all checked:border-[5px] focus-visible:ring-3 focus-visible:outline-none disabled:cursor-not-allowed"
|
|
230
|
+
type="radio"
|
|
231
|
+
name={name}
|
|
232
|
+
value={option}
|
|
233
|
+
checked={String(value ?? "") === option}
|
|
234
|
+
disabled={readOnly}
|
|
235
|
+
/>
|
|
236
|
+
<span class="select-none">{option}</span>
|
|
237
|
+
</label>
|
|
238
|
+
))}
|
|
239
|
+
</div>
|
|
240
|
+
)}
|
|
256
241
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
)
|
|
270
|
-
}
|
|
242
|
+
{field.type === "relation" && !readOnly && relationMeta && (
|
|
243
|
+
<RelationField
|
|
244
|
+
client:load
|
|
245
|
+
name={name}
|
|
246
|
+
value={Array.isArray(value) ? JSON.stringify(value) : String(value ?? "")}
|
|
247
|
+
hasMany={relationMeta.hasMany}
|
|
248
|
+
options={relationOptions}
|
|
249
|
+
collectionSlug={relationMeta.collectionSlug}
|
|
250
|
+
collectionLabel={relationMeta.collectionLabel}
|
|
251
|
+
labelField={relationMeta.labelField}
|
|
252
|
+
/>
|
|
253
|
+
)}
|
|
271
254
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
)
|
|
283
|
-
}
|
|
255
|
+
{field.type === "relation" && (readOnly || !relationMeta) && (
|
|
256
|
+
<SelectField
|
|
257
|
+
client:load
|
|
258
|
+
name={name}
|
|
259
|
+
value={String(value ?? "")}
|
|
260
|
+
placeholder="Select a document"
|
|
261
|
+
disabled={readOnly}
|
|
262
|
+
items={relationOptions}
|
|
263
|
+
/>
|
|
264
|
+
)}
|
|
284
265
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
)
|
|
289
|
-
}
|
|
266
|
+
{field.type === "richText" && !readOnly && (
|
|
267
|
+
<RichTextEditor client:load name={name} initialValue={value ? JSON.stringify(value) : ""} rows={rows} />
|
|
268
|
+
)}
|
|
290
269
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
)
|
|
302
|
-
}
|
|
270
|
+
{field.type === "richText" && readOnly && (
|
|
271
|
+
<Textarea
|
|
272
|
+
className={controlClass}
|
|
273
|
+
id={name}
|
|
274
|
+
name={name}
|
|
275
|
+
rows={rows}
|
|
276
|
+
readOnly={readOnly}
|
|
277
|
+
defaultValue={serializedValue}
|
|
278
|
+
/>
|
|
279
|
+
)}
|
|
303
280
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
)
|
|
315
|
-
}
|
|
281
|
+
{field.type === "json" && field.admin?.component === "menu-items" && !readOnly && (
|
|
282
|
+
<TreeItemsEditor
|
|
283
|
+
client:load
|
|
284
|
+
name={name}
|
|
285
|
+
value={serializedValue}
|
|
286
|
+
variant="menu"
|
|
287
|
+
label={label}
|
|
288
|
+
linkOptions={menuLinkOptions}
|
|
289
|
+
/>
|
|
290
|
+
)}
|
|
316
291
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
)
|
|
321
|
-
}
|
|
292
|
+
{field.type === "json" && field.admin?.component === "taxonomy-terms" && !readOnly && (
|
|
293
|
+
<TreeItemsEditor client:load name={name} value={serializedValue} variant="taxonomy" label={label} />
|
|
294
|
+
)}
|
|
322
295
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
)
|
|
336
|
-
}
|
|
296
|
+
{field.type === "json" &&
|
|
297
|
+
(field.admin?.component === "menu-items" || field.admin?.component === "taxonomy-terms") &&
|
|
298
|
+
readOnly && (
|
|
299
|
+
<Textarea
|
|
300
|
+
className={controlClass}
|
|
301
|
+
id={name}
|
|
302
|
+
name={name}
|
|
303
|
+
rows={rows}
|
|
304
|
+
readOnly={readOnly}
|
|
305
|
+
defaultValue={serializedValue}
|
|
306
|
+
/>
|
|
307
|
+
)}
|
|
337
308
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
)
|
|
348
|
-
}
|
|
309
|
+
{field.type === "blocks" && !readOnly && (
|
|
310
|
+
<BlockEditor
|
|
311
|
+
client:load
|
|
312
|
+
name={name}
|
|
313
|
+
value={serializedValue}
|
|
314
|
+
types={blockTypesMeta}
|
|
315
|
+
blockRelationOptions={blockRelationOptions}
|
|
316
|
+
/>
|
|
317
|
+
)}
|
|
349
318
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
)
|
|
361
|
-
}
|
|
319
|
+
{field.type === "blocks" && readOnly && (
|
|
320
|
+
<Textarea
|
|
321
|
+
className={controlClass}
|
|
322
|
+
id={name}
|
|
323
|
+
name={name}
|
|
324
|
+
rows={rows}
|
|
325
|
+
readOnly={readOnly}
|
|
326
|
+
defaultValue={serializedValue}
|
|
327
|
+
/>
|
|
328
|
+
)}
|
|
362
329
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
)
|
|
375
|
-
}
|
|
330
|
+
{field.type === "array" && "of" in field && field.of?.type === "text" && (
|
|
331
|
+
<Input
|
|
332
|
+
className={controlClass}
|
|
333
|
+
id={name}
|
|
334
|
+
name={name}
|
|
335
|
+
placeholder={placeholder || "item1, item2, item3"}
|
|
336
|
+
readOnly={readOnly}
|
|
337
|
+
required={field.required}
|
|
338
|
+
defaultValue={serializedValue}
|
|
339
|
+
/>
|
|
340
|
+
)}
|
|
376
341
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
342
|
+
{((field.type === "array" && !("of" in field && field.of?.type === "text")) ||
|
|
343
|
+
(field.type === "json" &&
|
|
344
|
+
field.admin?.component !== "menu-items" &&
|
|
345
|
+
field.admin?.component !== "taxonomy-terms")) && (
|
|
346
|
+
<Textarea
|
|
347
|
+
className={controlClass}
|
|
348
|
+
id={name}
|
|
349
|
+
name={name}
|
|
350
|
+
rows={rows}
|
|
351
|
+
placeholder={placeholder}
|
|
352
|
+
readOnly={readOnly}
|
|
353
|
+
required={field.required}
|
|
354
|
+
defaultValue={serializedValue}
|
|
355
|
+
/>
|
|
356
|
+
)}
|
|
357
|
+
</Fragment>
|
|
392
358
|
)
|
|
393
359
|
}
|
|
394
|
-
|
|
395
|
-
</Fragment>
|
|
396
|
-
)}
|
|
397
360
|
</div>
|
|
@@ -180,7 +180,7 @@ const ToolbarButton = ({
|
|
|
180
180
|
disabled={disabled}
|
|
181
181
|
title={title}
|
|
182
182
|
className={cn(
|
|
183
|
-
"focus-visible:ring-ring/50 focus-visible:border-ring inline-flex size-8 items-center justify-center rounded-md transition-colors outline-none focus-visible:ring-2 disabled:opacity-50 disabled:hover:bg-transparent
|
|
183
|
+
"focus-visible:ring-ring/50 focus-visible:border-ring disabled:hover:text-muted-foreground inline-flex size-8 items-center justify-center rounded-md transition-colors outline-none focus-visible:ring-2 disabled:opacity-50 disabled:hover:bg-transparent",
|
|
184
184
|
active ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:bg-accent/60 hover:text-foreground",
|
|
185
185
|
)}
|
|
186
186
|
>
|
|
@@ -623,7 +623,7 @@ export default function RichTextEditor({ name, initialValue, rows = 10, onChange
|
|
|
623
623
|
disabled={!editor}
|
|
624
624
|
title={markdownMode ? "Switch to editor" : "Switch to Markdown"}
|
|
625
625
|
>
|
|
626
|
-
<span className="text-xs font-semibold
|
|
626
|
+
<span className="text-xs leading-none font-semibold">MD</span>
|
|
627
627
|
</ToolbarButton>
|
|
628
628
|
</div>
|
|
629
629
|
|
|
@@ -37,10 +37,36 @@ import {
|
|
|
37
37
|
} from "lucide-react";
|
|
38
38
|
|
|
39
39
|
const navIconMap: Record<string, any> = {
|
|
40
|
-
BarChart,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
BarChart,
|
|
41
|
+
Bell,
|
|
42
|
+
Bookmark,
|
|
43
|
+
Calendar,
|
|
44
|
+
Clock,
|
|
45
|
+
Database,
|
|
46
|
+
FileText,
|
|
47
|
+
FolderTree,
|
|
48
|
+
Globe,
|
|
49
|
+
Home,
|
|
50
|
+
Image,
|
|
51
|
+
Key,
|
|
52
|
+
Layers,
|
|
53
|
+
LayoutGrid,
|
|
54
|
+
Link,
|
|
55
|
+
Lock,
|
|
56
|
+
Mail,
|
|
57
|
+
Menu,
|
|
58
|
+
MessageSquare,
|
|
59
|
+
Package,
|
|
60
|
+
Palette,
|
|
61
|
+
PencilRuler,
|
|
62
|
+
Search,
|
|
63
|
+
Settings,
|
|
64
|
+
Shield,
|
|
65
|
+
Star,
|
|
66
|
+
Tag,
|
|
67
|
+
Terminal,
|
|
68
|
+
Users,
|
|
69
|
+
Zap,
|
|
44
70
|
};
|
|
45
71
|
|
|
46
72
|
import MobileSidebar from "../components/MobileSidebar";
|
|
@@ -90,7 +116,15 @@ const singletonSlugs = new Set(collections.filter((c) => c.singleton).map((c) =>
|
|
|
90
116
|
const isActiveSingles =
|
|
91
117
|
activeCollection === "singles" || (activeCollection !== null && singletonSlugs.has(activeCollection));
|
|
92
118
|
|
|
93
|
-
type NavItem = {
|
|
119
|
+
type NavItem = {
|
|
120
|
+
href: string;
|
|
121
|
+
label: string;
|
|
122
|
+
singularLabel?: string;
|
|
123
|
+
Icon: any;
|
|
124
|
+
active: boolean;
|
|
125
|
+
newHref?: string;
|
|
126
|
+
weight: number;
|
|
127
|
+
};
|
|
94
128
|
const navItems: NavItem[] = [];
|
|
95
129
|
|
|
96
130
|
// Built-in nav items with default weights (spaced by 10 for easy interleaving)
|
|
@@ -210,31 +244,31 @@ navItems.sort((a, b) => a.weight - b.weight);
|
|
|
210
244
|
<nav class="flex-1 space-y-1 overflow-y-auto px-3 py-3">
|
|
211
245
|
{navItems.map((item) => (
|
|
212
246
|
<div
|
|
247
|
+
class={cn(
|
|
248
|
+
"group relative rounded-lg",
|
|
249
|
+
item.active ? "bg-foreground/10 dark:bg-accent" : "hover:bg-foreground/5 dark:hover:bg-accent/60",
|
|
250
|
+
)}
|
|
251
|
+
>
|
|
252
|
+
<a
|
|
253
|
+
href={item.href}
|
|
213
254
|
class={cn(
|
|
214
|
-
"
|
|
215
|
-
item.
|
|
255
|
+
"flex items-center gap-3 px-3 py-2.5 text-sm",
|
|
256
|
+
item.newHref && "pr-8",
|
|
257
|
+
item.active ? "text-accent-foreground" : "text-foreground/70 group-hover:text-foreground",
|
|
216
258
|
)}
|
|
217
259
|
>
|
|
260
|
+
<item.Icon className="size-4 shrink-0 stroke-1" />
|
|
261
|
+
<span>{item.label}</span>
|
|
262
|
+
</a>
|
|
263
|
+
{item.newHref && (
|
|
218
264
|
<a
|
|
219
|
-
href={item.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
item.newHref && "pr-8",
|
|
223
|
-
item.active ? "text-accent-foreground" : "text-foreground/70 group-hover:text-foreground",
|
|
224
|
-
)}
|
|
265
|
+
href={item.newHref}
|
|
266
|
+
title={`Add new ${(item.singularLabel ?? item.label).toLowerCase()}`}
|
|
267
|
+
class="text-foreground/40 hover:text-foreground/70 absolute top-1/2 right-1.5 hidden -translate-y-1/2 rounded p-1 group-hover:block"
|
|
225
268
|
>
|
|
226
|
-
<
|
|
227
|
-
<span>{item.label}</span>
|
|
269
|
+
<Plus className="size-3.5" />
|
|
228
270
|
</a>
|
|
229
|
-
|
|
230
|
-
<a
|
|
231
|
-
href={item.newHref}
|
|
232
|
-
title={`Add new ${(item.singularLabel ?? item.label).toLowerCase()}`}
|
|
233
|
-
class="text-foreground/40 hover:text-foreground/70 absolute top-1/2 right-1.5 hidden -translate-y-1/2 rounded p-1 group-hover:block"
|
|
234
|
-
>
|
|
235
|
-
<Plus className="size-3.5" />
|
|
236
|
-
</a>
|
|
237
|
-
)}
|
|
271
|
+
)}
|
|
238
272
|
</div>
|
|
239
273
|
))}
|
|
240
274
|
</nav>
|