feat: Nova UI component library + Studio dropdown/picker polish

- Add Nova UI library: Button, TextInput, Textarea, FormField, Select,
  NovaSelect, Checkbox, Radio/RadioGroup, Toggle, DatePicker,
  DateRangePicker, Modal + barrel index.js
- Replace all native <select> in Studio with NovaSelect (StudioFilters,
  StudioToolbar, BulkActionsBar) including frosted-glass portal and
  category group headers
- Replace native checkboxes in StudioGridCard, StudioTable, UploadSidebar,
  UploadWizard, Upload/Index with custom Checkbox component
- Add nova-scrollbar CSS utility (thin 4px, semi-transparent)
- Fix portal position drift: use viewport-relative coords (no scrollY offset)
  for NovaSelect, DatePicker and DateRangePicker
- Close portals on external scroll instead of remeasuring
- Improve hover highlight visibility in NovaSelect (bg-white/[0.13])
- Move search icon to right side in NovaSelect dropdown
- Reduce Studio layout top spacing (py-6 -> pt-4 pb-8)
- Add StudioCheckbox and SquareCheckbox backward-compat shims
- Add sync.sh rsync deploy script
This commit is contained in:
2026-03-01 10:41:43 +01:00
parent e3ca845a6d
commit a875203482
26 changed files with 2087 additions and 132 deletions

View File

@@ -1,4 +1,5 @@
import React, { useState } from 'react'
import NovaSelect from '../ui/NovaSelect'
const actions = [
{ value: 'publish', label: 'Publish', icon: 'fa-eye', danger: false },
@@ -37,18 +38,15 @@ export default function BulkActionsBar({ count, onExecute, onClearSelection }) {
</div>
<div className="flex items-center gap-2">
<select
value={action}
onChange={(e) => setAction(e.target.value)}
className="px-3 py-2 rounded-xl bg-white/5 border border-white/10 text-white text-sm focus:outline-none focus:ring-2 focus:ring-accent/50 min-w-[180px]"
>
<option value="" className="bg-nova-900">Choose action</option>
{actions.map((a) => (
<option key={a.value} value={a.value} className="bg-nova-900">
{a.label}
</option>
))}
</select>
<div className="min-w-[180px]">
<NovaSelect
options={actions.map((a) => ({ value: a.value, label: a.label }))}
value={action || null}
onChange={(val) => setAction(val ?? '')}
placeholder="Choose action…"
searchable={false}
/>
</div>
<button
onClick={handleExecute}