shadcn-ui 0.0.1 → 0.0.2

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/.prettierrc.json +6 -0
  3. data/README.md +260 -0
  4. data/app/assets/stylesheets/application.tailwind.css +101 -0
  5. data/app/controllers/components_controller.rb +1 -1
  6. data/app/controllers/documentation_controller.rb +9 -0
  7. data/app/helpers/application_helper.rb +19 -0
  8. data/app/helpers/components/card_helper.rb +1 -1
  9. data/app/helpers/components/filter_helper.rb +12 -0
  10. data/app/helpers/components/input_helper.rb +21 -0
  11. data/app/helpers/components/label_helper.rb +5 -0
  12. data/app/helpers/components/list_helper.rb +15 -0
  13. data/app/helpers/components/progress_helper.rb +5 -0
  14. data/app/helpers/components/sheet_helper.rb +20 -0
  15. data/app/helpers/components/skeleton_helper.rb +5 -0
  16. data/app/helpers/components/slider_helper.rb +5 -0
  17. data/app/helpers/documentation_helper.rb +2 -0
  18. data/app/javascript/controllers/theme_controller.js +25 -0
  19. data/app/javascript/controllers/ui/dialog_controller.js +3 -1
  20. data/app/javascript/controllers/ui/dropdown_controller.js +2 -22
  21. data/app/javascript/controllers/ui/filter_controller.js +20 -0
  22. data/app/javascript/controllers/ui/popover_controller.js +29 -1
  23. data/app/javascript/controllers/ui/sheet_controller.js +33 -0
  24. data/app/javascript/controllers/ui/slider_controller.js +14 -0
  25. data/app/javascript/controllers/ui/tooltip_controller.js +1 -1
  26. data/app/views/application/index.html.erb +122 -0
  27. data/app/views/components/ui/_alert_dialog.html.erb +1 -1
  28. data/app/views/components/ui/_card.html.erb +2 -2
  29. data/app/views/components/ui/_checkbox.html.erb +1 -6
  30. data/app/views/components/ui/_command.html.erb +0 -0
  31. data/app/views/components/ui/_dialog.html.erb +1 -1
  32. data/app/views/components/ui/_filter.html.erb +14 -0
  33. data/app/views/components/ui/_input.html.erb +8 -0
  34. data/app/views/components/ui/_label.html.erb +3 -0
  35. data/app/views/components/ui/_list.html.erb +5 -0
  36. data/app/views/components/ui/_progress.html.erb +15 -0
  37. data/app/views/components/ui/_sheet.html.erb +44 -0
  38. data/app/views/components/ui/_skeleton.html.erb +1 -0
  39. data/app/views/components/ui/_slider.html.erb +2 -0
  40. data/app/views/components/ui/_textarea.html.erb +1 -1
  41. data/app/views/components/ui/shared/{_dialog_background.html.erb → _backdrop.html.erb} +1 -0
  42. data/app/views/components/ui/svg/_check.html.erb +11 -0
  43. data/app/views/documentation/about.html.md +20 -0
  44. data/app/views/documentation/index.html.erb.bak +70 -0
  45. data/app/views/documentation/index.html.md +15 -0
  46. data/app/views/documentation/installation.html.md +249 -0
  47. data/app/views/examples/components/filter.html.erb +25 -0
  48. data/app/views/examples/components/input.html.erb +18 -0
  49. data/app/views/examples/components/label.html.erb +13 -0
  50. data/app/views/examples/components/progress.html.erb +12 -0
  51. data/app/views/examples/components/sheet.html.erb +19 -0
  52. data/app/views/examples/components/skeleton.html.erb +12 -0
  53. data/app/views/examples/components/slider.html.erb +12 -0
  54. data/app/views/layouts/application.html.erb +2 -3
  55. data/app/views/layouts/component.html.erb +2 -2
  56. data/app/views/layouts/documentation.html.erb +39 -0
  57. data/app/views/layouts/shared/_components.html.erb +61 -0
  58. data/app/views/layouts/shared/_header.html.erb +25 -33
  59. data/app/views/layouts/shared/_sidebar.html.erb +10 -0
  60. data/config/application.rb +2 -1
  61. data/config/importmap.rb +6 -6
  62. data/config/initializers/markdown.rb +24 -0
  63. data/config/routes.rb +7 -1
  64. data/lib/components.json +301 -0
  65. data/lib/generators/shadcn_ui_generator.rb +64 -15
  66. data/lib/shadcn-ui/version.rb +1 -1
  67. data/public/accordion.png +0 -0
  68. metadata +44 -4
  69. data/app/views/layouts/_sidebar.html.erb +0 -270
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c5bb322090bfc7c251b5bcfa6daaaee17f56194d345fb0f653e66b0c367194c
4
- data.tar.gz: e9b663d94ca1084d7a9351c355bc8093e78e1eb4d7af55b96f2c253824bb67ca
3
+ metadata.gz: 7a6f3c1462c5fec38163ab5cbac5af561ca2d05f3944ca41bab01649c14ac6fa
4
+ data.tar.gz: 5a7fb75ae5e0f7d0db67bf0e55fdc55e65390e5bf535b351f17d69b4b03dd23c
5
5
  SHA512:
