nanoui 0.4.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -0
  3. data/README.md +353 -15
  4. data/lib/generators/nanoui/component_generator.rb +49 -14
  5. data/lib/generators/nanoui/templates/css/components/accordion.css +58 -58
  6. data/lib/generators/nanoui/templates/css/components/alert.css +56 -56
  7. data/lib/generators/nanoui/templates/css/components/avatar.css +88 -0
  8. data/lib/generators/nanoui/templates/css/components/badge.css +30 -30
  9. data/lib/generators/nanoui/templates/css/components/breadcrumb.css +54 -0
  10. data/lib/generators/nanoui/templates/css/components/button.css +136 -115
  11. data/lib/generators/nanoui/templates/css/components/card.css +35 -35
  12. data/lib/generators/nanoui/templates/css/components/checkbox.css +74 -72
  13. data/lib/generators/nanoui/templates/css/components/checklist.css +133 -0
  14. data/lib/generators/nanoui/templates/css/components/code.css +80 -0
  15. data/lib/generators/nanoui/templates/css/components/copy.css +71 -0
  16. data/lib/generators/nanoui/templates/css/components/dialog.css +103 -103
  17. data/lib/generators/nanoui/templates/css/components/dropdown.css +71 -71
  18. data/lib/generators/nanoui/templates/css/components/empty.css +68 -0
  19. data/lib/generators/nanoui/templates/css/components/input.css +72 -83
  20. data/lib/generators/nanoui/templates/css/components/label.css +11 -5
  21. data/lib/generators/nanoui/templates/css/components/navbar.css +128 -0
  22. data/lib/generators/nanoui/templates/css/components/progress.css +53 -47
  23. data/lib/generators/nanoui/templates/css/components/radio.css +73 -71
  24. data/lib/generators/nanoui/templates/css/components/select.css +49 -22
  25. data/lib/generators/nanoui/templates/css/components/sidebar.css +145 -0
  26. data/lib/generators/nanoui/templates/css/components/skeleton.css +49 -0
  27. data/lib/generators/nanoui/templates/css/components/stat.css +91 -0
  28. data/lib/generators/nanoui/templates/css/components/switch.css +25 -25
  29. data/lib/generators/nanoui/templates/css/components/table.css +169 -29
  30. data/lib/generators/nanoui/templates/css/components/tabs.css +20 -20
  31. data/lib/generators/nanoui/templates/css/components/timeline.css +141 -0
  32. data/lib/generators/nanoui/templates/css/components/toast.css +119 -68
  33. data/lib/generators/nanoui/templates/css/components/tooltip.css +79 -72
  34. data/lib/generators/nanoui/templates/css/components/upload.css +161 -0
  35. data/lib/generators/nanoui/templates/css/layout/container.css +6 -6
  36. data/lib/generators/nanoui/templates/js/controllers/copy_controller.js +64 -0
  37. data/lib/generators/nanoui/templates/js/controllers/data_table_controller.js +75 -0
  38. data/lib/generators/nanoui/templates/js/controllers/navbar_controller.js +70 -0
  39. data/lib/generators/nanoui/templates/js/controllers/sidebar_controller.js +27 -0
  40. data/lib/generators/nanoui/templates/js/controllers/upload_controller.js +137 -0
  41. data/lib/nanoui/version.rb +1 -1
  42. metadata +47 -2
@@ -5,91 +5,93 @@
5
5
  display: flex;
6
6
  flex-direction: column;
7
7
  gap: var(--space-2);
8
- }
9
8
 
10
- .nano-radio-group__legend {
11
- font-size: var(--text-sm);
12
- font-weight: var(--font-medium);
13
- line-height: var(--leading-none);
14
- color: hsl(var(--color-foreground));
15
- margin-bottom: var(--space-2);
9
+ .nano-radio-group__legend {
10
+ font-size: var(--text-sm);
11
+ font-weight: var(--font-medium);
12
+ line-height: var(--leading-none);
13
+ color: hsl(var(--color-foreground));
14
+ margin-bottom: var(--space-2);
15
+ }
16
16
  }
17
17
 
18
18
  .nano-radio {
19
19
  position: relative;
20
20
  display: inline-flex;
21
- }
22
21
 
