protos 0.4.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +159 -58
- data/lib/protos/accordion/item.rb +7 -7
- data/lib/protos/accordion.rb +14 -4
- data/lib/protos/alert/actions.rb +6 -0
- data/lib/protos/attributes.rb +1 -18
- data/lib/protos/breadcrumbs.rb +6 -0
- data/lib/protos/collapse/title.rb +6 -5
- data/lib/protos/collapse.rb +15 -4
- data/lib/protos/command/input.rb +1 -1
- data/lib/protos/command/title.rb +1 -1
- data/lib/protos/component.rb +2 -2
- data/lib/protos/drawer/side.rb +6 -2
- data/lib/protos/drawer/trigger.rb +2 -2
- data/lib/protos/drawer.rb +13 -4
- data/lib/protos/mix.rb +39 -0
- data/lib/protos/popover/trigger.rb +3 -2
- data/lib/protos/swap.rb +7 -1
- data/lib/protos/tabs/tab.rb +3 -14
- data/lib/protos/toast/close_button.rb +1 -0
- data/lib/protos/version.rb +1 -1
- data/lib/protos.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0796561a80e75fb39f8b35ec75257104adc90b21227ef1525876576d10415269'
|
4
|
+
data.tar.gz: 394a8ee190d1d2647476db45d2d9b9edb156bc42a3935898d6f1deca35261326
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59e2288d2ae8adfc3b352838dda25c776edddf48d99b310181629e78f09831552be23e36adedaca7bf3601678cb08950500f4b72f2ed609324ccd72ddd32a92d
|
7
|
+
data.tar.gz: e0e4beda7e4cbff4e3b669899b65df2ec7c37fe388e75b561418ce1cc65abd4cb1cfecabd743f964bf0dee98de1c4ccdbf9f7e76f77078f294a8bfb0a4272802
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.6.0] - 2024-09-04
|
4
|
+
|
5
|
+
- Changes how merging attributes works to only mix on certain attributes,
|
6
|
+
overriding on all others. This is opposite to how attributes used to be merged
|
7
|
+
by default. This is a fix for attributes like `value` where you actually need
|
8
|
+
to override them.
|
9
|
+
- Adds tests for all Rails components
|
10
|
+
- Adds a separate tested `Mix` class for handling attribute merging
|
11
|
+
|
12
|
+
## [0.5.0] - 2024-08-27
|
13
|
+
|
14
|
+
- Fixes all accessibility violations according to Axe Core
|
15
|
+
- Reduces responsibility of Tabs to only be a tablist, no tab panels
|
16
|
+
- Fixes passing ID to popovers, dropdowns, drawers, etc to not override the
|
17
|
+
input ID
|
18
|
+
- Changes trigger on popover to be a button instead of a div
|
19
|
+
|
3
20
|
## [0.4.3] - 2024-08-14
|
4
21
|
|
5
22
|
- Removes unneeded auto-loading in Rails which fixes collisions with `protos-markdown`
|
data/README.md
CHANGED
@@ -20,10 +20,90 @@ Other Phlex based UI libraries worth checking out:
|
|
20
20
|
- [PhlexUI](https://phlexui.com/)
|
21
21
|
- [ZestUI](https://zestui.com/)
|
22
22
|
|
23
|
+
Thinking of making your next static site using Phlex? Check out
|
24
|
+
[staticky](https://github.com/nolantait/staticky). The protos docs were
|
25
|
+
published using it.
|
26
|
+
|
27
|
+
## Phlex components
|
28
|
+
|
29
|
+
Phlex is a fantastic framework for building frontend components in pure Ruby:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class Navbar
|
33
|
+
def view_template
|
34
|
+
header(class: "flex items-center justify-between") do
|
35
|
+
h3 { "My site" }
|
36
|
+
button { "Log out" }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
But how can we sometimes render this `Navbar` with a different background color?
|
43
|
+
|
44
|
+
It would be nice to have our components take a class like any other element:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
render Navbar.new(class: "bg-primary")
|
48
|
+
```
|
49
|
+
|
50
|
+
Unfortunately `class` is a special keyword in Ruby, so we need to do some
|
51
|
+
awkward handling to use it like this:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class Navbar
|
55
|
+
def initialize(**options)
|
56
|
+
# Keyword `class` is a special word in Ruby so we have to awkwardly unwrap
|
57
|
+
# like this instead of using keyword arguments
|
58
|
+
@classes = options[:class]
|
59
|
+
end
|
60
|
+
|
61
|
+
def view_template
|
62
|
+
header(class: "#{@classes} flex items-center justify-between") do
|
63
|
+
h3 { "My site" }
|
64
|
+
button { "Log out" }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Now we can pass in a style to our container, but what about overriding the style
|
71
|
+
of the `h3` tag?
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class Navbar
|
75
|
+
def initialize(**options)
|
76
|
+
# Keyword `class` is a special word in Ruby so we have to awkwardly unwrap
|
77
|
+
# like this instead of using keyword arguments
|
78
|
+
@container_classes = options[:class]
|
79
|
+
@title_classes = options[:title_class]
|
80
|
+
end
|
81
|
+
|
82
|
+
def view_template
|
83
|
+
header(class: "#{@classes} flex items-center justify-between") do
|
84
|
+
h3(class: @title_classes) { "My site" }
|
85
|
+
button { "Log out" }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
Eventually everyone makes a kind of ad-hoc system for specifying styles. It gets
|
92
|
+
more complicated when you have attributes like a data-controller. How do you
|
93
|
+
give a good experience letting people using your components to add their own
|
94
|
+
controllers while your component depends on one already?
|
95
|
+
|
96
|
+
This library is an attempt to make this kind of developer experience while
|
97
|
+
making reusable components more convention over configuration.
|
98
|
+
|
23
99
|
## Protos::Component
|
24
100
|
|
25
|
-
A protos component follows
|
26
|
-
components in your app
|
101
|
+
A protos component follows 3 conventions that make them easy to work with as
|
102
|
+
components in your app:
|
103
|
+
|
104
|
+
- [Slots and themes](#slots-and-themes)
|
105
|
+
- [Attrs and default attrs](#attrs-and-default-attrs)
|
106
|
+
- [Params and options](#params-and-options)
|
27
107
|
|
28
108
|
Every UI component library will have a tension between being too general to fit
|
29
109
|
in your app or too narrow to be useful. Making components that look good out of
|
@@ -32,20 +112,18 @@ the box can make them hard to customize.
|
|
32
112
|
We try and resolve this tension by making these components have a minimal style
|
33
113
|
that can be easily overridden using some ergonomic conventions.
|
34
114
|
|
35
|
-
There are 3 core conventions:
|
36
|
-
- [Slots and themes](#slots-and-themes)
|
37
|
-
- [Attrs and default attrs](#attrs-and-default-attrs)
|
38
|
-
- [Params and options](#params-and-options)
|
39
|
-
|
40
115
|
### Slots and themes
|
41
116
|
|
42
|
-
Components are styled with `css` slots that
|
43
|
-
a `theme`.
|
117
|
+
Components are styled with `css` slots that get their values from a simple hash
|
118
|
+
we call a `theme`.
|
119
|
+
|
120
|
+
You define a `theme` for your component by defining a `#theme` method that
|
121
|
+
returns a hash.
|
122
|
+
|
123
|
+
Users of your components can override, merge, or remove parts of your theme by
|
124
|
+
passing in their own as an argument to the component. Another nice benefit is
|
125
|
+
that your markup doesn't get overwhelmed horizontally with your css classes.
|
44
126
|
|
45
|
-
You define a theme for your component by defining a `#theme` method that returns
|
46
|
-
a hash. This hash will be merged with any theme provided when rendering your
|
47
|
-
component. This allows you to easily override styles for your components
|
48
|
-
depending on their context.
|
49
127
|
|
50
128
|
```ruby
|
51
129
|
class List < Protos::Component
|
@@ -58,7 +136,7 @@ class List < Protos::Component
|
|
58
136
|
|
59
137
|
def theme
|
60
138
|
{
|
61
|
-
list:
|
139
|
+
list: "space-y-4", # We can use `#tokens` from phlex (recommended)
|
62
140
|
item: "font-bold text-2xl" # Or just plain old strings
|
63
141
|
}
|
64
142
|
end
|
@@ -66,7 +144,10 @@ end
|
|
66
144
|
```
|
67
145
|
|
68
146
|
Using a theme and css slots allows us to easily override any part of a component
|
69
|
-
when we render
|
147
|
+
when we render.
|
148
|
+
|
149
|
+
Here we are passing in our own theme. The default behavior is to add these
|
150
|
+
styles on to the theme, rather than replacing them.
|
70
151
|
|
71
152
|
```ruby
|
72
153
|
render List.new(
|
@@ -77,7 +158,12 @@ render List.new(
|
|
77
158
|
)
|
78
159
|
```
|
79
160
|
|
80
|
-
|
161
|
+
When the component is rendered the `tailwind_merge` gem will also prune any
|
162
|
+
duplicate unneeded styles.
|
163
|
+
|
164
|
+
For example even though the themes `list` key would be added together to become
|
165
|
+
`space-y-4 space-y-8`, the `tailwind_merge` gem will prune it down to just
|
166
|
+
`space-y-8` as the two styles conflict.
|
81
167
|
|
82
168
|
```html
|
83
169
|
<ul class="space-y-8">
|
@@ -119,8 +205,8 @@ The new `css[:item]` slot would be:
|
|
119
205
|
<li class="font-bold">Item 1</li>
|
120
206
|
```
|
121
207
|
|
122
|
-
If you want to change the method we define our default theme you can
|
123
|
-
`theme_method` on the class:
|
208
|
+
If you want to change the method we define our default theme under you can
|
209
|
+
override the `theme_method` on the class:
|
124
210
|
|
125
211
|
```ruby
|
126
212
|
class List < Protos::Component
|
@@ -142,12 +228,15 @@ end
|
|
142
228
|
### Attrs and default attrs
|
143
229
|
|
144
230
|
By convention, all components spread in an `attrs` hash on their outermost
|
145
|
-
element of the component.
|
231
|
+
element of the component. There is no rule for this, but it makes them feel more
|
232
|
+
naturally like native html elements when you render them.
|
146
233
|
|
147
|
-
By doing this we enable
|
234
|
+
By doing this we enable 3 main conveniences:
|
148
235
|
1. We can pass a `class` keyword when initializing the component which will be
|
149
236
|
merged safely into the `css[:container]` slot
|
150
|
-
2. We can
|
237
|
+
2. We can pass any html attributes we want to the element such as `id`, `data`
|
238
|
+
etc and it will just work
|
239
|
+
3. We can add default attributes that are safely merged with any provided to the
|
151
240
|
component when its being initialized
|
152
241
|
|
153
242
|
```ruby
|
@@ -175,12 +264,15 @@ class List < Protos::Component
|
|
175
264
|
end
|
176
265
|
```
|
177
266
|
|
178
|
-
`#attrs` will by default merge the `class` keyword into the
|
179
|
-
slot which we define in our theme.
|
267
|
+
`#attrs` returns a hash which will by default merge the `class` keyword into the
|
268
|
+
`css[:container]` slot which we define in our theme. The `ul` elements class
|
269
|
+
would be `space-y-4` as that is the `css[:container]` on our theme.
|
270
|
+
|
271
|
+
Special html options (`class`, `data`) will be safely merged.
|
180
272
|
|
181
|
-
|
182
|
-
|
183
|
-
|
273
|
+
For examples, the component above defines a list controller. If we passed our
|
274
|
+
own controller into data when we initialize, the component's data-controller
|
275
|
+
attribute would be appended to:
|
184
276
|
|
185
277
|
```ruby
|
186
278
|
render List.new(
|
@@ -194,6 +286,9 @@ That would output both controllers to the DOM element:
|
|
194
286
|
<ul data-controller="list tooltip">
|
195
287
|
```
|
196
288
|
|
289
|
+
This makes it very convenient to add functionality to basic components without
|
290
|
+
overriding their core behavior or having to modify/override their class.
|
291
|
+
|
197
292
|
If we wanted to, just like for our theme we can change the method from
|
198
293
|
`default_attrs` by defining the `default_attrs_method` on the class:
|
199
294
|
|
@@ -224,25 +319,27 @@ class List < Protos::Component
|
|
224
319
|
end
|
225
320
|
```
|
226
321
|
|
322
|
+
This makes our initialization declarative and easy to extend without having to
|
323
|
+
consider how to call `super` in the initializer.
|
324
|
+
|
227
325
|
The following keywords are reserved in the base class:
|
228
326
|
|
229
327
|
- `class`
|
230
328
|
- `theme`
|
231
329
|
- `html_options`
|
232
330
|
|
331
|
+
You are free to add whatever positional or keyword arguments you like as long as
|
332
|
+
they don't directly conflict with those names.
|
333
|
+
|
233
334
|
## Putting it all together
|
234
335
|
|
235
|
-
|
336
|
+
Lets revisit the example of our `Navbar` component:
|
236
337
|
|
237
338
|
```ruby
|
238
339
|
require "protos"
|
239
340
|
|
240
341
|
class Navbar < Protos::Component
|
241
342
|
def view_template
|
242
|
-
# **attrs will add:
|
243
|
-
# - Any html options defined on the component initialization such as data,
|
244
|
-
# role, for, etc..
|
245
|
-
# - Class will be added to the css[:container] and applied
|
246
343
|
header(**attrs) do
|
247
344
|
h1(class: css[:heading]) { "Hello world" }
|
248
345
|
h2(class: css[:subtitle]) { "With a subtitle" }
|
@@ -259,19 +356,19 @@ class Navbar < Protos::Component
|
|
259
356
|
|
260
357
|
def theme
|
261
358
|
{
|
262
|
-
container:
|
263
|
-
|
264
|
-
|
265
|
-
"items-center",
|
266
|
-
"gap-sm"
|
267
|
-
),
|
268
|
-
heading: tokens("text-2xl", "font-bold"),
|
269
|
-
subtitle: tokens("text-base")
|
359
|
+
container: "flex justify-between items-center gap-sm",
|
360
|
+
heading: "text-2xl font-bold",
|
361
|
+
subtitle: "text-sm"
|
270
362
|
}
|
271
363
|
end
|
272
364
|
end
|
365
|
+
```
|
273
366
|
|
274
|
-
|
367
|
+
Now all the concerns about adding in our behavior, styles, etc are handled for
|
368
|
+
us by convention:
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
render Navbar.new(
|
275
372
|
# This will add to the component's css[:container] slot
|
276
373
|
class: "my-sm",
|
277
374
|
# This will add the controller and not remove
|
@@ -283,8 +380,6 @@ component = Navbar.new(
|
|
283
380
|
subtitle!: "text-xl" # We can override the entire slot
|
284
381
|
}
|
285
382
|
)
|
286
|
-
|
287
|
-
puts component.call
|
288
383
|
```
|
289
384
|
|
290
385
|
Which produces the following html:
|
@@ -308,7 +403,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
308
403
|
|
309
404
|
## Usage
|
310
405
|
|
311
|
-
Setup [
|
406
|
+
Setup [TailwindCSS](https://tailwindcss.com/), [DaisyUI](https://daisyui.com)
|
312
407
|
and add the protos path to your content.
|
313
408
|
|
314
409
|
```
|
@@ -316,7 +411,7 @@ npm install -D tailwindcss postcss autoprefixer daisyui
|
|
316
411
|
npx tailwindcss init
|
317
412
|
```
|
318
413
|
|
319
|
-
Then we need to add the protos path to the `content` of our
|
414
|
+
Then we need to add the protos path to the `content` of our tailwind config
|
320
415
|
so tailwind will read the styles defined in the Protos gem.
|
321
416
|
|
322
417
|
Protos also uses semantic spacing such as `p-sm` or `m-md` instead of set
|
@@ -344,15 +439,6 @@ module.exports = {
|
|
344
439
|
lg: "var(--spacing-lg)",
|
345
440
|
xl: "var(--spacing-xl)",
|
346
441
|
},
|
347
|
-
// If you use % based spacing you might want different spacing
|
348
|
-
// for any vertical gaps to prevent overflow
|
349
|
-
gap: {
|
350
|
-
xs: "var(--spacing-gap-xs)",
|
351
|
-
sm: "var(--spacing-gap-sm)",
|
352
|
-
md: "var(--spacing-gap-md)",
|
353
|
-
lg: "var(--spacing-gap-lg)",
|
354
|
-
xl: "var(--spacing-gap-xl)",
|
355
|
-
},
|
356
442
|
},
|
357
443
|
}
|
358
444
|
// ....
|
@@ -388,11 +474,12 @@ end
|
|
388
474
|
|
389
475
|
## Building your own components
|
390
476
|
|
391
|
-
You can override components simply by redefining the class in your
|
477
|
+
You can override components simply by redefining sub-classing the class in your
|
478
|
+
own app:
|
392
479
|
|
393
480
|
```ruby
|
394
|
-
module
|
395
|
-
class Swap < Component
|
481
|
+
module Components
|
482
|
+
class Swap < Protos::Component
|
396
483
|
private
|
397
484
|
|
398
485
|
def on(...)
|
@@ -408,8 +495,22 @@ module Protos
|
|
408
495
|
end
|
409
496
|
```
|
410
497
|
|
411
|
-
|
412
|
-
|
498
|
+
But its much better to avoid the sub-classing and just render the component
|
499
|
+
inside of your own:
|
500
|
+
|
501
|
+
```ruby
|
502
|
+
module Components
|
503
|
+
class Swap < ApplicationComponent
|
504
|
+
def view_template
|
505
|
+
render Protos::Swap.new do |c|
|
506
|
+
# ....
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
```
|
512
|
+
|
513
|
+
You could use `Proto::List` to create your own list and even use
|
413
514
|
`Phlex::DeferredRender` to make the API more convenient.
|
414
515
|
|
415
516
|
Let's create a list component with headers and actions:
|
@@ -5,16 +5,16 @@ module Protos
|
|
5
5
|
class Item < Component
|
6
6
|
# DOCS: An accorion is just a collapse with radio buttons.
|
7
7
|
|
8
|
-
option :
|
8
|
+
option :input_id, type: Types::String | Types::Integer
|
9
9
|
|
10
10
|
def view_template(&block)
|
11
11
|
li(**attrs) do
|
12
|
-
render Collapse.new(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
render Collapse.new(
|
13
|
+
theme: collapse_theme,
|
14
|
+
input_id: @input_id.to_s,
|
15
|
+
input_type: :radio,
|
16
|
+
&block
|
17
|
+
)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
data/lib/protos/accordion.rb
CHANGED
@@ -7,20 +7,30 @@ module Protos
|
|
7
7
|
# to be open at the same time, use the collapse component.
|
8
8
|
# https://daisyui.com/components/accordion/
|
9
9
|
|
10
|
-
option :id, default: -> { "collapse-#{SecureRandom.hex(4)}" }
|
11
|
-
|
12
10
|
def view_template(&)
|
13
11
|
ul(**attrs, &)
|
14
12
|
end
|
15
13
|
|
16
|
-
def item(*,
|
14
|
+
def item(*, input_id: nil, **, &)
|
15
|
+
self.current_input_id = input_id
|
16
|
+
|
17
|
+
render Item.new(*, input_id: current_input_id, **, &)
|
18
|
+
end
|
17
19
|
|
18
20
|
def content(...) = render Collapse::Content.new(...)
|
19
21
|
|
20
|
-
def title(*, **, &)
|
22
|
+
def title(*, **, &)
|
23
|
+
render Collapse::Title.new(*, input_id: current_input_id, **, &)
|
24
|
+
end
|
21
25
|
|
22
26
|
private
|
23
27
|
|
28
|
+
attr_reader :current_input_id
|
29
|
+
|
30
|
+
def current_input_id=(value)
|
31
|
+
@current_input_id = value || "collapse-#{SecureRandom.hex(4)}"
|
32
|
+
end
|
33
|
+
|
24
34
|
def theme
|
25
35
|
{
|
26
36
|
container: "join join-vertical"
|
data/lib/protos/alert/actions.rb
CHANGED
data/lib/protos/attributes.rb
CHANGED
@@ -4,11 +4,6 @@ module Protos
|
|
4
4
|
class Attributes
|
5
5
|
# DOCS: A class that represents the attributes of a component. This would be
|
6
6
|
# all html options except for `class` and `theme`.
|
7
|
-
#
|
8
|
-
# This class is responsible for safely merging in both user supplied and
|
9
|
-
# default attributes. When a user adds { data: { controller: "foo" }} to
|
10
|
-
# their component. This will merge the value in so that any default
|
11
|
-
# controllers do not get overridden.
|
12
7
|
|
13
8
|
def initialize(attrs = {}, **kwargs)
|
14
9
|
@attrs = attrs.merge!(kwargs)
|
@@ -34,19 +29,7 @@ module Protos
|
|
34
29
|
private
|
35
30
|
|
36
31
|
def mix(hash, *hashes)
|
37
|
-
|
38
|
-
result.merge!(hash) do |_key, a, b| # rubocop:disable Metrics/ParameterLists
|
39
|
-
next a unless b
|
40
|
-
next a if a == b
|
41
|
-
|
42
|
-
case [a, b]
|
43
|
-
in String, String then "#{a} #{b}"
|
44
|
-
in Array, Array then a + b
|
45
|
-
in Hash, Hash then mix(a, b)
|
46
|
-
else b
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
32
|
+
Mix.call(hash, *hashes)
|
50
33
|
end
|
51
34
|
end
|
52
35
|
end
|
data/lib/protos/breadcrumbs.rb
CHANGED
@@ -5,14 +5,15 @@ module Protos
|
|
5
5
|
class Title < Component
|
6
6
|
# DOCS: The title of a collapse. This is the content that is always
|
7
7
|
# visible and is used to toggle the collapse.
|
8
|
-
|
9
|
-
|
8
|
+
|
9
|
+
option :input_id,
|
10
|
+
type: Types::String | Types::Integer | Types::Nil,
|
10
11
|
reader: false,
|
11
|
-
default: -> {
|
12
|
+
default: -> { }
|
12
13
|
|
13
14
|
def view_template(&)
|
14
|
-
if @
|
15
|
-
label(for: @
|
15
|
+
if @input_id
|
16
|
+
label(for: @input_id.to_s, **attrs, &)
|
16
17
|
else
|
17
18
|
div(**attrs, &)
|
18
19
|
end
|
data/lib/protos/collapse.rb
CHANGED
@@ -6,19 +6,30 @@ module Protos
|
|
6
6
|
# is visible at all times, and the content is only visible when expanded.
|
7
7
|
# https://daisyui.com/components/collapse/
|
8
8
|
|
9
|
-
option :
|
10
|
-
option :
|
9
|
+
option :input_type, default: -> { :checkbox }, reader: false
|
10
|
+
option :input_id,
|
11
|
+
reader: false,
|
11
12
|
default: -> { "collapse-#{SecureRandom.hex(4)}" },
|
12
13
|
type: Types::String
|
13
14
|
|
14
15
|
def view_template
|
15
16
|
div(**attrs) do
|
16
|
-
|
17
|
+
if @input_type
|
18
|
+
input(
|
19
|
+
type: @input_type,
|
20
|
+
id: @input_id,
|
21
|
+
name: @input_id,
|
22
|
+
autocomplete: :off,
|
23
|
+
# form: "" prevents the radio button from being submitted if its
|
24
|
+
# within a form
|
25
|
+
form: ""
|
26
|
+
)
|
27
|
+
end
|
17
28
|
yield if block_given?
|
18
29
|
end
|
19
30
|
end
|
20
31
|
|
21
|
-
def title(*, **, &) = render Title.new(*,
|
32
|
+
def title(*, **, &) = render Title.new(*, input_id: @input_id, **, &)
|
22
33
|
|
23
34
|
def content(...) = render Content.new(...)
|
24
35
|
|
data/lib/protos/command/input.rb
CHANGED
data/lib/protos/command/title.rb
CHANGED
data/lib/protos/component.rb
CHANGED
@@ -27,14 +27,14 @@ module Protos
|
|
27
27
|
option :html_options, default: -> { {} }, reader: false
|
28
28
|
|
29
29
|
# Adds non-defined options to the html_options hash
|
30
|
-
def initialize(
|
30
|
+
def initialize(*, **kwargs, &)
|
31
31
|
defined_keys = self.class.dry_initializer.definitions.keys
|
32
32
|
defined, undefined =
|
33
33
|
kwargs
|
34
34
|
.partition { |key, _| defined_keys.include?(key) }
|
35
35
|
.map!(&:to_h)
|
36
36
|
|
37
|
-
super(
|
37
|
+
super(*, html_options: undefined, **defined, &)
|
38
38
|
end
|
39
39
|
|
40
40
|
private
|
data/lib/protos/drawer/side.rb
CHANGED
@@ -6,11 +6,15 @@ module Protos
|
|
6
6
|
# DOCS: The content that will be shown when you open the drawer using the
|
7
7
|
# trigger. This would be hidden until triggered.
|
8
8
|
|
9
|
-
option :
|
9
|
+
option :input_id, reader: false, type: Types::String
|
10
10
|
|
11
11
|
def view_template
|
12
12
|
aside(**attrs) do
|
13
|
-
label(
|
13
|
+
label(
|
14
|
+
for: @input_id,
|
15
|
+
aria_label: "close sidebar",
|
16
|
+
class: css[:toggle]
|
17
|
+
)
|
14
18
|
yield if block_given?
|
15
19
|
end
|
16
20
|
end
|
@@ -6,10 +6,10 @@ module Protos
|
|
6
6
|
# DOCS: The trigger for a drawer. When this is clicked the side will open
|
7
7
|
# and overlap the main content with a darker overlay.
|
8
8
|
|
9
|
-
option :
|
9
|
+
option :input_id, reader: false, type: Types::String
|
10
10
|
|
11
11
|
def view_template(&)
|
12
|
-
label(for:
|
12
|
+
label(for: @input_id, **attrs, &)
|
13
13
|
end
|
14
14
|
|
15
15
|
private
|
data/lib/protos/drawer.rb
CHANGED
@@ -7,20 +7,29 @@ module Protos
|
|
7
7
|
# trigger is clicked.
|
8
8
|
# https://daisyui.com/components/drawer/
|
9
9
|
|
10
|
-
option :id,
|
10
|
+
option :id,
|
11
|
+
reader: false,
|
12
|
+
type: Types::Coercible::String,
|
13
|
+
default: -> { "drawer-#{SecureRandom.hex(4)}" }
|
11
14
|
|
12
15
|
def view_template
|
13
16
|
div(**attrs) do
|
14
|
-
input(
|
17
|
+
input(
|
18
|
+
id: @id,
|
19
|
+
type: :checkbox,
|
20
|
+
class: css[:toggle],
|
21
|
+
autocomplete: :off,
|
22
|
+
form: ""
|
23
|
+
)
|
15
24
|
yield if block_given?
|
16
25
|
end
|
17
26
|
end
|
18
27
|
|
19
28
|
def content(...) = render Content.new(...)
|
20
29
|
|
21
|
-
def side(*, **, &) = render Side.new(*, id
|
30
|
+
def side(*, **, &) = render Side.new(*, input_id: @id, **, &)
|
22
31
|
|
23
|
-
def trigger(*, **, &) = render Trigger.new(*, id
|
32
|
+
def trigger(*, **, &) = render Trigger.new(*, input_id: @id, **, &)
|
24
33
|
|
25
34
|
private
|
26
35
|
|
data/lib/protos/mix.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Protos
|
4
|
+
class Mix
|
5
|
+
# DOCS: This class is responsible for safely merging in both user supplied
|
6
|
+
# and default attributes. When a user adds { data: { controller: "foo" }} to
|
7
|
+
# their component. This will merge the value in so that any default
|
8
|
+
# controllers do not get overridden.
|
9
|
+
|
10
|
+
MERGEABLE_ATTRIBUTES = Set.new(%i[class data]).freeze
|
11
|
+
|
12
|
+
def self.call(...) = new.call(...)
|
13
|
+
|
14
|
+
def call(old_hash, *hashes)
|
15
|
+
hashes
|
16
|
+
.compact
|
17
|
+
.each_with_object(old_hash) do |new_hash, result|
|
18
|
+
merge(result, new_hash, top_level: true)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def merge(old_hash, new_hash, top_level: false) # rubocop:disable Metrics/PerceivedComplexity
|
25
|
+
old_hash.merge!(new_hash) do |key, old, new|
|
26
|
+
next old unless new
|
27
|
+
next old if old == new
|
28
|
+
next new if top_level && !MERGEABLE_ATTRIBUTES.include?(key)
|
29
|
+
|
30
|
+
case [old, new]
|
31
|
+
in String, String then "#{old} #{new}"
|
32
|
+
in Array, Array then old + new
|
33
|
+
in Hash, Hash then merge(old, new)
|
34
|
+
else new
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -7,7 +7,7 @@ module Protos
|
|
7
7
|
# or click on to show the popover.
|
8
8
|
|
9
9
|
def view_template(&)
|
10
|
-
|
10
|
+
button(**attrs, &)
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
@@ -15,7 +15,8 @@ module Protos
|
|
15
15
|
def default_attrs
|
16
16
|
{
|
17
17
|
data: { "protos--popover-target": "trigger" },
|
18
|
-
tabindex: 0
|
18
|
+
tabindex: 0,
|
19
|
+
type: :button
|
19
20
|
}
|
20
21
|
end
|
21
22
|
end
|
data/lib/protos/swap.rb
CHANGED
@@ -8,7 +8,13 @@ module Protos
|
|
8
8
|
|
9
9
|
def view_template
|
10
10
|
label(**attrs) do
|
11
|
-
input(
|
11
|
+
input(
|
12
|
+
type: :checkbox,
|
13
|
+
class: css[:input],
|
14
|
+
autocomplete: :off,
|
15
|
+
form: "",
|
16
|
+
aria_label: "swap"
|
17
|
+
)
|
12
18
|
yield if block_given?
|
13
19
|
end
|
14
20
|
end
|
data/lib/protos/tabs/tab.rb
CHANGED
@@ -5,35 +5,24 @@ module Protos
|
|
5
5
|
class Tab < Component
|
6
6
|
# DOCS: A single tab in a tabs component
|
7
7
|
|
8
|
-
option :id
|
9
|
-
option :label
|
10
8
|
option :active, default: -> { false }
|
11
9
|
option :disabled, default: -> { false }
|
12
10
|
|
13
11
|
def view_template(&)
|
14
|
-
|
15
|
-
type: :radio,
|
16
|
-
class: css[:input],
|
17
|
-
name: id,
|
18
|
-
role: :tab,
|
19
|
-
aria_label: label,
|
20
|
-
autocomplete: :off
|
21
|
-
)
|
22
|
-
div(**attrs, &)
|
12
|
+
button(**attrs, disabled:, &)
|
23
13
|
end
|
24
14
|
|
25
15
|
private
|
26
16
|
|
27
17
|
def default_attrs
|
28
18
|
{
|
29
|
-
role: :
|
19
|
+
role: :tab
|
30
20
|
}
|
31
21
|
end
|
32
22
|
|
33
23
|
def theme
|
34
24
|
{
|
35
|
-
container:
|
36
|
-
input: tokens(
|
25
|
+
container: tokens(
|
37
26
|
"tab",
|
38
27
|
disabled: "tab-disabled",
|
39
28
|
active: "tab-active"
|
data/lib/protos/version.rb
CHANGED
data/lib/protos.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nolan J Tait
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-core
|
@@ -149,6 +149,7 @@ files:
|
|
149
149
|
- lib/protos/hero/overlay.rb
|
150
150
|
- lib/protos/list.rb
|
151
151
|
- lib/protos/list/item.rb
|
152
|
+
- lib/protos/mix.rb
|
152
153
|
- lib/protos/modal.rb
|
153
154
|
- lib/protos/modal/close_button.rb
|
154
155
|
- lib/protos/modal/dialog.rb
|
@@ -220,7 +221,7 @@ requirements:
|
|
220
221
|
- tailwindcss
|
221
222
|
- daisyui
|
222
223
|
- protos-stimulus
|
223
|
-
rubygems_version: 3.5.
|
224
|
+
rubygems_version: 3.5.18
|
224
225
|
signing_key:
|
225
226
|
specification_version: 4
|
226
227
|
summary: A UI component library built with phlex and daisyui
|