6
- metadata.gz: 1f85cd138dd5be8981c6578b63d0c595606ed2953171efcda89202a9136bd5eb63c32cb39ad3498fd2eb35555c43779079ddcd298f4698993868a5d77dae7bbd
7
- data.tar.gz: ecc9016da1365510ed5b854999e701c294d680b87adfd2540055c7c8f767b65add7245dd4571a29e0707df1032f8232ef395679c77fe94ee5851e364c2f5181f
6
+ metadata.gz: 1923bfc2b23ab4fd10aff954938111600c1d4e820c47b87827b67c8529bf1540eb36ae058d8bb0973dce3090802855414e6b200f633d08cfeac75db698e75057
7
+ data.tar.gz: b16a329953472dee87d8851958f17e2757b1c8709e21c3e5825990486ef80c1ed1b78f459ce23c3d55645786b8173fce76ad6d5b834ecafbadf9940970330410
data/.prettierrc.json CHANGED
@@ -23,6 +23,12 @@
23
23
  "options": {
24
24
  "parser": "html"
25
25
  }
26
+ },
27
+ {
28
+ "files": "*.md.html",
29
+ "options": {
30
+ "parser": "markdown"
31
+ }
26
32
  }
27
33
  ]
28
34
  }
data/README.md CHANGED
@@ -5,8 +5,268 @@
5
5
  Accessible and customizable components that you can copy and paste into your apps. Free. Open
6
6
  Source. **Use this to build your own component library**.
7
7
 
8
+ ## About
9
+
10
+ This is **NOT** a component library. It's a collection of re-usable components that you can copy and
11
+ paste into your apps.
12
+
13
+ **What do you mean by not a component library?**
14
+
15
+ I mean you do not install it as a dependency. It is not available or distributed via npm.
16
+
17
+ Pick the components you need. Copy and paste the code into your project and customize to your needs.
18
+ The code is yours.
19
+
20
+ Use this as a reference to build your own component libraries.
21
+
8
22
  ![hero](public/og.jpg)
9
23
 
24
+ ## Alpha Usage/Installation
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
+
10
270
  ## Documentation
11
271
 
12
272
  Visit https://avi.nyc/shadcn-on-rails to view the documentation.
@@ -90,3 +90,104 @@
90
90
  @apply bg-zinc-950;
91
91
  }
92
92
  }