23
- .nano-radio__input {
24
- position: absolute;
25
- width: 1px;
26
- height: 1px;
27
- padding: 0;
28
- margin: -1px;
29
- overflow: hidden;
30
- clip: rect(0, 0, 0, 0);
31
- white-space: nowrap;
32
- border-width: 0;
33
- }
22
+ .nano-radio__input {
23
+ position: absolute;
24
+ width: 1px;
25
+ height: 1px;
26
+ padding: 0;
27
+ margin: -1px;
28
+ overflow: hidden;
29
+ clip: rect(0, 0, 0, 0);
30
+ white-space: nowrap;
31
+ border-width: 0;
34
32
 
35
- .nano-radio__label {
36
- display: inline-flex;
37
- align-items: center;
38
- gap: var(--space-2);
39
- cursor: pointer;
40
- user-select: none;
41
- font-size: var(--text-sm);
42
- line-height: var(--leading-normal);
43
- color: hsl(var(--color-foreground));
44
- }
33
+ &:checked + .nano-radio__label::before {
34
+ border-color: hsl(var(--color-primary));
35
+ }
45
36
 
46
- .nano-radio__label::before {
47
- content: "";
48
- display: block;
49
- width: 1.125rem;
50
- height: 1.125rem;
51
- flex-shrink: 0;
52
- border: 1.5px solid hsl(var(--color-input));
53
- border-radius: var(--radius-full);
54
- background-color: hsl(var(--color-background));
55
- transition: border-color var(--duration-fast) var(--ease-default);
56
- }
37
+ &:checked + .nano-radio__label::after {
38
+ transform: translateY(-50%) scale(1);
39
+ }
57
40
 
58
- .nano-radio__label::after {
59
- content: "";
60
- position: absolute;
61
- left: 0.28125rem;
62
- top: 50%;
63
- transform: translateY(-50%) scale(0);
64
- width: 0.5625rem;
65
- height: 0.5625rem;
66
- border-radius: var(--radius-full);
67
- background-color: hsl(var(--color-primary));
68
- transition: transform var(--duration-fast) var(--ease-default);
69
- }
41
+ &:focus-visible + .nano-radio__label::before {
42
+ outline: 2px solid hsl(var(--color-ring));
43
+ outline-offset: 2px;
44
+ }
70
45
 
71
- .nano-radio__input:checked + .nano-radio__label::before {
72
- border-color: hsl(var(--color-primary));
73
- }
46
+ &:disabled + .nano-radio__label {
47
+ opacity: 0.5;
48
+ cursor: not-allowed;
49
+ }
50
+ }
74
51
 
75
- .nano-radio__input:checked + .nano-radio__label::after {
76
- transform: translateY(-50%) scale(1);
77
- }
52
+ .nano-radio__label {
53
+ display: inline-flex;
54
+ align-items: center;
55
+ gap: var(--space-2);
56
+ cursor: pointer;
57
+ user-select: none;
58
+ font-size: var(--text-sm);
59
+ line-height: var(--leading-normal);
60
+ color: hsl(var(--color-foreground));
78
61
 
79
- .nano-radio__input:focus-visible + .nano-radio__label::before {
80
- outline: 2px solid hsl(var(--color-ring));
81
- outline-offset: 2px;
82
- }
62
+ &::before {
63
+ content: "";
64
+ display: block;
65
+ width: 1.125rem;
66
+ height: 1.125rem;
67
+ flex-shrink: 0;
68
+ border: 1.5px solid hsl(var(--color-input));
69
+ border-radius: var(--radius-full);
70
+ background-color: hsl(var(--color-background));
71
+ transition: border-color var(--duration-fast) var(--ease-default);
72
+ }
83
73
 
84
- .nano-radio__input:disabled + .nano-radio__label {
85
- opacity: 0.5;
86
- cursor: not-allowed;
87
- }
74
+ &::after {
75
+ content: "";
76
+ position: absolute;
77
+ left: 0.28125rem;
78
+ top: 50%;
79
+ transform: translateY(-50%) scale(0);
80
+ width: 0.5625rem;
81
+ height: 0.5625rem;
82
+ border-radius: var(--radius-full);
83
+ background-color: hsl(var(--color-primary));
84
+ transition: transform var(--duration-fast) var(--ease-default);
85
+ }
86
+ }
88
87
 
89
- .nano-radio--error .nano-radio__label::before {
90
- border-color: hsl(var(--color-destructive));
91
- }
88
+ &--error {
89
+ .nano-radio__label::before {
90
+ border-color: hsl(var(--color-destructive));
91
+ }
92
92
 
93
- .nano-radio--error .nano-radio__input:checked + .nano-radio__label::after {
94
- background-color: hsl(var(--color-destructive));
93
+ .nano-radio__input:checked + .nano-radio__label::after {
94
+ background-color: hsl(var(--color-destructive));
95
+ }
96
+ }
95
97
  }
