shadcn-ui 0.0.4 → 0.0.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.
- checksums.yaml +4 -4
- data/Procfile.dev +0 -1
- data/README.md +13 -314
- data/app/assets/stylesheets/shadcn.css +8 -0
- data/app/controllers/components_controller.rb +1 -0
- data/app/controllers/users_controller.rb +19 -0
- data/app/form_builders/shadcn_form_builder.rb +43 -0
- data/app/helpers/components/alert_dialog_helper.rb +19 -3
- data/app/helpers/components/forms_helper.rb +13 -0
- data/app/helpers/components/input_helper.rb +14 -4
- data/app/helpers/components/toast_helper.rb +4 -1
- data/app/helpers/components_helper.rb +4 -0
- data/app/javascript/controllers/ui/dialog_controller.js +1 -1
- data/app/javascript/controllers/ui/dropdown_controller.js +1 -1
- data/app/javascript/controllers/ui/hover-card_controller.js +2 -3
- data/app/javascript/controllers/ui/popover_controller.js +2 -2
- data/app/javascript/controllers/ui/sheet_controller.js +1 -1
- data/app/javascript/controllers/ui/toast_controller.js +50 -23
- data/app/javascript/controllers/ui/transition_controller.js +1 -1
- data/app/models/user.rb +9 -0
- data/app/views/components/ui/_alert_dialog.html.erb +10 -6
- data/app/views/components/ui/_dialog.html.erb +0 -26
- data/app/views/components/ui/_filter.html.erb +1 -1
- data/app/views/components/ui/_input.html.erb +3 -0
- data/app/views/components/ui/_label.html.erb +1 -1
- data/app/views/components/ui/_toast.html.erb +8 -4
- data/app/views/documentation/installation.html.md +1 -1
- data/app/views/examples/components/alert-dialog/_usage.html.erb +24 -0
- data/app/views/examples/components/alert-dialog/code/_preview.erb +22 -6
- data/app/views/examples/components/alert-dialog/code/_usage.erb +13 -0
- data/app/views/examples/components/alert-dialog.html.erb +7 -1
- data/app/views/examples/components/card/code/_usage.erb +1 -1
- data/app/views/examples/components/forms/_usage.html.erb +28 -0
- data/app/views/examples/components/forms/code/_preview.erb +13 -0
- data/app/views/examples/components/forms/code/_usage.erb +6 -0
- data/app/views/examples/components/forms.html.erb +23 -0
- data/app/views/examples/components/toast/code/_destructive.erb +6 -0
- data/app/views/examples/components/toast/code/_preview.erb +4 -3
- data/app/views/examples/components/toast.html.erb +17 -2
- data/app/views/layouts/shared/_components.html.erb +3 -3
- data/config/routes.rb +3 -0
- data/lib/components.json +13 -1
- data/lib/generators/shadcn-ui_generator.rb +1 -1
- data/lib/shadcn-ui/version.rb +1 -1
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc5e15a7e38cfc62b7c6baeabfd28b0d1b1869d9c1af906cee95a6c1d6dafb0e
|
4
|
+
data.tar.gz: 4ace082612e01960d714dc068e734a67d57cac288f77bb13eed827d22591e87e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f604552bb826fd72f8974ce1a5fec84c93ece1957df5b8831287abb7f2ffb5520068a713bd6f8fd9d9b4804ab6cce50648adc32778282472f991a328f2da6b0b
|
7
|
+
data.tar.gz: 5bc64754ad18e8d2d70641934bfc8db0bea5855b3921b43a9626214122da0dfb12b9e925c904ae3525bbf7f7c44c50b696701b90f6c2bf3db239e0ae051c9232
|
data/Procfile.dev
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/shadcn-ui)
|
4
4
|
|
5
|
-
|
6
|
-
Source. **Use this to build your own component library**.
|
5
|
+
[Shadcn on Rails](https://shadcn.rails-components.com) provides customizable components that you can
|
6
|
+
copy and paste into your apps. Free. Open Source. **Use this to build your own component library**.
|
7
|
+
|
8
|
+
**If you're using this, [please let me know](https://twitter.com/aviflombaum) so I keep developing
|
9
|
+
it.**
|
7
10
|
|
8
11
|
## About
|
9
12
|
|
@@ -21,321 +24,17 @@ Use this as a reference to build your own component libraries.
|
|
21
24
|
|
22
25
|

|
23
26
|
|
24
|
-
##
|
25
|
-
|
26
|
-
Prior to the initial gem release, you can use this as an alpha by cloning this repository and
|
27
|
-
starting up the app as you would a standard rails app.
|
28
|
-
|
29
|
-
```
|
30
|
-
git clone https://github.com/aviflombaum/shadcn-rails.git
|
31
|
-
cd shadcn-rails
|
32
|
-
bundle install
|
33
|
-
./bin/dev
|
34
|
-
```
|
35
|
-
|
36
|
-
There are very few dependencies and no database so it should just boot up. Visit
|
37
|
-
http://localhost:3000 to see the demo app which is also the documentation. You'll be able to browse
|
38
|
-
the components at http://localhost:3000/components.
|
39
|
-
|
40
|
-
If there's a component you want to try in your app, you will be copying the code from this app to
|
41
|
-
yours. There's a few other steps you'll need.
|
42
|
-
|
43
|
-
## Installing a Component
|
44
|
-
|
45
|
-
### Add Tailwind CSS
|
46
|
-
|
47
|
-
Components are styled using Tailwind CSS. You need to install Tailwind CSS in your project.
|
48
|
-
|
49
|
-
[Follow the Tailwind CSS installation instructions to get started.](https://tailwindcss.com/docs/installation)
|
50
|
-
|
51
|
-
### Add dependencies
|
52
|
-
|
53
|
-
If you haven't already, install Tailwind into your rails application by adding `tailwindcss-rails`
|
54
|
-
to your `Gemfile` and install tailwind into your app:
|
55
|
-
|
56
|
-
```sh
|
57
|
-
./bin/bundle add tailwindcss-rails
|
58
|
-
./bin/rails tailwindcss:install
|
59
|
-
```
|
60
|
-
|
61
|
-
Then install ./bin/rails tailwindcss:install
|
62
|
-
|
63
|
-
### Configure tailwind.config.js
|
64
|
-
|
65
|
-
Here's what my `tailwind.config.js` file looks like:
|
66
|
-
|
67
|
-
```js title="tailwind.config.js"
|
68
|
-
const defaultTheme = require("tailwindcss/defaultTheme");
|
69
|
-
|
70
|
-
module.exports = {
|
71
|
-
darkMode: ["class"],
|
72
|
-
content: [
|
73
|
-
"./public/*.html",
|
74
|
-
"./app/helpers/**/*.rb",
|
75
|
-
"./app/javascript/**/*.js",
|
76
|
-
"./app/views/**/*.{erb,haml,html,slim}",
|
77
|
-
],
|
78
|
-
theme: {
|
79
|
-
container: {
|
80
|
-
center: true,
|
81
|
-
padding: "2rem",
|
82
|
-
screens: {
|
83
|
-
"2xl": "1400px",
|
84
|
-
},
|
85
|
-
},
|
86
|
-
extend: {
|
87
|
-
colors: {
|
88
|
-
border: "hsl(var(--border))",
|
89
|
-
input: "hsl(var(--input))",
|
90
|
-
ring: "hsl(var(--ring))",
|
91
|
-
background: "hsl(var(--background))",
|
92
|
-
foreground: "hsl(var(--foreground))",
|
93
|
-
primary: {
|
94
|
-
DEFAULT: "hsl(var(--primary))",
|
95
|
-
foreground: "hsl(var(--primary-foreground))",
|
96
|
-
},
|
97
|
-
secondary: {
|
98
|
-
DEFAULT: "hsl(var(--secondary))",
|
99
|
-
foreground: "hsl(var(--secondary-foreground))",
|
100
|
-
},
|
101
|
-
destructive: {
|
102
|
-
DEFAULT: "hsl(var(--destructive))",
|
103
|
-
foreground: "hsl(var(--destructive-foreground))",
|
104
|
-
},
|
105
|
-
muted: {
|
106
|
-
DEFAULT: "hsl(var(--muted))",
|
107
|
-
foreground: "hsl(var(--muted-foreground))",
|
108
|
-
},
|
109
|
-
accent: {
|
110
|
-
DEFAULT: "hsl(var(--accent))",
|
111
|
-
foreground: "hsl(var(--accent-foreground))",
|
112
|
-
},
|
113
|
-
popover: {
|
114
|
-
DEFAULT: "hsl(var(--popover))",
|
115
|
-
foreground: "hsl(var(--popover-foreground))",
|
116
|
-
},
|
117
|
-
card: {
|
118
|
-
DEFAULT: "hsl(var(--card))",
|
119
|
-
foreground: "hsl(var(--card-foreground))",
|
120
|
-
},
|
121
|
-
},
|
122
|
-
borderRadius: {
|
123
|
-
lg: `var(--radius)`,
|
124
|
-
md: `calc(var(--radius) - 2px)`,
|
125
|
-
sm: "calc(var(--radius) - 4px)",
|
126
|
-
},
|
127
|
-
fontFamily: {
|
128
|
-
sans: ["var(--font-sans)", ...defaultTheme.fontFamily.sans],
|
129
|
-
},
|
130
|
-
keyframes: {
|
131
|
-
"accordion-down": {
|
132
|
-
from: { height: 0 },
|
133
|
-
to: { height: "var(--radix-accordion-content-height)" },
|
134
|
-
},
|
135
|
-
"accordion-up": {
|
136
|
-
from: { height: "var(--radix-accordion-content-height)" },
|
137
|
-
to: { height: 0 },
|
138
|
-
},
|
139
|
-
},
|
140
|
-
animation: {
|
141
|
-
"accordion-down": "accordion-down 0.2s ease-out",
|
142
|
-
"accordion-up": "accordion-up 0.2s ease-out",
|
143
|
-
},
|
144
|
-
},
|
145
|
-
},
|
146
|
-
plugins: [
|
147
|
-
require("@tailwindcss/forms"),
|
148
|
-
require("@tailwindcss/aspect-ratio"),
|
149
|
-
require("@tailwindcss/typography"),
|
150
|
-
require("@tailwindcss/container-queries"),
|
151
|
-
require("tailwindcss-animate"),
|
152
|
-
],
|
153
|
-
};
|
154
|
-
```
|
155
|
-
|
156
|
-
### Configure styles
|
157
|
-
|
158
|
-
Add the following to your app/assets/stylesheets/application.tailwind.css file.
|
159
|
-
|
160
|
-
```css title="application.tailwind.css"
|
161
|
-
@tailwind base;
|
162
|
-
@tailwind components;
|
163
|
-
@tailwind utilities;
|
164
|
-
|
165
|
-
@layer base {
|
166
|
-
:root {
|
167
|
-
--background: 0 0% 100%;
|
168
|
-
--foreground: 222.2 47.4% 11.2%;
|
169
|
-
|
170
|
-
--muted: 210 40% 96.1%;
|
171
|
-
--muted-foreground: 215.4 16.3% 46.9%;
|
172
|
-
|
173
|
-
--popover: 0 0% 100%;
|
174
|
-
--popover-foreground: 222.2 47.4% 11.2%;
|
175
|
-
|
176
|
-
--border: 214.3 31.8% 91.4%;
|
177
|
-
--input: 214.3 31.8% 91.4%;
|
178
|
-
|
179
|
-
--card: 0 0% 100%;
|
180
|
-
--card-foreground: 222.2 47.4% 11.2%;
|
181
|
-
|
182
|
-
--primary: 222.2 47.4% 11.2%;
|
183
|
-
--primary-foreground: 210 40% 98%;
|
184
|
-
|
185
|
-
--secondary: 210 40% 96.1%;
|
186
|
-
--secondary-foreground: 222.2 47.4% 11.2%;
|
187
|
-
|
188
|
-
--accent: 210 40% 96.1%;
|
189
|
-
--accent-foreground: 222.2 47.4% 11.2%;
|
190
|
-
|
191
|
-
--destructive: 0 100% 50%;
|
192
|
-
--destructive-foreground: 210 40% 98%;
|
193
|
-
|
194
|
-
--ring: 215 20.2% 65.1%;
|
195
|
-
|
196
|
-
--radius: 0.5rem;
|
197
|
-
}
|
198
|
-
|
199
|
-
.dark {
|
200
|
-
--background: 224 71% 4%;
|
201
|
-
--foreground: 213 31% 91%;
|
202
|
-
|
203
|
-
--muted: 223 47% 11%;
|
204
|
-
--muted-foreground: 215.4 16.3% 56.9%;
|
205
|
-
|
206
|
-
--accent: 216 34% 17%;
|
207
|
-
--accent-foreground: 210 40% 98%;
|
208
|
-
|
209
|
-
--popover: 224 71% 4%;
|
210
|
-
--popover-foreground: 215 20.2% 65.1%;
|
211
|
-
|
212
|
-
--border: 216 34% 17%;
|
213
|
-
--input: 216 34% 17%;
|
214
|
-
|
215
|
-
--card: 224 71% 4%;
|
216
|
-
--card-foreground: 213 31% 91%;
|
217
|
-
|
218
|
-
--primary: 210 40% 98%;
|
219
|
-
--primary-foreground: 222.2 47.4% 1.2%;
|
220
|
-
|
221
|
-
--secondary: 222.2 47.4% 11.2%;
|
222
|
-
--secondary-foreground: 210 40% 98%;
|
223
|
-
|
224
|
-
--destructive: 0 63% 31%;
|
225
|
-
--destructive-foreground: 210 40% 98%;
|
226
|
-
|
227
|
-
--ring: 216 34% 17%;
|
228
|
-
|
229
|
-
--radius: 0.5rem;
|
230
|
-
}
|
231
|
-
}
|
232
|
-
|
233
|
-
@layer base {
|
234
|
-
* {
|
235
|
-
@apply border-border;
|
236
|
-
}
|
237
|
-
body {
|
238
|
-
@apply bg-background text-foreground;
|
239
|
-
font-feature-settings:
|
240
|
-
"rlig" 1,
|
241
|
-
"calt" 1;
|
242
|
-
}
|
243
|
-
}
|
244
|
-
```
|
245
|
-
|
246
|
-
### Copy a a component's files to your application
|
247
|
-
|
248
|
-
For example, if you want to use the Accordion component, you would copy the following files to your
|
249
|
-
application:
|
250
|
-
|
251
|
-
- `app/javascript/controllers/components/ui/accordion_controller.js` The Stimulus controller for any
|
252
|
-
component that requires javascript.
|
253
|
-
- `app/helpers/components/accordion_helper.rb` The helper for the component that allows for easy
|
254
|
-
rendering within views.
|
255
|
-
- `app/views/components/ui/_accordion.html.erb` The html for the component.
|
256
|
-
|
257
|
-
Once those are copied in your application you can render an accordion with:
|
258
|
-
|
259
|
-
```erb
|
260
|
-
<%= render_accordion title: "Did you know?", description: "You can wrap shadcn helpers in any
|
261
|
-
component library you want!" %>
|
262
|
-
<%= render_accordion title: "Use the generators.", description: "Add components with #{code("rails g shadcn_ui add accordion")}".html_safe %>
|
263
|
-
```
|
264
|
-
|
265
|
-
See the component's demo page in `app/views/examples/components/accordion.html.erb` for more
|
266
|
-
examples.
|
267
|
-
|
268
|
-
This will be similar for each component.
|
269
|
-
|
270
|
-
## Documentation
|
271
|
-
|
272
|
-
Visit https://avi.nyc/shadcn-on-rails to view the documentation.
|
273
|
-
|
274
|
-
## Contributing
|
275
|
-
|
276
|
-
I am desperately seeking contributors to this project as it is in the very early stages.
|
277
|
-
|
278
|
-
### Contributing with Issues
|
279
|
-
|
280
|
-
I am looking for people to start documenting issues in the project. The issues I'm interested in
|
281
|
-
are:
|
282
|
-
|
283
|
-
1. What components are missing? Just start listing out the components that have yet to be
|
284
|
-
implemented, better yet, open a PR with a branch for that issue and we can all start adding to
|
285
|
-
it.
|
286
|
-
2. What components are not accessible? I am not an accessibility expert, so I need help with this.
|
287
|
-
If you see something that is not accessible, please open an issue and let me know. This might
|
288
|
-
mean the aria labels are hardcoded and not customizable or that the labels are simply missing.
|
289
|
-
3. What components are not customizable? I am trying to make all components as customizable as
|
290
|
-
possible. If you see something that is not customizable, please open an issue and let me know.
|
291
|
-
All the attributes of a component, like aria labels or classes or id or name, etc, should be
|
292
|
-
customizeable by passing attributes into their helper functions that are passed down to the
|
293
|
-
component's partial to be rendered.
|
294
|
-
4. Suggestions for the API of the components. I am trying to make the API as simple as possible. If
|
295
|
-
you have any suggestions for how to make the API simpler, please open an issue and let me know. I
|
296
|
-
am open to any and all suggestions.
|
297
|
-
|
298
|
-
These are 3 main areas that would make the project easier for people to contribute to. They all make
|
299
|
-
for great opportunities for someone new to open source to both file the issue and even begin to
|
300
|
-
slowly implement them.
|
301
|
-
|
302
|
-
### Setup
|
303
|
-
|
304
|
-
1. Fork and clone the repo.
|
305
|
-
2. Run `bundle install` to install dependencies, there aren't many as this is currently a standard
|
306
|
-
Rails applications.
|
307
|
-
3. `/bin/dev` to start the application.
|
308
|
-
|
309
|
-
### App Structure
|
310
|
-
|
311
|
-
For now this is a standard Rails 7 application using propshaft and **importmaps**. This will soon be
|
312
|
-
extracted into a gem that provides the components to be installed (copied) into the including
|
313
|
-
application.
|
314
|
-
|
315
|
-
### Components
|
316
|
-
|
317
|
-
The goal of this project is to provide a set of components that can be copied into your application.
|
318
|
-
The components are built using [TailwindCSS](https://tailwindcss.com/) and
|
319
|
-
[Stimulus](https://stimulus.hotwire.dev/). Each component follows the same structure:
|
27
|
+
## Installation
|
320
28
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
2. There is a component helper named after the component that is responsible for rendering the
|
325
|
-
component and taking in arguments to customize the component located in `app/helpers/components`.
|
326
|
-
Ex `app/helpers/components/button.rb` provides the `render_button` helper that accepts arguments
|
327
|
-
such as `variant` which describes the kind of button and passes the classes for that `variant` to
|
328
|
-
the partial.
|
329
|
-
3. When needed there is a stimulus controller for the component that provides the javascript
|
330
|
-
required to make the component interactive. Ex. `app/javascript/controllers/toast_controller.js`
|
331
|
-
provides the javascript for the toast component to display it and then hide it after a certain
|
332
|
-
amount of time.
|
29
|
+
Refer to
|
30
|
+
[Installation](https://github.com/aviflombaum/shadcn-rails/blob/main/app/views/documentation/installation.html.md)
|
31
|
+
or the [Installation](https://shadcn.rails-components.com/docs/installation) page on the demo site.
|
333
32
|
|
334
|
-
|
335
|
-
`app/views/examples/components`. This is used to create the documentation site that this application
|
336
|
-
provides with examples of teh components rendered.
|
33
|
+
## Development
|
337
34
|
|
338
|
-
|
35
|
+
Clone the repo and run `bin/setup` to install dependencies. Then, run `bin/dev` to start the
|
36
|
+
tailwind watcher and then run `rails s`. I have to run the server and tailwind separately to keep
|
37
|
+
debuggers working.
|
339
38
|
|
340
39
|
## [shadcn-ui](https://ui.shadcn.com)
|
341
40
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class UsersController < ApplicationController
|
2
|
+
layout "component"
|
3
|
+
|
4
|
+
def create
|
5
|
+
params[:component] = "forms"
|
6
|
+
@user = User.new(user_params)
|
7
|
+
if @user.valid?
|
8
|
+
# Toast message
|
9
|
+
else
|
10
|
+
render "examples/components/forms", status: 422
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def user_params
|
17
|
+
params.require(:user).permit(:email, :password)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class ShadcnFormBuilder < ActionView::Helpers::FormBuilder
|
2
|
+
def label(method, options = {})
|
3
|
+
error_class = @object.errors[method].any? ? "error" : ""
|
4
|
+
options[:class] = @template.tw("#{options[:class]} #{error_class}")
|
5
|
+
@template.render_label(name: "#{object_name}[#{method}]", label: method.capitalize, **options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def text_field(method, options = {})
|
9
|
+
options[:class] << " error" if @object.errors[method].any?
|
10
|
+
@template.render_input(
|
11
|
+
name: "#{object_name}[#{method}]",
|
12
|
+
id: "#{object_name}_#{method}",
|
13
|
+
value: @object.send(method),
|
14
|
+
type: "text", **options
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def password_field(method, options = {})
|
19
|
+
error_class = @object.errors[method].any? ? "error" : ""
|
20
|
+
options[:class] = @template.tw("#{options[:class]} #{error_class}")
|
21
|
+
@template.render_input(
|
22
|
+
name: "#{object_name}[#{method}]",
|
23
|
+
id: "#{object_name}_#{method}",
|
24
|
+
value: @object.send(method),
|
25
|
+
type: "password", **options
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def email_field(method, options = {})
|
30
|
+
error_class = @object.errors[method].any? ? "error" : ""
|
31
|
+
options[:class] = @template.tw("#{options[:class]} #{error_class}")
|
32
|
+
@template.render_input(
|
33
|
+
name: "#{object_name}[#{method}]",
|
34
|
+
id: "#{object_name}_#{method}",
|
35
|
+
value: @object.send(method),
|
36
|
+
type: "email", **options
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def submit(value = nil, options = {})
|
41
|
+
@template.render_button(value, **options)
|
42
|
+
end
|
43
|
+
end
|
@@ -1,6 +1,22 @@
|
|
1
1
|
module Components::AlertDialogHelper
|
2
|
-
def render_alert_dialog(
|
3
|
-
|
4
|
-
render "components/ui/alert_dialog",
|
2
|
+
def render_alert_dialog(**options, &block)
|
3
|
+
content = capture(&block) if block
|
4
|
+
render "components/ui/alert_dialog", content: content, **options
|
5
|
+
end
|
6
|
+
|
7
|
+
def alert_dialog_trigger(&block)
|
8
|
+
content_for :alert_dialog_trigger, capture(&block), flush: true
|
9
|
+
end
|
10
|
+
|
11
|
+
def alert_dialog_content(&block)
|
12
|
+
content_for :alert_dialog_content, capture(&block), flush: true
|
13
|
+
end
|
14
|
+
|
15
|
+
def alert_dialog_continue(&block)
|
16
|
+
content_for :alert_dialog_continue, capture(&block), flush: true
|
17
|
+
end
|
18
|
+
|
19
|
+
def alert_dialog_cancel(&block)
|
20
|
+
content_for :alert_dialog_cancel, capture(&block), flush: true
|
5
21
|
end
|
6
22
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Components::FormsHelper
|
2
|
+
def render_form_with(**opts)
|
3
|
+
form_with(**opts.merge(builder: ShadcnFormBuilder)) do |form|
|
4
|
+
yield form
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def render_form_for(obj, **opts)
|
9
|
+
form_for(obj, **opts.merge(builder: ShadcnFormBuilder), html: opts) do |form|
|
10
|
+
yield form
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,14 +1,24 @@
|
|
1
1
|
module Components::InputHelper
|
2
2
|
def render_input(name:, label: false, id: nil, type: :text, value: nil, **options)
|
3
|
-
options[:class] = "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 #{options[:class]} "
|
3
|
+
options[:class] = "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm transition-colors ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 #{options[:class]} "
|
4
4
|
options[:class] << case options[:variant]
|
5
5
|
when :borderless
|
6
6
|
" border-0 focus-visible:outline-none focus-visible:shadow-none focus-visible:ring-transparent"
|
7
7
|
else
|
8
|
-
" focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:border-muted"
|
8
|
+
"shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:border-muted"
|
9
9
|
end
|
10
|
-
options
|
11
|
-
|
10
|
+
options[:class] = tw(options[:class])
|
11
|
+
|
12
|
+
options.reverse_merge!(
|
13
|
+
label: (options[:lable] || false),
|
14
|
+
required: (options[:required] || false),
|
15
|
+
disabled: (options[:disabled] || false),
|
16
|
+
readonly: (options[:readonly] || false),
|
17
|
+
placeholder: (options[:placeholder] || ""),
|
18
|
+
autocomplete: (options[:autocomplete] || ""),
|
19
|
+
autocapitalize: (options[:autocapitalize] || nil),
|
20
|
+
autocorrect: (options[:autocorrect] || nil)
|
21
|
+
)
|
12
22
|
render partial: "components/ui/input", locals: {
|
13
23
|
type:,
|
14
24
|
label:,
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module Components::ToastHelper
|
2
|
-
def render_toast(header: nil, description: nil, action: nil, class: nil, data:
|
2
|
+
def render_toast(header: nil, description: nil, action: nil, class: nil, data: {}, variant: :default, **options, &block)
|
3
|
+
options[:class] ||= ""
|
4
|
+
options[:class] << " destructive group border-destructive bg-destructive text-destructive-foreground " if variant == :destructive
|
5
|
+
|
3
6
|
render "components/ui/toast", header:, description:, action:, class:, data:, options: options
|
4
7
|
end
|
5
8
|
end
|
@@ -1,4 +1,8 @@
|
|
1
1
|
module ComponentsHelper
|
2
|
+
def tw(classes)
|
3
|
+
TailwindMerge::Merger.new.merge(classes)
|
4
|
+
end
|
5
|
+
|
2
6
|
PRIMARY_CLASSES = " bg-primary text-primary-foreground hover:bg-primary/80 "
|
3
7
|
SECONDARY_CLASSES = " bg-secondary text-secondary-foreground hover:bg-secondary/80 "
|
4
8
|
OUTLINE_CLASSES = " border border-input bg-background hover:bg-accent hover:text-accent-foreground "
|
@@ -1,6 +1,6 @@
|
|
1
1
|
// Inspired by https://github.com/kanety/stimulus-dialog
|
2
2
|
import { Controller } from "@hotwired/stimulus";
|
3
|
-
import "
|
3
|
+
import "https://ga.jspm.io/npm:@kanety/stimulus-static-actions@1.0.1/dist/index.modern.js";
|
4
4
|
|
5
5
|
export default class UIDialog extends Controller {
|
6
6
|
static targets = ["dialog", "modal", "focus", "drag", "backdrop", "closeButton"];
|
@@ -1,5 +1,5 @@
|
|
1
1
|
// Inspired By: https://github.com/stimulus-components/stimulus-dropdown/blob/master/src/index.ts
|
2
2
|
import UIPopover from "controllers/ui/popover_controller";
|
3
|
-
import { useTransition } from "stimulus-use";
|
3
|
+
import { useTransition } from "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js";
|
4
4
|
|
5
5
|
export default class extends UIPopover {}
|
@@ -1,9 +1,8 @@
|
|
1
1
|
// Inspired by: https://github.com/excid3/tailwindcss-stimulus-components/blob/master/src/popover.js
|
2
2
|
|
3
3
|
import { Controller } from "@hotwired/stimulus";
|
4
|
-
import { createPopper } from "
|
5
|
-
import { useDebounce } from "stimulus-use";
|
6
|
-
import { useHover } from "stimulus-use";
|
4
|
+
import { createPopper } from "https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js";
|
5
|
+
import { useDebounce, useHover } from "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js";
|
7
6
|
|
8
7
|
export default class UIHoverCardController extends Controller {
|
9
8
|
static debounces = ["mouseEnter", "mouseLeave"];
|
@@ -1,8 +1,8 @@
|
|
1
1
|
// Inspired by: https://github.com/excid3/tailwindcss-stimulus-components/blob/master/src/popover.js
|
2
2
|
|
3
3
|
import { Controller } from "@hotwired/stimulus";
|
4
|
-
import { createPopper } from "
|
5
|
-
import { useClickOutside } from "stimulus-use";
|
4
|
+
import { createPopper } from "https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js";
|
5
|
+
import { useClickOutside } from "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js";
|
6
6
|
|
7
7
|
export default class UIPopover extends Controller {
|
8
8
|
static values = {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import UIDialog from "controllers/ui/dialog_controller";
|
2
|
-
import "
|
2
|
+
import "https://ga.jspm.io/npm:@kanety/stimulus-static-actions@1.0.1/dist/index.modern.js";
|
3
3
|
|
4
4
|
export default class extends UIDialog {
|
5
5
|
// Handles a button triggering the sheet in a different
|
@@ -4,40 +4,67 @@ export default class UIToastController extends Controller {
|
|
4
4
|
static targets = ["item"];
|
5
5
|
|
6
6
|
connect() {
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
if (this.element.role == "region" && this.element.dataset.auto != "false") {
|
8
|
+
setTimeout(() => {
|
9
|
+
this.open();
|
10
|
+
}, 1000);
|
11
|
+
this.close();
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
open() {
|
16
|
+
const toastElement = this.element.querySelector("[data-ui--toast-target='item']");
|
17
|
+
this.element.dataset.state = "open";
|
18
|
+
this.element.classList.remove("hidden");
|
19
|
+
this.showToast(toastElement);
|
20
|
+
}
|
21
|
+
|
22
|
+
close() {
|
23
|
+
const toastElement = this.element.querySelector("[data-ui--toast-target='item']");
|
24
|
+
this.element.dataset.state = "closed";
|
25
|
+
this.element.classList.add("hidden");
|
26
|
+
this.closeToast(toastElement);
|
27
|
+
}
|
28
|
+
|
29
|
+
showToast(el) {
|
30
|
+
if (el) {
|
31
|
+
el.dataset.state = "open";
|
32
|
+
el.classList.remove("hidden");
|
33
|
+
}
|
34
|
+
}
|
10
35
|
|
11
|
-
|
36
|
+
closeToast(el) {
|
37
|
+
if (el) {
|
38
|
+
setTimeout(() => {
|
39
|
+
el.dataset.state = "closed";
|
40
|
+
el.classList.add("hidden");
|
41
|
+
}, el.dataset.duration || 3000);
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
trigger() {
|
46
|
+
const idTarget = this.element.dataset.target;
|
47
|
+
const toastContainer = document.querySelector(`${idTarget}`);
|
48
|
+
toastContainer.dataset.state = "open";
|
49
|
+
toastContainer.classList.remove("hidden");
|
50
|
+
const toastElement = toastContainer.querySelector("[data-ui--toast-target='item']");
|
51
|
+
this.showToast(toastElement);
|
52
|
+
this.closeToast(toastElement);
|
12
53
|
}
|
13
54
|
|
14
55
|
openAll() {
|
15
|
-
const toastElements = document.querySelectorAll(
|
56
|
+
const toastElements = document.querySelectorAll(
|
57
|
+
"[data-ui--toast-target='item']:not([data-visible='false'])",
|
58
|
+
);
|
16
59
|
toastElements.forEach((toastElement) => {
|
17
|
-
toastElement
|
18
|
-
toastElement.classList.remove("hidden");
|
60
|
+
this.showToast(toastElement);
|
19
61
|
});
|
20
62
|
}
|
21
63
|
|
22
64
|
closeAll() {
|
23
65
|
const toastElements = document.querySelectorAll("[data-ui--toast-target='item']");
|
24
66
|
toastElements.forEach((toastElement) => {
|
25
|
-
|
26
|
-
toastElement.dataset.state = "closed";
|
27
|
-
toastElement.classList.add("hidden");
|
28
|
-
}, toastElement.dataset.duration);
|
67
|
+
this.closeToast(toastElement);
|
29
68
|
});
|
30
69
|
}
|
31
|
-
|
32
|
-
test() {
|
33
|
-
const $this = this;
|
34
|
-
this.closeAll();
|
35
|
-
setTimeout(() => {
|
36
|
-
$this.openAll();
|
37
|
-
setTimeout(() => {
|
38
|
-
$this.closeAll();
|
39
|
-
}, 3000);
|
40
|
-
}, 1000);
|
41
|
-
}
|
42
|
-
close() {}
|
43
70
|
}
|
data/app/models/user.rb
ADDED
@@ -1,7 +1,7 @@
|
|
1
1
|
<div data-controller="ui--dialog">
|
2
|
+
<div data-action="click->ui--dialog#open"><%= content_for(:alert_dialog_trigger) %></div>
|
2
3
|
<%= render "components/ui/shared/backdrop", as: "backdrop" %>
|
3
4
|
|
4
|
-
<%= trigger %>
|
5
5
|
<div
|
6
6
|
role="alertdialog"
|
7
7
|
data-state="closed"
|
@@ -9,12 +9,16 @@
|
|
9
9
|
tabindex="-1"
|
10
10
|
style="pointer-events: auto"
|
11
11
|
data-ui--dialog-target="dialog">
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
</div>
|
12
|
+
<div>
|
13
|
+
<%= content_for(:alert_dialog_content) %>
|
14
|
+
</div>
|
16
15
|
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
|
17
|
-
|
16
|
+
<div data-action="click->ui--dialog#close">
|
17
|
+
<%= content_for(:alert_dialog_cancel) %>
|
18
|
+
</div>
|
19
|
+
<div data-action="click->ui--dialog#close">
|
20
|
+
<%= content_for(:alert_dialog_continue) %>
|
21
|
+
</div>
|
18
22
|
</div>
|
19
23
|
</div>
|
20
24
|
</div>
|
@@ -41,29 +41,3 @@
|
|
41
41
|
</button>
|
42
42
|
</div>
|
43
43
|
</div>
|
44
|
-
<!-- <div data-controller="ui--dialog">
|
45
|
-
<div
|
46
|
-
data-ui--dialog-target="modal"
|
47
|
-
data-state="closed"
|
48
|
-
class="hidden fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
49
|
-
style="pointer-events: auto"
|
50
|
-
data-aria-hidden="true"
|
51
|
-
aria-hidden="true"></div>
|
52
|
-
|
53
|
-
<%#= trigger %>
|
54
|
-
<div
|
55
|
-
role="alertdialog"
|
56
|
-
data-state="closed"
|
57
|
-
class="hidden fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full"
|
58
|
-
tabindex="-1"
|
59
|
-
style="pointer-events: auto"
|
60
|
-
data-ui--dialog-target="dialog">
|
61
|
-
<div class="flex flex-col space-y-2 text-center sm:text-left">
|
62
|
-
<h2 class="text-lg font-semibold"><%#= label %></h2>
|
63
|
-
<p class="text-sm text-muted-foreground"><%#= description %></p>
|
64
|
-
</div>
|
65
|
-
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
|
66
|
-
<%#= cancel %> <%#= continue %>
|
67
|
-
</div>
|
68
|
-
</div>
|
69
|
-
</div> -->
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<%= render_card do %>
|
3
3
|
<div class="flex items-center">
|
4
4
|
<%= content_for :filter_icon %>
|
5
|
-
<%= render_input name: "filter", placeholder: "Filter items...",
|
5
|
+
<%= render_input name: "filter", placeholder: "Filter items...", variant: :borderless, class: input_class, data: {"ui--filter-target": "source", action: "input->ui--filter#filter"} %></div>
|
6
6
|
<%= render_separator %>
|
7
7
|
<div class="<%= options[:class] %>">
|
8
8
|
<%= content_tag :div, role: "group" do
|
@@ -2,6 +2,9 @@
|
|
2
2
|
<%= text_field_tag name, value, type: type, id: id,
|
3
3
|
class: options[:class],
|
4
4
|
placeholder: options[:placeholder],
|
5
|
+
autocomplete: options[:autocomplete],
|
6
|
+
autocapitalize: options[:autocapitalize],
|
7
|
+
autocorrect: options[:autocorrect],
|
5
8
|
disabled: options[:disabled],
|
6
9
|
required: options[:required],
|
7
10
|
readonly: options[:readonly],
|
@@ -1,3 +1,3 @@
|
|
1
1
|
<%= label_tag name, label,
|
2
2
|
data: options[:data],
|
3
|
-
class: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 #{options[:class]}" %>
|
3
|
+
class: tw("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 #{options[:class]}") %>
|
@@ -1,4 +1,8 @@
|
|
1
|
-
<div role="region" aria-label="Notifications (F8)" tabindex="-1" style=""
|
1
|
+
<div role="region" aria-label="Notifications (F8)" tabindex="-1" style=""
|
2
|
+
id="<%= options[:id] %>"
|
3
|
+
data-controller="ui--toast <%= data[:controller] %>"
|
4
|
+
data-auto="<%= options[:auto] %>"
|
5
|
+
class="hidden">
|
2
6
|
<span aria-hidden="true" tabindex="0" style="position: fixed; border: 0px; width: 1px; height: 1px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; overflow-wrap: normal;"></span>
|
3
7
|
<ol tabindex="-1" class="fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]">
|
4
8
|
<li role="status"
|
@@ -9,7 +13,7 @@
|
|
9
13
|
data-ui--toast-target="item"
|
10
14
|
data-swipe-direction="right"
|
11
15
|
data-duration="5000"
|
12
|
-
class="toast-item hidden group pointer-events-auto relative flex w-full items-center
|
16
|
+
class="<%= tw("toast-item hidden group pointer-events-auto relative flex w-full items-center
|
13
17
|
justify-between space-x-4 overflow-hidden rounded-md p-6 pr-8 shadow-lg
|
14
18
|
transition-all
|
15
19
|
data-[swipe=cancel]:translate-x-0
|
@@ -20,14 +24,14 @@
|
|
20
24
|
data-[state=closed]:fade-out-80
|
21
25
|
data-[state=open]:slide-in-from-top-full
|
22
26
|
data-[state=open]:sm:slide-in-from-bottom-full
|
23
|
-
border bg-background"
|
27
|
+
border bg-background #{options[:class]}") %>"
|
24
28
|
style="user-select: none; touch-action: none;">
|
25
29
|
<div class="grid gap-1">
|
26
30
|
<div class="text-sm font-semibold"><%= header %></div>
|
27
31
|
<div class="text-sm opacity-90"><%= description %></div>
|
28
32
|
</div>
|
29
33
|
<%= action %>
|
30
|
-
<button data-action="ui--toast#
|
34
|
+
<button data-action="ui--toast#close" type="button" class="absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600" toast-close="" data-radix-toast-announce-exclude=""><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4"><line x1="18" x2="6" y1="6" y2="18"></line><line x1="6" x2="18" y1="6" y2="18"></line></svg></button>
|
31
35
|
</li>
|
32
36
|
</ol>
|
33
37
|
<span aria-hidden="true" tabindex="0" style="position: fixed; border: 0px; width: 1px; height: 1px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; overflow-wrap: normal;"></span>
|
@@ -32,7 +32,7 @@ The components need a few things in order to render and function properly
|
|
32
32
|
package.json if you need via `echo '{}' >> package.json`.
|
33
33
|
|
34
34
|
```
|
35
|
-
npm install -D
|
35
|
+
npm install -D tailwindcss-animate
|
36
36
|
```
|
37
37
|
|
38
38
|
These are the requirements if you want to add them individually:
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<p class="leading-7 [&:not(:first-child)]:mt-6">The Alert Dialog component introduces:</p>
|
2
|
+
|
3
|
+
<ul class="my-6 ml-6 list-disc [&>li]:mt-2 text-sm">
|
4
|
+
<li><%= code("app/helpers/components/alert_dialog_helper.rb") %></li>
|
5
|
+
<li><%= code("app/views/components/ui/_alert_dialog.html.erb") %></li>
|
6
|
+
</ul>
|
7
|
+
|
8
|
+
And relies on <%= code("app/javascript/controllers/ui/dialog_controller.js") %>.
|
9
|
+
|
10
|
+
<p class="leading-7 [&:not(:first-child)]:mt-6">
|
11
|
+
The method <%= code("render_alert_dialog") %> defined in <%= code("app/helpers/components/alert_dialog_helper.rb") %>
|
12
|
+
accepts a block for the inner components of the dialog. It renders the partial
|
13
|
+
<%= code("app/views/components/ui/_alert_dialog.html.erb") %> which contains the model structure and
|
14
|
+
and the 2 actions buttons of continue and cancel.
|
15
|
+
</p>
|
16
|
+
|
17
|
+
<p class="leading-7 [&:not(:first-child)]:mt-6">
|
18
|
+
The modal is composed of 4 components with corresponding helper methods, <%= code("alert_dialog_trigger") %>,
|
19
|
+
which accepts a block for the element that will trigger the modal, and <%= code("alert_dialog_content") %>,
|
20
|
+
which accepts a block for the body of the modal and the two actions for the modal, <%= code("alert_dialog_cancel") %> for the element that you would like to cancel the dialog interaction, for example a button, and <%= code("alert_dialog_continue") %> for the element you would like to use to continue to interaction. Note, you should feel free to bind your own data actions to these buttons as they are enclosed in an element that will handle closing the modal on click.
|
21
|
+
</p>
|
22
|
+
|
23
|
+
<p class="leading-7 [&:not(:first-child)]:mt-6">
|
24
|
+
See <a href="/docs/components/dialog">Dialog</a> for more context as this component inherits functionality from that one.</p>
|
@@ -1,6 +1,22 @@
|
|
1
|
-
<%= render_alert_dialog
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
<%= render_alert_dialog do %>
|
2
|
+
<%= alert_dialog_trigger do %>
|
3
|
+
<%= render_button("Open Dialog", variant: :outline) %>
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<%= alert_dialog_content do %>
|
7
|
+
<div class="flex flex-col space-y-2 text-center sm:text-left">
|
8
|
+
<h2 class="text-lg font-semibold">Are you absolutely sure?</h2>
|
9
|
+
<p class="text-sm text-muted-foreground">
|
10
|
+
This action cannot be undone. This will permanently delete your account and remove your data from our servers.
|
11
|
+
</p>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<%= alert_dialog_cancel do %>
|
16
|
+
<%= render_button("Cancel", variant: :outline) %>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<%= alert_dialog_continue do %>
|
20
|
+
<%= render_button("Continue") %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
@@ -15,7 +15,13 @@
|
|
15
15
|
|
16
16
|
<%= render_preview %>
|
17
17
|
|
18
|
-
|
19
18
|
<h2 class="font-heading scroll-m-20 border-b pb-2 text-2xl font-semibold tracking-tight first:mt-0" id="installation">Installation</h2>
|
19
|
+
<%= code_sample(language: "sh") do %>
|
20
|
+
rails generate shadcn-ui alert-dialog
|
21
|
+
<% end %>
|
20
22
|
|
21
23
|
<h2 class="font-heading mt-12 scroll-m-20 border-b pb-2 mb-2 text-2xl font-semibold tracking-tight first:mt-0" id="usage"><a href="/docs/components/dialog#usage">Usage</a></h2>
|
24
|
+
|
25
|
+
<%= code_partial("alert-dialog/usage", :erb) %>
|
26
|
+
|
27
|
+
<%= render_usage("alert-dialog") %>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<p class="leading-7 [&:not(:first-child)]:mt-6">The Forms component introduces:</p>
|
2
|
+
|
3
|
+
<ul class="my-6 ml-6 list-disc [&>li]:mt-2 text-sm">
|
4
|
+
<li><%= code("app/form_builders/shadcn_form_builder.rb") %></li>
|
5
|
+
<li><%= code("app/helpers/components/forms_helper.rb") %></li>
|
6
|
+
</ul>
|
7
|
+
|
8
|
+
<p class="leading-7 [&:not(:first-child)]:mt-6">
|
9
|
+
The <code>ShadcnFormBuilder</code> is a custom form builder that extends the
|
10
|
+
<code>FormBuilder</code> class. It adds the following methods that provide the form with inputs and controls:
|
11
|
+
<p>
|
12
|
+
|
13
|
+
<ul class="my-6 ml-6 list-disc [&>li]:mt-2 text-sm">
|
14
|
+
<li><%= code("label") %></li>
|
15
|
+
<li><%= code("text_field") %></li>
|
16
|
+
<li><%= code("password_field") %></li>
|
17
|
+
<li><%= code("email_field") %></li>
|
18
|
+
</ul>
|
19
|
+
</p>
|
20
|
+
|
21
|
+
<p class="leading-7 [&:not(:first-child)]:mt-6">
|
22
|
+
To use these methods which generate the styled, ActiveModel backed form, you use the <%= code("render_form_for") %> or <%= code("render_form_with") %> methods provided by <%= code("app/helpers/components/forms_helper.rb") %>.
|
23
|
+
These methods yield the common <code>FormBuilder</code> instance that you can use to generate the controls and inputs above.
|
24
|
+
</p>
|
25
|
+
|
26
|
+
<p class="leading-7 [&:not(:first-child)]:mt-6">
|
27
|
+
If a form field or attribute fails validation, its label and input will be given an <code>error</code> class to allow for styling. The default <code>error</code> style is defined in <%= code("app/assets/stylesheets/shadcn.css") %>
|
28
|
+
</p>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<%= render_form_for(@user, class: "w-full") do |form| %>
|
2
|
+
<div class="grid grid-cols-3 items-baseline mb-4">
|
3
|
+
<%= form.label :email, class: "text-right mr-4 text-lg" %>
|
4
|
+
<%= form.text_field :email, class: "col-span-2" %>
|
5
|
+
</div>
|
6
|
+
<div class="grid grid-cols-3 items-baseline">
|
7
|
+
<%= form.label :password, class: "text-right mr-4 text-lg" %>
|
8
|
+
<%= form.password_field :password, class: "col-span-2 mb-8" %>
|
9
|
+
</div>
|
10
|
+
<div class="flex justify-center">
|
11
|
+
<%= form.submit "Create Account" %>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<%= render_component_header title: "Forms",
|
2
|
+
description: "Creates an ActiveModel backed form with error " %>
|
3
|
+
|
4
|
+
<% content_for :preview, flush: true do %>
|
5
|
+
<div class="w-full flex justify-center">
|
6
|
+
<%= render_code_preview('forms') %>
|
7
|
+
</div>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<% content_for :code, flush: true do %>
|
11
|
+
<%= code_partial("forms/preview", :erb) %>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<%= render_preview %>
|
15
|
+
|
16
|
+
<h2 class="font-heading scroll-m-20 border-b pb-2 text-2xl font-semibold tracking-tight first:mt-0" id="installation">Installation</h2>
|
17
|
+
<%= code_sample(language: "sh") do %>
|
18
|
+
rails generate shadcn-ui forms
|
19
|
+
<% end %>
|
20
|
+
|
21
|
+
<h2 class="font-heading mt-12 scroll-m-20 border-b pb-2 mb-2 text-2xl font-semibold tracking-tight first:mt-0" id="usage">Usage</h2>
|
22
|
+
<%= code_partial("forms/usage", :erb) %>
|
23
|
+
<%= render_usage("forms") %>
|
@@ -1,3 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
<%= render_toast header: "Scheduled: Catch up",
|
2
|
+
description: "Friday, February 10, 2023 at 5:57 PM",
|
3
|
+
action: render_button("Undo", variant: :outline),
|
4
|
+
id: "default_toast" %>
|
@@ -2,8 +2,10 @@
|
|
2
2
|
description: "A succinct message that is displayed temporarily." %>
|
3
3
|
|
4
4
|
<% content_for :preview, flush: true do %>
|
5
|
-
|
6
|
-
|
5
|
+
<div class="w-full flex justify-center">
|
6
|
+
<%= render_button "Trigger Toast", variant: "outline", data: {controller: "ui--toast", action: "ui--toast#trigger", target: "#default_toast"} %>
|
7
|
+
<%= render_code_preview('toast') %>
|
8
|
+
</div>
|
7
9
|
<% end %>
|
8
10
|
|
9
11
|
<% content_for :code, flush: true do %>
|
@@ -21,3 +23,16 @@
|
|
21
23
|
<%= code_partial("toast/usage", :erb) %>
|
22
24
|
|
23
25
|
<%= render_usage("toast") %>
|
26
|
+
|
27
|
+
<% content_for :examples, flush: true do %>
|
28
|
+
<div class="w-full flex justify-center">
|
29
|
+
<%= render_button "Trigger Destructive Toast", variant: "outline", data: {controller: "ui--toast", action: "ui--toast#trigger", target: "#alert_toast"} %>
|
30
|
+
<%= render "examples/components/toast/code/destructive" %>
|
31
|
+
</div>
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<% content_for :code, flush: true do %>
|
35
|
+
<%= code_partial("toast/destructive", :erb) %>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<%= render_example %>
|
@@ -18,7 +18,7 @@
|
|
18
18
|
<div class="grid grid-flow-row auto-rows-max text-sm">
|
19
19
|
<%= sidebar_link "✅ 📖 Accordion", "/docs/components/accordion" %>
|
20
20
|
<%= sidebar_link "✅ 📖 Alert", "/docs/components/alert" %>
|
21
|
-
|
21
|
+
<%= sidebar_link "✅ 📖 Alert Dialog", "/docs/components/alert-dialog" %>
|
22
22
|
<%#= sidebar_link "❌ Aspect Ratio", "/docs/components/aspect-ratio" %>
|
23
23
|
<%#= sidebar_link "❌ Avatar", "/docs/components/avatar" %>
|
24
24
|
<%= sidebar_link "✅ 📖 Badge", "/docs/components/badge" %>
|
@@ -34,8 +34,8 @@
|
|
34
34
|
<%#= sidebar_link "Date Picker", "/docs/components/date-picker" %>
|
35
35
|
<%= sidebar_link "✅ 📖 Dialog", "/docs/components/dialog" %>
|
36
36
|
<%= sidebar_link "✅ 📖 Dropdown Menu", "/docs/components/dropdown-menu" %>
|
37
|
-
<%= sidebar_link "
|
38
|
-
|
37
|
+
<%= sidebar_link "✅ 📖 Filter", "/docs/components/filter" %>
|
38
|
+
<%= sidebar_link "✅ 📖 Forms", "/docs/components/forms" %>
|
39
39
|
<%= sidebar_link "✅ 📖 Hover Card", "/docs/components/hover-card" %>
|
40
40
|
<%= sidebar_link "✅ 📖 Input", "/docs/components/input" %>
|
41
41
|
<%= sidebar_link "✅ 📖 Label", "/docs/components/label" %>
|
data/config/routes.rb
CHANGED
@@ -8,6 +8,9 @@ Rails.application.routes.draw do
|
|
8
8
|
get "/docs/:id" => "documentation#show", :as => :documentation, :format => false
|
9
9
|
get "/docs", to: "documentation#index", as: :documentation_index
|
10
10
|
|
11
|
+
# This is just for the forms component
|
12
|
+
resources :users
|
13
|
+
|
11
14
|
# Defines the root path route ("/")
|
12
15
|
root "application#index"
|
13
16
|
end
|
data/lib/components.json
CHANGED
@@ -24,7 +24,8 @@
|
|
24
24
|
"app/helpers/components/alert-dialog_helper.rb",
|
25
25
|
"app/views/components/ui/_alert-dialog.html.erb",
|
26
26
|
"app/javascript/controllers/ui/alert-dialog_controller.js"
|
27
|
-
]
|
27
|
+
],
|
28
|
+
"dependencies": [{ "component": "dialog" }]
|
28
29
|
},
|
29
30
|
"badge": {
|
30
31
|
"name": "badge",
|
@@ -111,6 +112,17 @@
|
|
111
112
|
"app/views/components/ui/shared/_menu_item.html.erb"
|
112
113
|
]
|
113
114
|
},
|
115
|
+
"forms": {
|
116
|
+
"name": "forms",
|
117
|
+
"type": "components:ui",
|
118
|
+
"files": ["app/helpers/components/forms_helper.rb", "app/form_builders/shadcn_form_builder.rb"],
|
119
|
+
"dependencies": [
|
120
|
+
{ "component": "input" },
|
121
|
+
{ "component": "label" },
|
122
|
+
{ "component": "textarea" },
|
123
|
+
{ "component": "button" }
|
124
|
+
]
|
125
|
+
},
|
114
126
|
"hover-card": {
|
115
127
|
"name": "hover-card",
|
116
128
|
"type": "components:ui",
|
@@ -67,7 +67,7 @@ class ShadcnUiGenerator < Rails::Generators::Base
|
|
67
67
|
|
68
68
|
available_components.each do |component, _|
|
69
69
|
description = "# A #{component} component"
|
70
|
-
banner_line = "rails
|
70
|
+
banner_line = "rails generate shadcn-ui #{component}:install #{" " * (20 - component.length)} #{description}"
|
71
71
|
puts banner_line
|
72
72
|
end
|
73
73
|
end
|
data/lib/shadcn-ui/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shadcn-ui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Avi Flombaum
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-07-
|
11
|
+
date: 2023-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: This gem is a documentation site and gem that will copy components from
|
14
14
|
the shadcn-ui library into a Ruby on Rails application.
|
@@ -44,6 +44,8 @@ files:
|
|
44
44
|
- app/controllers/components_controller.rb
|
45
45
|
- app/controllers/concerns/.keep
|
46
46
|
- app/controllers/documentation_controller.rb
|
47
|
+
- app/controllers/users_controller.rb
|
48
|
+
- app/form_builders/shadcn_form_builder.rb
|
47
49
|
- app/helpers/application_helper.rb
|
48
50
|
- app/helpers/components/accordion_helper.rb
|
49
51
|
- app/helpers/components/alert_dialog_helper.rb
|
@@ -56,6 +58,7 @@ files:
|
|
56
58
|
- app/helpers/components/dialog_helper.rb
|
57
59
|
- app/helpers/components/dropdown_menu_helper.rb
|
58
60
|
- app/helpers/components/filter_helper.rb
|
61
|
+
- app/helpers/components/forms_helper.rb
|
59
62
|
- app/helpers/components/hover_card_helper.rb
|
60
63
|
- app/helpers/components/input_helper.rb
|
61
64
|
- app/helpers/components/label_helper.rb
|
@@ -98,6 +101,7 @@ files:
|
|
98
101
|
- app/mailers/application_mailer.rb
|
99
102
|
- app/models/application_record.rb
|
100
103
|
- app/models/concerns/.keep
|
104
|
+
- app/models/user.rb
|
101
105
|
- app/views/application/index.html.erb
|
102
106
|
- app/views/components/ui/_accordion.html.erb
|
103
107
|
- app/views/components/ui/_alert.html.erb
|
@@ -143,6 +147,7 @@ files:
|
|
143
147
|
- app/views/examples/components/accordion/code/_preview.erb
|
144
148
|
- app/views/examples/components/accordion/code/_usage.erb
|
145
149
|
- app/views/examples/components/alert-dialog.html.erb
|
150
|
+
- app/views/examples/components/alert-dialog/_usage.html.erb
|
146
151
|
- app/views/examples/components/alert-dialog/code/_preview.erb
|
147
152
|
- app/views/examples/components/alert-dialog/code/_usage.erb
|
148
153
|
- app/views/examples/components/alert.html.erb
|
@@ -191,6 +196,10 @@ files:
|
|
191
196
|
- app/views/examples/components/filter/code/_icon.html.erb
|
192
197
|
- app/views/examples/components/filter/code/_preview.erb
|
193
198
|
- app/views/examples/components/filter/code/_usage.erb
|
199
|
+
- app/views/examples/components/forms.html.erb
|
200
|
+
- app/views/examples/components/forms/_usage.html.erb
|
201
|
+
- app/views/examples/components/forms/code/_preview.erb
|
202
|
+
- app/views/examples/components/forms/code/_usage.erb
|
194
203
|
- app/views/examples/components/hover-card.html.erb
|
195
204
|
- app/views/examples/components/hover-card/_usage.html.erb
|
196
205
|
- app/views/examples/components/hover-card/code/_preview.erb
|
@@ -237,6 +246,7 @@ files:
|
|
237
246
|
- app/views/examples/components/textarea/code/_usage.erb
|
238
247
|
- app/views/examples/components/toast.html.erb
|
239
248
|
- app/views/examples/components/toast/_usage.html.erb
|
249
|
+
- app/views/examples/components/toast/code/_destructive.erb
|
240
250
|
- app/views/examples/components/toast/code/_preview.erb
|
241
251
|
- app/views/examples/components/toast/code/_usage.erb
|
242
252
|
- app/views/examples/components/toggle.html.erb
|