93
+ input[type="range"] {
94
+ display: inline-block;
95
+ vertical-align: middle;
96
+ font-size: 1em;
97
+ font-family: Arial, sans-serif;
98
+ }
99
+
100
+ /* input[type="range"]:focus,
101
+ input[type="number"]:focus {
102
+ box-shadow: 0 0 3px 1px #4b81dd;
103
+ outline: none;
104
+ } */
105
+
106
+ input[type="range"] {
107
+ -webkit-appearance: none;
108
+ margin-right: 15px;
109
+ width: 200px;
110
+ height: 7px;
111
+ background: #f4f4f5;
112
+ border-radius: 5px;
113
+ background-image: linear-gradient(#000, #000);
114
+ background-repeat: no-repeat;
115
+ }
116
+
117
+ /* Input Thumb */
118
+ input[type="range"]::-webkit-slider-thumb {
119
+ -webkit-appearance: none;
120
+ height: 20px;
121
+ width: 20px;
122
+ border-radius: 50%;
123
+ background: #fff;
124
+ cursor: ew-resize;
125
+ box-shadow: 0 0 2px 0 #000;
126
+ transition: background 0.3s ease-in-out;
127
+ }
128
+
129
+ input[type="range"]::-moz-range-thumb {
130
+ -webkit-appearance: none;
131
+ height: 20px;
132
+ width: 20px;
133
+ border-radius: 50%;
134
+ background: #fff;
135
+ cursor: ew-resize;
136
+ box-shadow: 0 0 2px 0 #000;
137
+ transition: background 0.3s ease-in-out;
138
+ }
139
+
140
+ input[type="range"]::-ms-thumb {
141
+ -webkit-appearance: none;
142
+ height: 20px;
143
+ width: 20px;
144
+ border-radius: 50%;
145
+ background: #fff;
146
+ cursor: ew-resize;
147
+ box-shadow: 0 0 2px 0 #000;
148
+ transition: background 0.3s ease-in-out;
149
+ }
150
+
151
+ /* input[type="range"]::-webkit-slider-thumb:hover {
152
+ background: #a1a1aa;
153
+ }
154
+
155
+ input[type="range"]::-moz-range-thumb:hover {
156
+ background: #a1a1aa;
157
+ }
158
+
159
+ input[type="range"]::-ms-thumb:hover {
160
+ background: #a1a1aa;
161
+ } */
162
+
163
+ input[type="range"]::-moz-range-track {
164
+ -webkit-appearance: none;
165
+ box-shadow: none;
166
+ border: none;
167
+ background: transparent;
168
+ }
169
+
170
+ input[type="range"]::-ms-track {
171
+ -webkit-appearance: none;
172
+ box-shadow: none;
173
+ border: none;
174
+ background: transparent;
175
+ }
176
+
177
+ article.documentation h1 {
178
+ @apply scroll-m-20 text-4xl font-bold tracking-tight;
179
+ }
180
+ article.documentation subtitle {
181
+ @apply text-lg text-muted-foreground;
182
+ }
183
+ article.documentation p {
184
+ @apply leading-7 [&:not(:first-child)]:mt-6;
185
+ }
186
+
187
+ article.documentation :where(code):not(:where([class~="not-prose"] *)):before {
188
+ content: "";
189
+ }
190
+
191
+ article.documentation :where(code):not(:where([class~="not-prose"] *)):after {
192
+ content: "";
193
+ }
@@ -1,4 +1,4 @@
1
- class ComponentsController < ApplicationController
1
+ class ComponentsController < ActionController::Base
2
2
  layout "component"
3
3
 
4
4
  def show
@@ -0,0 +1,9 @@
1
+ class DocumentationController < ActionController::Base
2
+ layout "documentation"
3
+ def index
4
+ end
5
+
6
+ def show
7
+ render "documentation/#{params[:id]}"
8
+ end
9
+ end
@@ -1,2 +1,21 @@
1
1
  module ApplicationHelper
2
+ def page_title
3
+ @page_title = ""
4
+ if request.path.include?("/docs/components")
5
+ component_name = params[:component].to_s.titleize
6
+ @page_title << "#{component_name} - " if component_name.present?
7
+ end
8
+ @page_title << "shadcn/ui on Rails"
9
+ @page_title
10
+ end
11
+
12
+ def sidebar_link(text, path)
13
+ classes = "group flex w-full items-center rounded-md border border-transparent px-2 py-1 hover:underline"
14
+ classes << if request.path == path
15
+ " text-foreground font-bold"
16
+ else
17
+ " text-muted-foreground"
18
+ end
19
+ link_to text, path, class: classes
20
+ end
2
21
  end
@@ -1,5 +1,5 @@
1
1
  module Components::CardHelper
2
2
  def render_card(footer: nil, title: nil, subtitle: nil, body: nil, **options, &block)
3
- render "components/ui/card", title: title, subtitle: subtitle, footer: footer, body: (block ? capture(&block) : body), block:, **options
3
+ render "components/ui/card", title: title, subtitle: subtitle, footer: footer, body: (block ? capture(&block) : body), block:, options: options
4
4
  end
5
5
  end
@@ -0,0 +1,12 @@
1
+ module Components::FilterHelper
2
+ def filter_icon(&block)
3
+ content_for :filter_icon, capture(&block), flush: true
4
+ end
5
+
6
+ def render_filter(items, **options, &block)
7
+ content_for :filter_icon, "", flush: true
8
+ content = capture(&block) if block
9
+ input_class = content_for?(:filter_icon) ? "pl-1" : ""
10
+ render "components/ui/filter", items: items, options: options, input_class: input_class, content: content
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ module Components::InputHelper
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]} "
4
+ options[:class] << case options[:style]
5
+ when :borderless
6
+ " border-0 focus-visible:outline-none focus-visible:shadow-none focus-visible:ring-transparent"
7
+ else
8
+ " focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:border-muted"
9
+ end
10
+ options.reverse_merge!(required: false, disabled: false,
11
+ readonly: false, label: false, placeholder: "Type here...")
12
+ render partial: "components/ui/input", locals: {
13
+ type:,
14
+ label:,
15
+ name:,
16
+ value:,
17
+ id:,
18
+ options: options
19
+ }
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ module Components::LabelHelper
2
+ def render_label(name:, label:, **options)
3
+ render "components/ui/label", name: name, label: label, options: options
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ module Components::ListHelper
2
+ def list_item(value:, name:, selected: false, as: :div)
3
+ content_tag as, value,
4
+ class: "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm
5
+ outline-none aria-selected:bg-accent aria-selected:text-accent-foreground hover:bg-accent hover:text-accent-foreground
6
+ data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
7
+ role: "option",
8
+ data: {value:, selected:},
9
+ aria: {selected:}
10
+ end
11
+
12
+ def render_list(items, as: :div, **options)
13
+ render "components/ui/list", items:, as:, **options
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Components::ProgressHelper
2
+ def render_progress(value:, **options)
3
+ render "components/ui/progress", value: (100 - value), **options
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ module Components::SheetHelper
2
+ def render_sheet(**options, &block)
3
+ options[:direction] ||= "left"
4
+ options[:width] ||= "w-3/4"
5
+
6
+ content_for :sheet_trigger, "", flush: true
7
+ content_for :sheet_content, "", flush: true
8
+
9
+ content = capture(&block) if block
10
+ render "components/ui/sheet", content: content, options: options
11
+ end
12
+
13
+ def sheet_trigger(&block)
14
+ content_for :sheet_trigger, capture(&block), flush: true
15
+ end
16
+
17
+ def sheet_content(&block)
18
+ content_for :sheet_content, capture(&block), flush: true
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ module Components::SkeletonHelper
2
+ def render_skeleton
3
+ render "components/ui/skeleton"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Components::SliderHelper
2
+ def render_slider(value:, id:, name:, **options)
3
+ render "components/ui/slider", value:, id:, name:, **options
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ module DocumentationHelper
2
+ end
@@ -0,0 +1,25 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ static targets = ["toggleButton"];
5
+
6
+ connect() {
7
+ this.loadThemePreference();
8
+ }
9
+
10
+ toggle() {
11
+ const isDarkMode = document.documentElement.classList.toggle("dark");
12
+ this.saveThemePreference(isDarkMode);
13
+ }
14
+
15
+ loadThemePreference() {
16
+ const isDarkMode = localStorage.getItem("themePreference") === "true";
17
+ if (isDarkMode) {
18
+ document.documentElement.classList.add("dark");
19
+ }
20
+ }
21
+
22
+ saveThemePreference(isDarkMode) {
23
+ localStorage.setItem("themePreference", isDarkMode);
24
+ }
25
+ }
@@ -2,7 +2,7 @@
2
2
  import { Controller } from "@hotwired/stimulus";