@@ -1,5 +1,4 @@
1
- .nano-select,
2
- select {
1
+ .nano-select {
3
2
  display: block;
4
3
  width: 100%;
5
4
  padding: var(--space-2) var(--space-8) var(--space-2) var(--space-3);
@@ -17,31 +16,59 @@ select {
17
16
  background-size: 1rem;
18
17
  transition: border-color var(--duration-fast) var(--ease-default),
19
18
  box-shadow var(--duration-fast) var(--ease-default);
20
- }
21
19
 
22
- .nano-select:focus,
23
- select:focus {
24
- border-color: hsl(var(--color-input-focus));
25
- outline: 2px solid hsl(var(--color-input-focus));
26
- outline-offset: -1px;
27
- }
20
+ &:focus {
21
+ border-color: hsl(var(--color-input-focus));
22
+ outline: 2px solid hsl(var(--color-input-focus));
23
+ outline-offset: -1px;
24
+ }
28
25
 
29
- .nano-select:disabled,
30
- select:disabled {
31
- opacity: 0.5;
32
- cursor: not-allowed;
33
- }
26
+ &:disabled {
27
+ opacity: 0.5;
28
+ cursor: not-allowed;
29
+ }
34
30
 
35
- .nano-select--error,
36
- .nano-select[aria-invalid="true"] {
37
- border-color: hsl(var(--color-destructive));
31
+ &--error,
32
+ &[aria-invalid="true"] {
33
+ border-color: hsl(var(--color-destructive));
34
+
35
+ &:focus {
36
+ border-color: hsl(var(--color-destructive));
37
+ outline: 2px solid hsl(var(--color-destructive));
38
+ outline-offset: -1px;
39
+ }
40
+ }
38
41
  }
39
42
 
40
- .nano-select--error:focus,
41
- .nano-select[aria-invalid="true"]:focus {
42
- border-color: hsl(var(--color-destructive));
43
- outline: 2px solid hsl(var(--color-destructive));
44
- outline-offset: -1px;
43
+ select {
44
+ display: block;
45
+ width: 100%;
46
+ padding: var(--space-2) var(--space-8) var(--space-2) var(--space-3);
47
+ font-size: var(--text-sm);
48
+ font-family: var(--font-sans);
49
+ line-height: var(--leading-normal);
50
+ color: hsl(var(--color-foreground));
51
+ background-color: hsl(var(--color-background));
52
+ border: 1px solid hsl(var(--color-input));
53
+ border-radius: var(--radius-md);
54
+ appearance: none;
55
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
56
+ background-repeat: no-repeat;
57
+ background-position: right var(--space-2) center;
58
+ background-size: 1rem;
59
+ transition: border-color var(--duration-fast) var(--ease-default),
60
+ box-shadow var(--duration-fast) var(--ease-default);
61
+
62
+ &:focus {
63
+ border-color: hsl(var(--color-input-focus));
64
+ outline: 2px solid hsl(var(--color-input-focus));
65
+ outline-offset: -1px;
66
+ }
67
+
68
+ &:disabled {
69
+ opacity: 0.5;
70
+ cursor: not-allowed;
71
+ }
45
72
  }
46
73
 
47
74
  .dark .nano-select,
@@ -0,0 +1,145 @@
1
+ .nano-sidebar {
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 260px;
5
+ height: 100%;
6
+ background-color: hsl(var(--color-background));
7
+ border-right: 1px solid hsl(var(--color-border));
8
+ transition: width var(--duration-normal) var(--ease-default);
9
+ overflow: hidden;
10
+
11
+ &--collapsed {
12
+ width: 60px;
13
+
14
+ .nano-sidebar__header {
15
+ padding: var(--space-4) var(--space-2);
16
+ }
17
+
18
+ .nano-sidebar__item {
19
+ justify-content: center;
20
+ padding: var(--space-2);
21
+
22
+ span {
23
+ display: none;
24
+ }
25
+ }
26
+
27
+ .nano-sidebar__group-label {
28
+ display: none;
29
+ }
30
+
31
+ .nano-sidebar__nav {
32
+ padding: var(--space-2);
33
+ }
34
+
35
+ .nano-sidebar__footer {
36
+ padding: var(--space-4) var(--space-2);
37
+ }
38
+ }
39
+
40
+ .nano-sidebar__header {
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: space-between;
44
+ padding: var(--space-4) var(--space-4);
45
+ border-bottom: 1px solid hsl(var(--color-border));
46
+ }
47
+
48
+ .nano-sidebar__nav {
49
+ display: flex;
50
+ flex-direction: column;
51
+ gap: var(--space-1);
52
+ padding: var(--space-2) var(--space-3);
53
+ flex: 1;
54
+ overflow-y: auto;
55
+ }
56
+
57
+ .nano-sidebar__item {
58
+ display: flex;
59
+ flex-direction: row;
60
+ align-items: center;
61
+ gap: var(--space-3);
62
+ padding: var(--space-2) var(--space-3);
63
+ border-radius: var(--radius-md);
64
+ font-size: var(--text-sm);
65
+ font-weight: var(--font-medium);
66
+ color: hsl(var(--color-muted-foreground));
67
+ text-decoration: none;
68
+ cursor: pointer;
69
+ transition: color var(--duration-fast) var(--ease-default),
70
+ background-color var(--duration-fast) var(--ease-default);
71
+
72
+ &:hover {
73
+ color: hsl(var(--color-foreground));
74
+ background-color: hsl(var(--color-muted) / 0.5);
75
+ }
76
+
77
+ &:focus-visible {
78
+ outline: 2px solid hsl(var(--color-ring));
79
+ outline-offset: -2px;
80
+ }
81
+
82
+ &--active {
83
+ color: hsl(var(--color-foreground));
84
+ background-color: hsl(var(--color-muted));
85
+ }
86
+
87
+ svg {
88
+ width: 1rem;
89
+ height: 1rem;
90
+ flex-shrink: 0;
91
+ }
92
+ }
93
+
94
+ .nano-sidebar__group {
95
+ display: flex;
96
+ flex-direction: column;
97
+ gap: var(--space-1);
98
+ }
99
+
100
+ .nano-sidebar__group-label {
101
+ padding: var(--space-2) var(--space-3);
102
+ font-size: var(--text-xs);
103
+ font-weight: var(--font-semibold);
104
+ text-transform: uppercase;
105
+ letter-spacing: var(--tracking-wide);
106
+ color: hsl(var(--color-muted-foreground));
107
+ }
108
+
109
+ .nano-sidebar__toggle {
110
+ display: inline-flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ width: 1.5rem;
114
+ height: 1.5rem;
115
+ padding: 0;
116
+ border: none;
117
+ border-radius: var(--radius-sm);
118
+ background-color: transparent;
119
+ color: hsl(var(--color-muted-foreground));
120
+ cursor: pointer;
121
+ transition: color var(--duration-fast) var(--ease-default),
122
+ background-color var(--duration-fast) var(--ease-default);
123
+
124
+ &:hover {
125
+ color: hsl(var(--color-foreground));
126
+ background-color: hsl(var(--color-muted));
127
+ }
128
+
129
+ &:focus-visible {
130
+ outline: 2px solid hsl(var(--color-ring));
131
+ outline-offset: 2px;
132
+ }
133
+
134
+ svg {
135
+ width: 1rem;
136
+ height: 1rem;
137
+ }
138
+ }
139
+
140
+ .nano-sidebar__footer {
141
+ margin-top: auto;
142
+ padding: var(--space-4);
143
+ border-top: 1px solid hsl(var(--color-border));
144
+ }
145
+ }
@@ -0,0 +1,49 @@
1
+ @keyframes nano-shimmer {
2
+ from {
3
+ transform: translateX(-100%);
4
+ }
5
+
6
+ to {
7
+ transform: translateX(100%);
8
+ }
9
+ }
10
+
11
+ .nano-skeleton {
12
+ display: block;
13
+ background-color: hsl(var(--color-muted));
14
+ border-radius: var(--radius-md);
15
+ height: 1rem;
16
+ position: relative;
17
+ overflow: hidden;
18
+
19
+ &::after {
20
+ content: "";
21
+ position: absolute;
22
+ inset: 0;
23
+ background: linear-gradient(
24
+ 90deg,
25
+ transparent,
26
+ hsl(var(--color-background) / 0.4),
27
+ transparent
28
+ );
29
+ animation: nano-shimmer 1.5s var(--ease-default) infinite;
30
+ }
31
+
32
+ &--text {
33
+ height: 0.75rem;
34
+ width: 100%;
35
+ margin-bottom: var(--space-2);
36
+ }
37
+
38
+ &--circle {
39
+ border-radius: var(--radius-full);
40
+ width: 40px;
41
+ aspect-ratio: 1;
42
+ height: auto;
43
+ }
44
+
45
+ &--card {
46
+ height: 200px;
47
+ border-radius: var(--radius-lg);
48
+ }
49
+ }
@@ -0,0 +1,91 @@
1
+ .nano-stat {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--space-1-5);
5
+ padding: var(--space-6);
6
+ background-color: hsl(var(--color-card));
7
+ color: hsl(var(--color-card-foreground));
8
+ border: 1px solid hsl(var(--color-border));
9
+ border-radius: var(--radius-lg);
10
+ box-shadow: var(--shadow-sm);
11
+
12
+ .nano-stat__label {
13
+ font-size: var(--text-sm);
14
+ font-weight: var(--font-medium);
15
+ color: hsl(var(--color-muted-foreground));
16
+ line-height: var(--leading-none);
17
+ }
18
+
19
+ .nano-stat__value {
20
+ font-size: var(--text-3xl);
21
+ font-weight: var(--font-bold);
22
+ line-height: var(--leading-tight);
23
+ letter-spacing: var(--tracking-tight);
24
+ color: hsl(var(--color-foreground));
25
+ }
26
+
27
+ .nano-stat__delta {
28
+ display: inline-flex;
29
+ align-items: center;
30
+ gap: var(--space-1);
31
+ font-size: var(--text-xs);
32
+ font-weight: var(--font-medium);
33
+ color: hsl(var(--color-muted-foreground));
34
+
35
+ svg {
36
+ width: 0.875rem;
37
+ height: 0.875rem;
38
+ }
39
+
40
+ &--up {
41
+ color: hsl(var(--color-success));
42
+ }
43
+
44
+ &--down {
45
+ color: hsl(var(--color-destructive));
46
+ }
47
+ }
48
+
49
+ .nano-stat__help {
50
+ font-size: var(--text-xs);
51
+ color: hsl(var(--color-muted-foreground));
52
+ line-height: var(--leading-normal);
53
+ }
54
+
55
+ .nano-stat__footer {
56
+ margin-top: var(--space-2);
57
+ padding-top: var(--space-3);
58
+ border-top: 1px solid hsl(var(--color-border));
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: space-between;
62
+ gap: var(--space-2);
63
+ font-size: var(--text-xs);
64
+ color: hsl(var(--color-muted-foreground));
65
+ }
66
+
67
+ &--elevated {
68
+ box-shadow: var(--shadow-md);
69
+ border: none;
70
+ }
71
+
72
+ &--bordered {
73
+ border: 2px solid hsl(var(--color-border));
74
+ box-shadow: none;
75
+ }
76
+
77
+ &--compact {
78
+ padding: var(--space-4);
79
+ gap: var(--space-1);
80
+
81
+ .nano-stat__value {
82
+ font-size: var(--text-2xl);
83
+ }
84
+ }
85
+ }
86
+
87
+ .nano-stat-grid {
88
+ display: grid;
89
+ gap: var(--space-4);
90
+ grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
91
+ }
@@ -9,34 +9,34 @@
9
9
  background-color: hsl(var(--color-input));
10
10
  cursor: pointer;
11
11
  transition: background-color var(--duration-fast) var(--ease-default);
12
- }
13
12
 
14
- .nano-switch[aria-checked="true"] {
15
- background-color: hsl(var(--color-primary));
16
- }
13
+ &[aria-checked="true"] {
14
+ background-color: hsl(var(--color-primary));
17
15
 
18
- .nano-switch[aria-checked="true"] .nano-switch__thumb {
19
- transform: translateX(1.25rem);
20
- }
16
+ .nano-switch__thumb {
17
+ transform: translateX(1.25rem);
18
+ }
19
+ }
21
20
 
22
- .nano-switch__thumb {
23
- display: block;
24
- width: 1rem;
25
- height: 1rem;
26
- border-radius: var(--radius-full);
27
- background-color: hsl(var(--color-background));
28
- box-shadow: var(--shadow-sm);
29
- transition: transform var(--duration-fast) var(--ease-default);
30
- pointer-events: none;
31
- }
21
+ .nano-switch__thumb {
22
+ display: block;
23
+ width: 1rem;
24
+ height: 1rem;
25
+ border-radius: var(--radius-full);
26
+ background-color: hsl(var(--color-background));
27
+ box-shadow: var(--shadow-sm);
28
+ transition: transform var(--duration-fast) var(--ease-default);
29
+ pointer-events: none;
30
+ }
32
31
 
33
- .nano-switch:focus-visible {
34
- outline: 2px solid hsl(var(--color-ring));
35
- outline-offset: 2px;
36
- }
32
+ &:focus-visible {
33
+ outline: 2px solid hsl(var(--color-ring));
34
+ outline-offset: 2px;
35
+ }
37
36
 
38
- .nano-switch:disabled,
39
- .nano-switch[aria-disabled="true"] {
40
- opacity: 0.5;
41
- pointer-events: none;
37
+ &:disabled,
38
+ &[aria-disabled="true"] {
39
+ opacity: 0.5;
40
+ pointer-events: none;
41
+ }
42
42
  }