nanoui 0.5.0 → 0.6.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +231 -17
- data/lib/generators/nanoui/component_generator.rb +45 -17
- data/lib/generators/nanoui/templates/css/components/checklist.css +133 -0
- data/lib/generators/nanoui/templates/css/components/code.css +80 -0
- data/lib/generators/nanoui/templates/css/components/copy.css +71 -0
- data/lib/generators/nanoui/templates/css/components/empty.css +68 -0
- data/lib/generators/nanoui/templates/css/components/stat.css +91 -0
- data/lib/generators/nanoui/templates/css/components/table.css +134 -0
- data/lib/generators/nanoui/templates/css/components/timeline.css +141 -0
- data/lib/generators/nanoui/templates/css/components/toast.css +51 -0
- data/lib/generators/nanoui/templates/css/components/upload.css +161 -0
- data/lib/generators/nanoui/templates/js/controllers/copy_controller.js +64 -0
- data/lib/generators/nanoui/templates/js/controllers/data_table_controller.js +75 -0
- data/lib/generators/nanoui/templates/js/controllers/upload_controller.js +137 -0
- data/lib/nanoui/version.rb +1 -1
- metadata +40 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7853589240e9826e1a84eacb068ffd3ec668c5c459081cfe3a8d93fd4231c096
|
|
4
|
+
data.tar.gz: d62fa64e526a04c473f30355e0cde98c5441a7e136bff1813ff08a376af0728f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb21d047b98e632f831fdf5dc96a0001f7cb6222ca566dd60b72a3b23ac5968f15b6b07dc0a4d0b7a7c35410607c4f1defcc1a858ce468a50f165f085e5c0a96
|
|
7
|
+
data.tar.gz: f1710c2cc03164d20663095e0b0acb7fae811498f754c43c23236d02c99e400c6e85f5771168ae56a628d7526c3181049af08886e6957df09e5bc9c9e770f1a7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 (2026-04-17)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Stat** component — dashboard KPI card with label, value, delta, and optional footer (CSS only)
|
|
8
|
+
- **Empty state** component — centered "no data yet" / "get started" layout with icon, title, description, and actions (CSS only)
|
|
9
|
+
- **Timeline** component — vertical activity feed with color-coded markers for retry history, audit logs, and status changes (CSS only)
|
|
10
|
+
- **Checklist** component — onboarding / setup checklist with pending / current / done states and optional progress summary (CSS only)
|
|
11
|
+
- **Copy** component — one-click copy-to-clipboard button with transient success state and `execCommand` fallback (CSS + Stimulus)
|
|
12
|
+
- **Code** component — monospace block for snippets, inline fragments, and terminal-style output (CSS only)
|
|
13
|
+
- **Upload** component — drag-and-drop file input with preview, size/type validation, and accessible fallback (CSS + Stimulus)
|
|
14
|
+
- **Data-table Stimulus controller** — click-to-sort headers (`string` / `number` / `date`), dispatches `nanoui-data-table:sort` for server-side sorting; paired with new CSS for `.nano-table__header--sortable` and `.nano-table-pagination`
|
|
15
|
+
- **Toast action buttons** — `.nano-toast__action` is now a fully styled inline CTA that adopts each variant's accent color
|
|
16
|
+
- New component groups: `dashboard` (stat, empty, timeline, checklist) and `utilities` (copy, code)
|
|
17
|
+
- GitHub Actions workflows for CI (RSpec on Ruby 3.1 / 3.2 / 3.3 + Rubocop) and Release (automated gem publish on `v*.*.*` tags)
|
|
18
|
+
- Rubocop config with `rubocop-rspec` plugin
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Component count: 23 → 30
|
|
23
|
+
- Stimulus controllers: 9 → 12
|
|
24
|
+
- Component generator now maps each component to its controller files via `CONTROLLER_FILES`; installing `table` automatically installs the richer `data_table` controller
|
|
25
|
+
- Gemspec declares `rubocop` and `rubocop-rspec` as development dependencies
|
|
26
|
+
|
|
3
27
|
## 0.5.0 (2026-03-21)
|
|
4
28
|
|
|
5
29
|
### Added
|
data/README.md
CHANGED
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Vanilla CSS + Stimulus component library for Rails. Zero runtime dependencies.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
30 components. Semantic HTML. Accessible by default. No build step.
|
|
6
6
|
|
|
7
|
-
**[Documentation & Live Previews](https://chille1987.github.io/nanoui/)** — Browse all
|
|
7
|
+
**[Documentation & Live Previews](https://chille1987.github.io/nanoui/)** — Browse all 30 components with interactive examples.
|
|
8
8
|
|
|
9
|
-
## What's New in v0.
|
|
9
|
+
## What's New in v0.6.0
|
|
10
10
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
11
|
+
- **7 new components** — Stat, Empty state, Timeline, Checklist, Copy, Code, Upload
|
|
12
|
+
- **Sortable data tables** — `nanoui-data-table` Stimulus controller with client- or server-side sorting + pagination styling
|
|
13
|
+
- **Toast action buttons** — inline CTAs styled to match each toast variant
|
|
14
|
+
- **New component groups** — `dashboard` (stat, empty, timeline, checklist) and `utilities` (copy, code)
|
|
15
|
+
- **CI + release automation** — GitHub Actions for RSpec / Rubocop and tag-driven gem publishing
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
@@ -61,15 +63,18 @@ If NanoUI falls back to system fonts instead of Inter:
|
|
|
61
63
|
With Rails 8 + importmap + `eagerLoadControllersFrom`, controllers auto-register by file name. Rename the controllers with a `nanoui_` prefix:
|
|
62
64
|
|
|
63
65
|
```
|
|
64
|
-
nanoui_dialog_controller.js
|
|
65
|
-
nanoui_dropdown_controller.js
|
|
66
|
-
nanoui_tooltip_controller.js
|
|
67
|
-
nanoui_toast_controller.js
|
|
68
|
-
nanoui_tabs_controller.js
|
|
69
|
-
nanoui_accordion_controller.js
|
|
70
|
-
nanoui_switch_controller.js
|
|
71
|
-
nanoui_navbar_controller.js
|
|
72
|
-
nanoui_sidebar_controller.js
|
|
66
|
+
nanoui_dialog_controller.js → data-controller="nanoui-dialog"
|
|
67
|
+
nanoui_dropdown_controller.js → data-controller="nanoui-dropdown"
|
|
68
|
+
nanoui_tooltip_controller.js → data-controller="nanoui-tooltip"
|
|
69
|
+
nanoui_toast_controller.js → data-controller="nanoui-toast"
|
|
70
|
+
nanoui_tabs_controller.js → data-controller="nanoui-tabs"
|
|
71
|
+
nanoui_accordion_controller.js → data-controller="nanoui-accordion"
|
|
72
|
+
nanoui_switch_controller.js → data-controller="nanoui-switch"
|
|
73
|
+
nanoui_navbar_controller.js → data-controller="nanoui-navbar"
|
|
74
|
+
nanoui_sidebar_controller.js → data-controller="nanoui-sidebar"
|
|
75
|
+
nanoui_copy_controller.js → data-controller="nanoui-copy"
|
|
76
|
+
nanoui_upload_controller.js → data-controller="nanoui-upload"
|
|
77
|
+
nanoui_data_table_controller.js → data-controller="nanoui-data-table"
|
|
73
78
|
```
|
|
74
79
|
|
|
75
80
|
Or register manually:
|
|
@@ -583,6 +588,211 @@ Loading placeholder with shimmer animation.
|
|
|
583
588
|
|
|
584
589
|
---
|
|
585
590
|
|
|
591
|
+
### Dashboard
|
|
592
|
+
|
|
593
|
+
#### Stat
|
|
594
|
+
|
|
595
|
+
Dashboard KPI card with label, value, optional delta, helper text, and footer. Group multiples with `.nano-stat-grid`.
|
|
596
|
+
|
|
597
|
+
```html
|
|
598
|
+
<div class="nano-stat">
|
|
599
|
+
<span class="nano-stat__label">Recovered revenue</span>
|
|
600
|
+
<span class="nano-stat__value">$4,238</span>
|
|
601
|
+
<span class="nano-stat__delta nano-stat__delta--up">12% vs last month</span>
|
|
602
|
+
</div>
|
|
603
|
+
|
|
604
|
+
<div class="nano-stat-grid">
|
|
605
|
+
<div class="nano-stat">…</div>
|
|
606
|
+
<div class="nano-stat nano-stat--compact">…</div>
|
|
607
|
+
</div>
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**Variants:** default, `--elevated`, `--bordered`, `--compact`
|
|
611
|
+
|
|
612
|
+
#### Empty state
|
|
613
|
+
|
|
614
|
+
Centered layout for "no data yet" or "get started" screens.
|
|
615
|
+
|
|
616
|
+
```html
|
|
617
|
+
<div class="nano-empty">
|
|
618
|
+
<div class="nano-empty__icon"><!-- SVG --></div>
|
|
619
|
+
<h3 class="nano-empty__title">No failed payments yet</h3>
|
|
620
|
+
<p class="nano-empty__description">When Stripe reports a failed charge we'll queue retries here.</p>
|
|
621
|
+
<div class="nano-empty__actions">
|
|
622
|
+
<button class="nano-btn nano-btn--primary">Connect Stripe</button>
|
|
623
|
+
</div>
|
|
624
|
+
</div>
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
**Variants:** default, `--bordered` (dashed outline), `--compact`
|
|
628
|
+
|
|
629
|
+
#### Timeline
|
|
630
|
+
|
|
631
|
+
Vertical activity feed with color-coded markers.
|
|
632
|
+
|
|
633
|
+
```html
|
|
634
|
+
<ol class="nano-timeline">
|
|
635
|
+
<li class="nano-timeline__item nano-timeline__item--destructive">
|
|
636
|
+
<span class="nano-timeline__marker"><!-- SVG --></span>
|
|
637
|
+
<span class="nano-timeline__line"></span>
|
|
638
|
+
<div class="nano-timeline__body">
|
|
639
|
+
<div class="nano-timeline__header">
|
|
640
|
+
<span class="nano-timeline__title">Payment failed</span>
|
|
641
|
+
<time class="nano-timeline__time">3 days ago</time>
|
|
642
|
+
</div>
|
|
643
|
+
<span class="nano-timeline__description">Card declined — insufficient funds.</span>
|
|
644
|
+
</div>
|
|
645
|
+
</li>
|
|
646
|
+
</ol>
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
**Item states:** default, `--info`, `--success`, `--warning`, `--destructive`
|
|
650
|
+
|
|
651
|
+
#### Checklist
|
|
652
|
+
|
|
653
|
+
Onboarding / setup checklist with pending, current, and done states. Drive the summary bar with the `--nano-checklist-progress` CSS variable.
|
|
654
|
+
|
|
655
|
+
```html
|
|
656
|
+
<div class="nano-checklist">
|
|
657
|
+
<div class="nano-checklist__summary">
|
|
658
|
+
<span><span class="nano-checklist__count">2 of 4</span> complete</span>
|
|
659
|
+
<span class="nano-checklist__bar" style="--nano-checklist-progress: 50%;">
|
|
660
|
+
<span class="nano-checklist__bar-fill"></span>
|
|
661
|
+
</span>
|
|
662
|
+
</div>
|
|
663
|
+
<div class="nano-checklist__item nano-checklist__item--done">
|
|
664
|
+
<span class="nano-checklist__indicator"><!-- check SVG --></span>
|
|
665
|
+
<div class="nano-checklist__body">
|
|
666
|
+
<span class="nano-checklist__title">Connect Stripe</span>
|
|
667
|
+
</div>
|
|
668
|
+
</div>
|
|
669
|
+
<div class="nano-checklist__item nano-checklist__item--current">
|
|
670
|
+
<span class="nano-checklist__indicator">3</span>
|
|
671
|
+
<div class="nano-checklist__body">
|
|
672
|
+
<span class="nano-checklist__title">Pick an email template</span>
|
|
673
|
+
<a class="nano-checklist__action" href="#">Choose template →</a>
|
|
674
|
+
</div>
|
|
675
|
+
</div>
|
|
676
|
+
</div>
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
### Utilities
|
|
682
|
+
|
|
683
|
+
#### Copy to clipboard
|
|
684
|
+
|
|
685
|
+
Button (or button + value) that copies to clipboard with a transient success state. Uses the Clipboard API with a `document.execCommand` fallback.
|
|
686
|
+
|
|
687
|
+
```html
|
|
688
|
+
<div class="nano-copy" data-controller="nanoui-copy">
|
|
689
|
+
<span class="nano-copy__value" data-nanoui-copy-target="source">
|
|
690
|
+
https://app.example.com/webhooks/stripe
|
|
691
|
+
</span>
|
|
692
|
+
<button class="nano-copy__button"
|
|
693
|
+
data-nanoui-copy-target="button"
|
|
694
|
+
data-action="nanoui-copy#copy"
|
|
695
|
+
type="button">
|
|
696
|
+
<span data-nanoui-copy-target="idle">Copy</span>
|
|
697
|
+
<span data-nanoui-copy-target="copied" hidden>Copied</span>
|
|
698
|
+
</button>
|
|
699
|
+
</div>
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Dispatches `nanoui-copy:copied` with `{ text }` in the event detail.
|
|
703
|
+
|
|
704
|
+
#### Code
|
|
705
|
+
|
|
706
|
+
Monospace block with optional header, inline variant, terminal (dark) variant, and wrap mode.
|
|
707
|
+
|
|
708
|
+
```html
|
|
709
|
+
<div class="nano-code">
|
|
710
|
+
<div class="nano-code__header">
|
|
711
|
+
<span class="nano-code__language">shell</span>
|
|
712
|
+
</div>
|
|
713
|
+
<pre class="nano-code__body"><code>stripe listen --forward-to localhost:3000/webhooks/stripe</code></pre>
|
|
714
|
+
</div>
|
|
715
|
+
|
|
716
|
+
<!-- Inline -->
|
|
717
|
+
<p>Set <code class="nano-code nano-code--inline">STRIPE_WEBHOOK_SECRET</code> before deploying.</p>
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
**Variants:** default, `--inline`, `--terminal`, `--wrap`
|
|
721
|
+
|
|
722
|
+
---
|
|
723
|
+
|
|
724
|
+
### Data table enhancements (built on Table)
|
|
725
|
+
|
|
726
|
+
Add sortable columns and pagination to any existing table. Installing the `table` component also copies `nanoui_data_table_controller.js`.
|
|
727
|
+
|
|
728
|
+
```html
|
|
729
|
+
<div class="nano-table-wrapper" data-controller="nanoui-data-table">
|
|
730
|
+
<table class="nano-table nano-table--hoverable">
|
|
731
|
+
<thead class="nano-table__head">
|
|
732
|
+
<tr>
|
|
733
|
+
<th class="nano-table__header nano-table__header--sortable"
|
|
734
|
+
data-nanoui-data-table-target="header"
|
|
735
|
+
data-sort-key="amount"
|
|
736
|
+
data-sort-type="number"
|
|
737
|
+
data-action="click->nanoui-data-table#sort">
|
|
738
|
+
<button type="button" class="nano-table__sort">Amount</button>
|
|
739
|
+
</th>
|
|
740
|
+
</tr>
|
|
741
|
+
</thead>
|
|
742
|
+
<tbody class="nano-table__body" data-nanoui-data-table-target="body">
|
|
743
|
+
<tr class="nano-table__row" data-nanoui-data-table-target="row">
|
|
744
|
+
<td class="nano-table__cell" data-sort-value="49">$49.00</td>
|
|
745
|
+
</tr>
|
|
746
|
+
</tbody>
|
|
747
|
+
</table>
|
|
748
|
+
</div>
|
|
749
|
+
|
|
750
|
+
<nav class="nano-table-pagination" aria-label="Table pagination">
|
|
751
|
+
<span class="nano-table-pagination__info">Showing <strong>1–10</strong> of <strong>42</strong></span>
|
|
752
|
+
<span class="nano-table-pagination__controls">
|
|
753
|
+
<a class="nano-table-pagination__button" aria-current="page" href="?page=1">1</a>
|
|
754
|
+
<a class="nano-table-pagination__button" href="?page=2">2</a>
|
|
755
|
+
</span>
|
|
756
|
+
</nav>
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
Set `data-nanoui-data-table-server-value="true"` to skip client-side sorting and only dispatch the `nanoui-data-table:sort` event (e.g. for Turbo Frames).
|
|
760
|
+
|
|
761
|
+
Supported `data-sort-type` values: `string` (default), `number`, `date`.
|
|
762
|
+
|
|
763
|
+
---
|
|
764
|
+
|
|
765
|
+
### Forms (extended)
|
|
766
|
+
|
|
767
|
+
#### Upload
|
|
768
|
+
|
|
769
|
+
Drag-and-drop file input with preview, `maxSize` / `accept` validation, and accessible fallback to the native picker. Wraps a hidden `<input type="file">` so standard form submission works unchanged (including `form_with` + Active Storage).
|
|
770
|
+
|
|
771
|
+
```html
|
|
772
|
+
<div class="nano-upload"
|
|
773
|
+
data-controller="nanoui-upload"
|
|
774
|
+
data-nanoui-upload-accept-value="image/*"
|
|
775
|
+
data-nanoui-upload-max-size-value="2097152"
|
|
776
|
+
data-action="dragover->nanoui-upload#onDragover dragleave->nanoui-upload#onDragleave drop->nanoui-upload#onDrop">
|
|
777
|
+
<input type="file" name="logo" class="nano-upload__input"
|
|
778
|
+
data-nanoui-upload-target="input"
|
|
779
|
+
data-action="change->nanoui-upload#onChange">
|
|
780
|
+
<label class="nano-upload__dropzone"
|
|
781
|
+
data-nanoui-upload-target="dropzone"
|
|
782
|
+
data-action="click->nanoui-upload#openPicker"
|
|
783
|
+
tabindex="0">
|
|
784
|
+
<span class="nano-upload__icon"><!-- SVG --></span>
|
|
785
|
+
<span class="nano-upload__prompt"><strong>Click to upload</strong> or drag and drop</span>
|
|
786
|
+
<span class="nano-upload__hint">PNG or JPG up to 2 MB</span>
|
|
787
|
+
</label>
|
|
788
|
+
<!-- preview element is toggled automatically via data-state -->
|
|
789
|
+
</div>
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
Dispatches `nanoui-upload:selected` and `nanoui-upload:removed`.
|
|
793
|
+
|
|
794
|
+
---
|
|
795
|
+
|
|
586
796
|
## Design Tokens
|
|
587
797
|
|
|
588
798
|
Customize your theme by editing the CSS custom properties:
|
|
@@ -614,11 +824,13 @@ All components update automatically, including dark mode.
|
|
|
614
824
|
| Group | Components |
|
|
615
825
|
|---|---|
|
|
616
826
|
| **Essentials** | Button, Input, Label, Card, Badge, Alert |
|
|
617
|
-
| **Forms** | Checkbox, Radio, Switch, Select |
|
|
827
|
+
| **Forms** | Checkbox, Radio, Switch, Select, Upload |
|
|
618
828
|
| **Overlays** | Dialog, Dropdown, Tooltip, Toast |
|
|
619
|
-
| **Data** | Table, Tabs, Accordion, Progress |
|
|
829
|
+
| **Data** | Table (with sortable data-table controller), Tabs, Accordion, Progress |
|
|
620
830
|
| **Navigation** | Navbar, Sidebar, Breadcrumb |
|
|
621
831
|
| **Feedback** | Avatar, Skeleton |
|
|
832
|
+
| **Dashboard** | Stat, Empty state, Timeline, Checklist |
|
|
833
|
+
| **Utilities** | Copy to clipboard, Code block |
|
|
622
834
|
| **Layout** | Container |
|
|
623
835
|
|
|
624
836
|
```bash
|
|
@@ -628,6 +840,8 @@ rails generate nanoui:component --group overlays
|
|
|
628
840
|
rails generate nanoui:component --group data
|
|
629
841
|
rails generate nanoui:component --group navigation
|
|
630
842
|
rails generate nanoui:component --group feedback
|
|
843
|
+
rails generate nanoui:component --group dashboard
|
|
844
|
+
rails generate nanoui:component --group utilities
|
|
631
845
|
rails generate nanoui:component --all
|
|
632
846
|
```
|
|
633
847
|
|
|
@@ -27,6 +27,13 @@ module Nanoui
|
|
|
27
27
|
breadcrumb
|
|
28
28
|
avatar
|
|
29
29
|
skeleton
|
|
30
|
+
stat
|
|
31
|
+
empty
|
|
32
|
+
timeline
|
|
33
|
+
checklist
|
|
34
|
+
copy
|
|
35
|
+
code
|
|
36
|
+
upload
|
|
30
37
|
container
|
|
31
38
|
].freeze
|
|
32
39
|
|
|
@@ -38,25 +45,45 @@ module Nanoui
|
|
|
38
45
|
class_option :all, type: :boolean, default: false, desc: "Install all components"
|
|
39
46
|
|
|
40
47
|
GROUPS = {
|
|
41
|
-
"essentials"
|
|
42
|
-
"forms"
|
|
43
|
-
"overlays"
|
|
44
|
-
"data"
|
|
45
|
-
"navigation"
|
|
46
|
-
"feedback"
|
|
47
|
-
"
|
|
48
|
+
"essentials" => %w[button input label card badge alert],
|
|
49
|
+
"forms" => %w[button input label checkbox radio switch select badge alert upload],
|
|
50
|
+
"overlays" => %w[dialog dropdown tooltip toast],
|
|
51
|
+
"data" => %w[table tabs accordion progress],
|
|
52
|
+
"navigation" => %w[navbar sidebar breadcrumb],
|
|
53
|
+
"feedback" => %w[avatar skeleton],
|
|
54
|
+
"dashboard" => %w[stat empty timeline checklist],
|
|
55
|
+
"utilities" => %w[copy code],
|
|
56
|
+
"layout" => %w[container],
|
|
48
57
|
}.freeze
|
|
49
58
|
|
|
50
|
-
|
|
59
|
+
# Maps a component name to the Stimulus controller files it installs.
|
|
60
|
+
# Most components ship with a same-named controller, but `table` ships
|
|
61
|
+
# with the richer `data_table` controller for sort + pagination behavior.
|
|
62
|
+
CONTROLLER_FILES = {
|
|
63
|
+
"dialog" => %w[dialog],
|
|
64
|
+
"dropdown" => %w[dropdown],
|
|
65
|
+
"tooltip" => %w[tooltip],
|
|
66
|
+
"toast" => %w[toast],
|
|
67
|
+
"tabs" => %w[tabs],
|
|
68
|
+
"accordion" => %w[accordion],
|
|
69
|
+
"switch" => %w[switch],
|
|
70
|
+
"navbar" => %w[navbar],
|
|
71
|
+
"sidebar" => %w[sidebar],
|
|
72
|
+
"copy" => %w[copy],
|
|
73
|
+
"upload" => %w[upload],
|
|
74
|
+
"table" => %w[data_table],
|
|
75
|
+
}.freeze
|
|
76
|
+
|
|
77
|
+
STIMULUS_COMPONENTS = CONTROLLER_FILES.keys.freeze
|
|
51
78
|
|
|
52
79
|
def resolve_components
|
|
53
80
|
@resolved = if options[:all]
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
81
|
+
GROUPS.values.flatten.uniq
|
|
82
|
+
elsif options[:group]
|
|
83
|
+
GROUPS.fetch(options[:group]) { abort "Unknown group: #{options[:group]}" }
|
|
84
|
+
else
|
|
85
|
+
components
|
|
86
|
+
end
|
|
60
87
|
|
|
61
88
|
abort "Specify components, --group, or --all" if @resolved.empty?
|
|
62
89
|
end
|
|
@@ -71,9 +98,10 @@ module Nanoui
|
|
|
71
98
|
|
|
72
99
|
def copy_stimulus_controllers
|
|
73
100
|
@resolved.each do |name|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
101
|
+
CONTROLLER_FILES.fetch(name, []).each do |controller|
|
|
102
|
+
copy_file "js/controllers/#{controller}_controller.js",
|
|
103
|
+
"app/javascript/controllers/nanoui_#{controller}_controller.js"
|
|
104
|
+
end
|
|
77
105
|
end
|
|
78
106
|
end
|
|
79
107
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
.nano-checklist {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--space-2);
|
|
5
|
+
|
|
6
|
+
.nano-checklist__summary {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: space-between;
|
|
10
|
+
gap: var(--space-3);
|
|
11
|
+
padding: var(--space-3) var(--space-4);
|
|
12
|
+
background-color: hsl(var(--color-muted));
|
|
13
|
+
border-radius: var(--radius-md);
|
|
14
|
+
font-size: var(--text-sm);
|
|
15
|
+
color: hsl(var(--color-muted-foreground));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.nano-checklist__count {
|
|
19
|
+
font-weight: var(--font-semibold);
|
|
20
|
+
color: hsl(var(--color-foreground));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.nano-checklist__bar {
|
|
24
|
+
flex: 1;
|
|
25
|
+
height: 0.375rem;
|
|
26
|
+
background-color: hsl(var(--color-background));
|
|
27
|
+
border-radius: var(--radius-full);
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.nano-checklist__bar-fill {
|
|
32
|
+
display: block;
|
|
33
|
+
height: 100%;
|
|
34
|
+
width: var(--nano-checklist-progress, 0%);
|
|
35
|
+
background-color: hsl(var(--color-primary));
|
|
36
|
+
border-radius: var(--radius-full);
|
|
37
|
+
transition: width var(--duration-slow) var(--ease-default);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.nano-checklist__item {
|
|
41
|
+
display: flex;
|
|
42
|
+
gap: var(--space-3);
|
|
43
|
+
padding: var(--space-4);
|
|
44
|
+
border: 1px solid hsl(var(--color-border));
|
|
45
|
+
border-radius: var(--radius-md);
|
|
46
|
+
background-color: hsl(var(--color-card));
|
|
47
|
+
transition: border-color var(--duration-fast) var(--ease-default),
|
|
48
|
+
background-color var(--duration-fast) var(--ease-default);
|
|
49
|
+
|
|
50
|
+
&--current {
|
|
51
|
+
border-color: hsl(var(--color-primary) / 0.5);
|
|
52
|
+
background-color: hsl(var(--color-primary) / 0.04);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&--done {
|
|
56
|
+
border-color: hsl(var(--color-success) / 0.4);
|
|
57
|
+
background-color: hsl(var(--color-success) / 0.04);
|
|
58
|
+
|
|
59
|
+
.nano-checklist__title {
|
|
60
|
+
color: hsl(var(--color-muted-foreground));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.nano-checklist__indicator {
|
|
66
|
+
display: inline-flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
justify-content: center;
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
width: 1.75rem;
|
|
71
|
+
height: 1.75rem;
|
|
72
|
+
margin-top: var(--space-0-5);
|
|
73
|
+
border-radius: var(--radius-full);
|
|
74
|
+
border: 2px solid hsl(var(--color-border));
|
|
75
|
+
background-color: hsl(var(--color-background));
|
|
76
|
+
font-size: var(--text-xs);
|
|
77
|
+
font-weight: var(--font-semibold);
|
|
78
|
+
color: hsl(var(--color-muted-foreground));
|
|
79
|
+
|
|
80
|
+
svg {
|
|
81
|
+
width: 0.875rem;
|
|
82
|
+
height: 0.875rem;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.nano-checklist__item--current .nano-checklist__indicator {
|
|
87
|
+
border-color: hsl(var(--color-primary));
|
|
88
|
+
color: hsl(var(--color-primary));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.nano-checklist__item--done .nano-checklist__indicator {
|
|
92
|
+
border-color: hsl(var(--color-success));
|
|
93
|
+
background-color: hsl(var(--color-success));
|
|
94
|
+
color: hsl(var(--color-success-foreground));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.nano-checklist__body {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
gap: var(--space-1);
|
|
101
|
+
flex: 1;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.nano-checklist__title {
|
|
106
|
+
font-size: var(--text-sm);
|
|
107
|
+
font-weight: var(--font-semibold);
|
|
108
|
+
color: hsl(var(--color-foreground));
|
|
109
|
+
line-height: var(--leading-tight);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.nano-checklist__description {
|
|
113
|
+
font-size: var(--text-sm);
|
|
114
|
+
color: hsl(var(--color-muted-foreground));
|
|
115
|
+
line-height: var(--leading-normal);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.nano-checklist__action {
|
|
119
|
+
display: inline-flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
gap: var(--space-1);
|
|
122
|
+
margin-top: var(--space-1);
|
|
123
|
+
font-size: var(--text-sm);
|
|
124
|
+
font-weight: var(--font-medium);
|
|
125
|
+
color: hsl(var(--color-primary));
|
|
126
|
+
text-decoration: none;
|
|
127
|
+
align-self: flex-start;
|
|
128
|
+
|
|
129
|
+
&:hover {
|
|
130
|
+
text-decoration: underline;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
.nano-code {
|
|
2
|
+
position: relative;
|
|
3
|
+
background-color: hsl(var(--color-muted));
|
|
4
|
+
border: 1px solid hsl(var(--color-border));
|
|
5
|
+
border-radius: var(--radius-md);
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
|
|
8
|
+
.nano-code__header {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: space-between;
|
|
12
|
+
gap: var(--space-2);
|
|
13
|
+
padding: var(--space-2) var(--space-3);
|
|
14
|
+
background-color: hsl(var(--color-background));
|
|
15
|
+
border-bottom: 1px solid hsl(var(--color-border));
|
|
16
|
+
font-size: var(--text-xs);
|
|
17
|
+
color: hsl(var(--color-muted-foreground));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.nano-code__language {
|
|
21
|
+
font-family: var(--font-mono);
|
|
22
|
+
font-weight: var(--font-semibold);
|
|
23
|
+
text-transform: uppercase;
|
|
24
|
+
letter-spacing: var(--tracking-wide);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.nano-code__actions {
|
|
28
|
+
display: inline-flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
gap: var(--space-1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.nano-code__body {
|
|
34
|
+
display: block;
|
|
35
|
+
margin: 0;
|
|
36
|
+
padding: var(--space-4);
|
|
37
|
+
font-family: var(--font-mono);
|
|
38
|
+
font-size: var(--text-sm);
|
|
39
|
+
line-height: var(--leading-relaxed);
|
|
40
|
+
color: hsl(var(--color-foreground));
|
|
41
|
+
overflow-x: auto;
|
|
42
|
+
white-space: pre;
|
|
43
|
+
tab-size: 2;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&--inline {
|
|
47
|
+
display: inline-block;
|
|
48
|
+
padding: var(--space-0-5) var(--space-1-5);
|
|
49
|
+
margin: 0;
|
|
50
|
+
background-color: hsl(var(--color-muted));
|
|
51
|
+
border: 1px solid hsl(var(--color-border));
|
|
52
|
+
border-radius: var(--radius-sm);
|
|
53
|
+
font-family: var(--font-mono);
|
|
54
|
+
font-size: 0.9em;
|
|
55
|
+
color: hsl(var(--color-foreground));
|
|
56
|
+
overflow: visible;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&--terminal {
|
|
60
|
+
background-color: hsl(var(--color-foreground));
|
|
61
|
+
border-color: hsl(var(--color-foreground));
|
|
62
|
+
|
|
63
|
+
.nano-code__header {
|
|
64
|
+
background-color: hsl(var(--color-foreground) / 0.9);
|
|
65
|
+
border-bottom-color: hsl(var(--color-muted-foreground) / 0.3);
|
|
66
|
+
color: hsl(var(--color-background) / 0.7);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.nano-code__body {
|
|
70
|
+
color: hsl(var(--color-background));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&--wrap {
|
|
75
|
+
.nano-code__body {
|
|
76
|
+
white-space: pre-wrap;
|
|
77
|
+
word-break: break-word;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|