3
3
  import "@kanety/stimulus-static-actions";
4
4
 
5
- export default class extends Controller {
5
+ export default class UIDialog extends Controller {
6
6
  static targets = ["dialog", "modal", "focus", "drag", "backdrop", "closeButton"];
7
7
  static actions = [
8
8
  ["element", "keydown@window->closeByKey"],
@@ -64,6 +64,8 @@ export default class extends Controller {
64
64
  this.dispatch("closed", { detail: { target: target } });
65
65
  }
66
66
 
67
+ // Refactor Me
68
+ // This needs to be combined with the toggle method in sheet_controller
67
69
  toggleClass(visible) {
68
70
  if (visible) {
69
71
  this.dialogTarget.classList.remove("hidden");
@@ -1,25 +1,5 @@
1
1
  // Inspired By: https://github.com/stimulus-components/stimulus-dropdown/blob/master/src/index.ts
2
- import { Controller } from "@hotwired/stimulus";
2
+ import UIPopover from "controllers/ui/popover_controller";
3
3
  import { useTransition } from "stimulus-use";
4
4
 
5
- export default class extends Controller {
6
- // menuTarget: HTMLElement
7
- // toggleTransition: (event?: Event) => void
8
- // leave: (event?: Event) => void
9
- // transitioned: false
10
- // static targets = ['menu']
11
- // connect (): void {
12
- // useTransition(this, {
13
- // element: this.menuTarget
14
- // })
15
- // }
16
- // toggle (): void {
17
- // this.toggleTransition()
18
- // }
19
- // hide (event: Event): void {
20
- // // @ts-ignore
21
- // if (!this.element.contains(event.target) && !this.menuTarget.classList.contains('hidden')) {
22
- // this.leave()
23
- // }
24
- // }
25
- }
5
+ export default class extends UIPopover {}
@@ -0,0 +1,20 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class UIFilter extends Controller {
4
+ static targets = ["source", "item"];
5
+
6
+ connect() {}
7
+
8
+ filter(event) {
9
+ let lowerCaseFilterTerm = this.sourceTarget.value.toLowerCase();
10
+ let regex = new RegExp("^" + lowerCaseFilterTerm);
11
+ if (this.hasItemTarget) {
12
+ this.itemTargets.forEach((el, i) => {
13
+ let filterableKey = el.innerText.toLowerCase();
14
+
15
+ // Check for consecutive characters match using regex
16
+ el.classList.toggle("hidden", !regex.test(filterableKey));
17
+ });
18
+ }
19
+ }
20
+ }
@@ -3,7 +3,7 @@
3
3
  import { Controller } from "@hotwired/stimulus";
4
4
  import { createPopper } from "@popperjs/core";
5
5
 
6
- export default class extends Controller {
6
+ export default class UIPopover extends Controller {
7
7
  static values = {
8
8
  dismissAfter: Number,
9
9
  };
@@ -24,4 +24,32 @@ export default class extends Controller {
24
24
  ],
25
25
  });
26
26
  }
27
+
28
+ // Show the popover
29
+ show() {
30
+ this.contentTarget.classList.remove("hidden");
31
+ this.contentTarget.dataset.state = "open";
32
+ }
33
+
34
+ // Hide the popover
35
+ hide() {
36
+ this.contentTarget.classList.add("hidden");
37
+ this.contentTarget.dataset.state = "closed";
38
+ }
39
+
40
+ // Toggle the popover on demand
41
+ toggle(event) {
42
+ this.popperInstance.update();
43
+ if (this.contentTarget.classList.contains("hidden")) {
44
+ this.show();
45
+
46
+ if (this.hasDismissAfterValue) {
47
+ setTimeout(() => {
48
+ this.hide();
49
+ }, this.dismissAfterValue);
50
+ }
51
+ } else {
52
+ this.hide();
53
+ }
54
+ }
27
55
  }