lightning_ui_kit 0.1.1
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +33 -0
- data/Rakefile +5 -0
- data/app/assets/builds/lightning_ui.css +2710 -0
- data/app/assets/builds/lightning_ui.js +6 -0
- data/app/assets/builds/lightning_ui.js.map +7 -0
- data/app/assets/stylesheets/lightning_ui/application.css +1 -0
- data/app/components/lightning_ui/alert_component.html.erb +4 -0
- data/app/components/lightning_ui/alert_component.rb +16 -0
- data/app/components/lightning_ui/avatar_component.html.erb +20 -0
- data/app/components/lightning_ui/avatar_component.rb +20 -0
- data/app/components/lightning_ui/badge_component.html.erb +9 -0
- data/app/components/lightning_ui/badge_component.rb +43 -0
- data/app/components/lightning_ui/banner_component.html.erb +17 -0
- data/app/components/lightning_ui/banner_component.rb +35 -0
- data/app/components/lightning_ui/base_component.rb +9 -0
- data/app/components/lightning_ui/button_component.html.erb +13 -0
- data/app/components/lightning_ui/button_component.rb +75 -0
- data/app/components/lightning_ui/checkbox_component.html.erb +37 -0
- data/app/components/lightning_ui/checkbox_component.rb +14 -0
- data/app/components/lightning_ui/description_list/item_component.html.erb +13 -0
- data/app/components/lightning_ui/description_list/item_component.rb +7 -0
- data/app/components/lightning_ui/description_list_component.html.erb +5 -0
- data/app/components/lightning_ui/description_list_component.rb +5 -0
- data/app/components/lightning_ui/dropdown/item_component.rb +5 -0
- data/app/components/lightning_ui/dropdown_component.html.erb +35 -0
- data/app/components/lightning_ui/dropdown_component.rb +23 -0
- data/app/components/lightning_ui/input_component.html.erb +99 -0
- data/app/components/lightning_ui/input_component.rb +44 -0
- data/app/components/lightning_ui/link_component.html.erb +7 -0
- data/app/components/lightning_ui/link_component.rb +8 -0
- data/app/components/lightning_ui/modal_component.html.erb +27 -0
- data/app/components/lightning_ui/modal_component.rb +21 -0
- data/app/components/lightning_ui/pagination_component.html.erb +21 -0
- data/app/components/lightning_ui/pagination_component.rb +42 -0
- data/app/components/lightning_ui/select_component.html.erb +20 -0
- data/app/components/lightning_ui/select_component.rb +25 -0
- data/app/components/lightning_ui/sidebar_component.html.erb +1 -0
- data/app/components/lightning_ui/sidebar_component.rb +4 -0
- data/app/components/lightning_ui/skeleton_component.html.erb +10 -0
- data/app/components/lightning_ui/skeleton_component.rb +8 -0
- data/app/components/lightning_ui/spinner_component.html.erb +10 -0
- data/app/components/lightning_ui/spinner_component.rb +4 -0
- data/app/components/lightning_ui/switch_component.html.erb +38 -0
- data/app/components/lightning_ui/switch_component.rb +31 -0
- data/app/components/lightning_ui/table/action_component.rb +9 -0
- data/app/components/lightning_ui/table/column_component.rb +12 -0
- data/app/components/lightning_ui/table_component.html.erb +40 -0
- data/app/components/lightning_ui/table_component.rb +16 -0
- data/app/components/lightning_ui/text_component.html.erb +3 -0
- data/app/components/lightning_ui/text_component.rb +12 -0
- data/app/components/lightning_ui/textarea_component.html.erb +48 -0
- data/app/components/lightning_ui/textarea_component.rb +42 -0
- data/app/helpers/lightning_ui/application_helper.rb +7 -0
- data/app/helpers/lightning_ui/heroicon_helper.rb +14 -0
- data/app/javascript/lightning_ui/controllers/accordion_controller.js +51 -0
- data/app/javascript/lightning_ui/controllers/banner_controller.js +11 -0
- data/app/javascript/lightning_ui/controllers/checkbox_controller.js +18 -0
- data/app/javascript/lightning_ui/controllers/clipboard_controller.js +21 -0
- data/app/javascript/lightning_ui/controllers/dropdown_controller.js +22 -0
- data/app/javascript/lightning_ui/controllers/main_controller.js +17 -0
- data/app/javascript/lightning_ui/controllers/modal_controller.js +44 -0
- data/app/javascript/lightning_ui/controllers/reveal_controller.js +22 -0
- data/app/javascript/lightning_ui/controllers/switch_controller.js +18 -0
- data/app/javascript/lightning_ui/index.js +31 -0
- data/config/initializers/heroicons.rb +5 -0
- data/lib/lightning_ui/engine.rb +20 -0
- data/lib/lightning_ui/version.rb +3 -0
- data/lib/lightning_ui.rb +6 -0
- data/lib/tasks/lightning_ui_tasks.rake +4 -0
- metadata +200 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LightningUi::BadgeComponent < LightningUi::BaseComponent
|
4
|
+
def initialize(status: :default, progress: nil)
|
5
|
+
@status = status
|
6
|
+
@progress = progress
|
7
|
+
end
|
8
|
+
|
9
|
+
def classes
|
10
|
+
status_classes = case @status
|
11
|
+
when :success
|
12
|
+
"bg-green-200 text-zinc-500"
|
13
|
+
when :warning
|
14
|
+
"bg-yellow-200 text-zinc-500"
|
15
|
+
when :error
|
16
|
+
"bg-red-600 text-white"
|
17
|
+
else
|
18
|
+
"bg-zinc-400/20 text-zinc-500"
|
19
|
+
end
|
20
|
+
[defalt_classes, status_classes].join(" ")
|
21
|
+
end
|
22
|
+
|
23
|
+
def progress_classes
|
24
|
+
progress_classes = case @progress
|
25
|
+
when :complete
|
26
|
+
"bg-zinc-400 border-zinc-400"
|
27
|
+
when :incomplete
|
28
|
+
"bg-yellow-300 border-zinc-400"
|
29
|
+
when :partialy_complete
|
30
|
+
"relative border-yellow-600 after:w-[3.75px] after:h-[8.2px] after:border-transparent after:border-l-yellow-600 after:border-r-yellow-600 after:border-[1px] after:-rotate-45 after:absolute after:-top-[1px] after:left-[1px] after:margin-0 after-margin-y-[1px]"
|
31
|
+
end
|
32
|
+
[default_progress_classes, progress_classes].join(" ")
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_progress_classes
|
36
|
+
"h-2 w-2 rounded-[3px] border-[1px]"
|
37
|
+
end
|
38
|
+
|
39
|
+
# hover:bg-zinc-400/30
|
40
|
+
def defalt_classes
|
41
|
+
"relative inline-flex items-center relative gap-x-1.5 rounded-[10px] px-2.5 py-1 text-sm font-medium sm:text-xs forced-colors:outline"
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<%= tag.div(class: classes, data: { type: @type, controller: "lui-banner" }) do %>
|
2
|
+
<div data-slot="header" class="flex items-center px-4 py-3">
|
3
|
+
<%= heroicon(icon, variant: :outline, options: { class: "shrink-0 w-5 h-5 me-2" }) %>
|
4
|
+
<span class="sr-only"><%= @type.to_s.humanize %></span>
|
5
|
+
<h3 class="text-md font-medium"><%= @title %></h3>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<div class="text-sm text-gray-800 p-4">
|
9
|
+
<%= content %>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<% if footer.present? %>
|
13
|
+
<div class="p-4 pt-0">
|
14
|
+
<%= footer %>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
<% end %>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LightningUi::BannerComponent < LightningUi::BaseComponent
|
4
|
+
renders_one :footer
|
5
|
+
|
6
|
+
def initialize(title:, type: :info, **options)
|
7
|
+
@type = type
|
8
|
+
@title = title
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def classes
|
13
|
+
type_classes = case @type
|
14
|
+
when :error
|
15
|
+
"*:data-[slot=header]:bg-red-600/90 *:data-[slot=header]:text-white"
|
16
|
+
else
|
17
|
+
"*:data-[slot=header]:bg-gray-50"
|
18
|
+
end
|
19
|
+
|
20
|
+
merge_classes([default_classes, type_classes, @options[:class]].compact.join(" "))
|
21
|
+
end
|
22
|
+
|
23
|
+
def icon
|
24
|
+
case @type
|
25
|
+
when :error
|
26
|
+
"exclamation-triangle"
|
27
|
+
else
|
28
|
+
"info-circle"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_classes
|
33
|
+
"border border-zinc-950/10 rounded-lg overflow-hidden transition-opacity duration-300 ease-out opacity-100"
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<% if @url %>
|
2
|
+
<%= link_to @url, class: classes, data: data_attributes do %>
|
3
|
+
<%= content %>
|
4
|
+
<% end %>
|
5
|
+
<% else %>
|
6
|
+
<%= content_tag :button,
|
7
|
+
class: classes,
|
8
|
+
data: data_attributes,
|
9
|
+
disabled: @disabled,
|
10
|
+
type: @type do %>
|
11
|
+
<%= content %>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LightningUi::ButtonComponent < LightningUi::BaseComponent
|
4
|
+
def initialize(type: :button, style: :default, size: :default, disabled: false, url: nil, **options)
|
5
|
+
@type = type
|
6
|
+
@style = style
|
7
|
+
@size = size
|
8
|
+
@disabled = disabled
|
9
|
+
@url = url
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def data_attributes
|
14
|
+
@options[:data] || {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def classes
|
18
|
+
class_list = []
|
19
|
+
class_list << @options[:class]
|
20
|
+
|
21
|
+
case @style
|
22
|
+
when :outline
|
23
|
+
class_list << outline_classes
|
24
|
+
when :destructive
|
25
|
+
class_list << default_classes
|
26
|
+
class_list << destructive_classes
|
27
|
+
else
|
28
|
+
class_list << default_classes
|
29
|
+
end
|
30
|
+
|
31
|
+
case @size
|
32
|
+
when :small
|
33
|
+
class_list << "text-xs"
|
34
|
+
when :default
|
35
|
+
class_list << "text-base/6 font-semibold sm:text-sm/6"
|
36
|
+
end
|
37
|
+
|
38
|
+
merge_classes(class_list.compact.join(" "))
|
39
|
+
end
|
40
|
+
|
41
|
+
def destructive_classes
|
42
|
+
"text-white border-red-500 hover:bg-red-600 active:bg-red-700\
|
43
|
+
[--btn-bg:var(--color-red-600)]\
|
44
|
+
[--btn-border:var(--color-red-950)]/90
|
45
|
+
[--btn-hover-overlay:var(--color-white)]/10\
|
46
|
+
[--btn-icon:var(--color-red-400)]\
|
47
|
+
active:[--btn-icon:var(--color-zinc-300)] hover:[--btn-icon:var(--color-zinc-300)] cursor-pointer"
|
48
|
+
end
|
49
|
+
|
50
|
+
# TODO:
|
51
|
+
def default_classes
|
52
|
+
"relative isolate rounded-lg border inline-flex items-baseline justify-center gap-x-2 px-4 py-2 sm:px-[calc(--spacing(3)-1px)]\
|
53
|
+
sm:py-[calc(--spacing(1.5)-1px)] focus:outline-hidden *:data-[slot=icon]:-mx-0.5 *:data-[slot=icon]:my-0.5\
|
54
|
+
*:data-[slot=icon]:size-5 *:data-[slot=icon]:shrink-0 *:data-[slot=icon]:self-center *:data-[slot=icon]:text-(--btn-icon)\
|
55
|
+
sm:*:data-[slot=icon]:my-1 sm:*:data-[slot=icon]:size-4 border-transparent bg-(--btn-border) before:absolute before:inset-0\
|
56
|
+
before:-z-10 before:rounded-[calc(var(--radius-lg)-1px)] before:bg-(--btn-bg) before:shadow-sm after:absolute after:inset-0
|
57
|
+
after:-z-10 after:rounded-[calc(var(--radius-lg)-1px)] after:shadow-[shadow:inset_0_1px_--theme(--color-white/15%)]\
|
58
|
+
active:after:bg-(--btn-hover-overlay) hover:after:bg-(--btn-hover-overlay) disabled:opacity-50 disabled:before:shadow-none disabled:after:shadow-none\
|
59
|
+
text-white\
|
60
|
+
[--btn-bg:var(--color-zinc-900)]\
|
61
|
+
[--btn-border:var(--color-zinc-950)]/90
|
62
|
+
[--btn-hover-overlay:var(--color-white)]/10\
|
63
|
+
[--btn-icon:var(--color-zinc-400)]\
|
64
|
+
active:[--btn-icon:var(--color-zinc-300)] hover:[--btn-icon:var(--color-zinc-300)] cursor-pointer"
|
65
|
+
end
|
66
|
+
|
67
|
+
def outline_classes
|
68
|
+
"relative isolate inline-flex items-baseline justify-center gap-x-2 rounded-lg border px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)]\
|
69
|
+
sm:py-[calc(--spacing(1.5)-1px)] focus:outline-hidden focus:outline focus:outline-2 focus:outline-offset-2\
|
70
|
+
focus:outline-blue-500 disabled:opacity-50 *:data-[slot=icon]:-mx-0.5 *:data-[slot=icon]:my-0.5 *:data-[slot=icon]:size-5\
|
71
|
+
*:data-[slot=icon]:shrink-0 *:data-[slot=icon]:self-center *:data-[slot=icon]:text-(--btn-icon) sm:*:data-[slot=icon]:my-1\
|
72
|
+
sm:*:data-[slot=icon]:size-4 border-zinc-950/10 text-zinc-950 active:bg-zinc-950/[2.5%] hover:bg-zinc-950/[2.5%]\
|
73
|
+
[--btn-icon:var(--color-zinc-500)] active:[--btn-icon:var(--color-zinc-700)] hover:[--btn-icon:var(--color-zinc-700)] cursor-pointer"
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<div data-controller="lui-checkbox" data-slot="field" class="grid grid-cols-[1.125rem_1fr] items-center gap-x-4 gap-y-1 sm:grid-cols-[1rem_1fr] *:data-[slot=control]:col-start-1 *:data-[slot=control]:row-start-1 *:data-[slot=control]:justify-self-center *:data-[slot=label]:col-start-2 *:data-[slot=label]:row-start-1 *:data-[slot=label]:justify-self-start *:data-[slot=description]:col-start-2 *:data-[slot=description]:row-start-2 has-data-[slot=description]:**:data-[slot=label]:font-medium" data-headlessui-state="">
|
2
|
+
<span
|
3
|
+
data-lui-checkbox-target="control"
|
4
|
+
data-slot="control"
|
5
|
+
class="group inline-flex focus:outline-hidden" id="headlessui-control-:r2m:" role="checkbox" aria-checked="false" tabindex="0" data-headlessui-state="" aria-labelledby="headlessui-label-:r2o:" aria-describedby="headlessui-description-:r2p:"
|
6
|
+
data-action="click->lui-checkbox#toggle"
|
7
|
+
>
|
8
|
+
<span class="relative isolate flex size-[1.125rem] items-center justify-center rounded-[0.3125rem] sm:size-4 before:absolute before:inset-0 before:-z-10 before:rounded-[calc(0.3125rem-1px)] before:bg-white before:shadow-sm group-data-checked:before:bg-(--checkbox-checked-bg) border border-zinc-950/15 group-data-checked:border-transparent group-data-hover:group-data-checked:border-transparent group-data-hover:border-zinc-950/30 group-data-checked:bg-(--checkbox-checked-border) after:absolute after:inset-0 after:rounded-[calc(0.3125rem-1px)] after:shadow-[inset_0_1px_--theme(--color-white/15%)] group-data-focus:outline group-data-focus:outline-2 group-data-focus:outline-offset-2 group-data-focus:outline-blue-500 group-data-disabled:opacity-50 group-data-disabled:border-zinc-950/25 group-data-disabled:bg-zinc-950/5 group-data-disabled:[--checkbox-check:var(--color-zinc-950)]/50 group-data-disabled:before:bg-transparent forced-colors:[--checkbox-check:HighlightText] forced-colors:[--checkbox-checked-bg:Highlight] forced-colors:group-data-disabled:[--checkbox-check:Highlight] [--checkbox-check:var(--color-white)] [--checkbox-checked-bg:var(--color-zinc-900)] [--checkbox-checked-border:var(--color-zinc-950)]/90">
|
9
|
+
<svg class="size-4 stroke-(--checkbox-check) opacity-0 group-data-checked:opacity-100 sm:h-3.5 sm:w-3.5" viewBox="0 0 14 14" fill="none">
|
10
|
+
<path class="opacity-100 group-data-indeterminate:opacity-0" d="M3 8L6 11L11 3.5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
|
11
|
+
<path class="opacity-0 group-data-indeterminate:opacity-100" d="M3 7H11" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
|
12
|
+
</svg>
|
13
|
+
</span>
|
14
|
+
</span>
|
15
|
+
<% if @label %>
|
16
|
+
<label data-slot="label" class="text-base/6 text-zinc-950 select-none data-disabled:opacity-50 sm:text-sm/6" id="headlessui-label-:r2o:" for="headlessui-control-:r2m:" data-headlessui-state=""
|
17
|
+
data-action="click->lui-checkbox#toggle"
|
18
|
+
>
|
19
|
+
<%= @label %>
|
20
|
+
</label>
|
21
|
+
<% end %>
|
22
|
+
<% if @description %>
|
23
|
+
<p data-slot="description" class="text-base/6 text-zinc-500 data-disabled:opacity-50 sm:text-sm/6" id="headlessui-description-:r2p:" data-headlessui-state="">
|
24
|
+
<%= @description %>
|
25
|
+
</p>
|
26
|
+
<% end %>
|
27
|
+
<% if @form %>
|
28
|
+
<%= @form.hidden_field(@name, value: @checked, data: { lui_checkbox_target: "field" }) %>
|
29
|
+
<% else %>
|
30
|
+
<%= hidden_field(
|
31
|
+
nil,
|
32
|
+
@name,
|
33
|
+
value: @checked,
|
34
|
+
data: { lui_checkbox_target: "field" }
|
35
|
+
) %>
|
36
|
+
<% end %>
|
37
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LightningUi::CheckboxComponent < LightningUi::BaseComponent
|
4
|
+
def initialize(name:, value: nil, label: nil, description: nil, form: nil, checked: false, disabled: false, **options)
|
5
|
+
@form = form
|
6
|
+
@name = name
|
7
|
+
@value = value
|
8
|
+
@description = description
|
9
|
+
@label = label
|
10
|
+
@checked = checked
|
11
|
+
@disabled = disabled
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<dt class="col-start-1 border-t border-zinc-950/5 pt-3 text-zinc-500 first:border-none sm:border-t sm:border-zinc-950/5 sm:py-3 flex items-center">
|
2
|
+
<%= @label %>
|
3
|
+
</dt>
|
4
|
+
|
5
|
+
<dd class="pt-1 pb-3 text-zinc-950 sm:border-t sm:border-zinc-950/5 sm:py-3 sm:nth-2:border-none flex items-center">
|
6
|
+
<div class="w-full">
|
7
|
+
<% if @value %>
|
8
|
+
<%= @value %>
|
9
|
+
<% else %>
|
10
|
+
<%= content %>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
13
|
+
</dd>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<div data-controller="lui-dropdown" class="relative">
|
2
|
+
<button type="button" data-action="lui-dropdown#toggle click@window->lui-dropdown#hide" class="flex items-center justify-between cursor-pointer w-full text-sm/6 font-medium text-zinc-700 hover:text-zinc-950">
|
3
|
+
<% if trigger? %>
|
4
|
+
<%= trigger %>
|
5
|
+
<% else %>
|
6
|
+
<%= @trigger_text %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<% case @anchor.to_s %>
|
10
|
+
<% when "bottom_right" %>
|
11
|
+
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
12
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
13
|
+
</svg>
|
14
|
+
<% when "top_right" %>
|
15
|
+
<svg class="w-2.5 h-2.5 ms-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
16
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 5 4-4 4 4"/>
|
17
|
+
</svg>
|
18
|
+
<% end %>
|
19
|
+
</button>
|
20
|
+
|
21
|
+
<div
|
22
|
+
data-lui-dropdown-target="menu"
|
23
|
+
class="<%=menu_classes %>"
|
24
|
+
data-transition-enter-from="opacity-0 scale-95"
|
25
|
+
data-transition-enter-to="opacity-100 scale-100"
|
26
|
+
data-transition-leave-from="opacity-100 scale-100"
|
27
|
+
data-transition-leave-to="opacity-0 scale-95"
|
28
|
+
>
|
29
|
+
<% items.each do |item| %>
|
30
|
+
<div class="px-4 py-2 text-sm/5 font-medium text-zinc-950 hover:bg-zinc-950/5">
|
31
|
+
<%= item %>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LightningUi::DropdownComponent < LightningUi::BaseComponent
|
4
|
+
renders_one :trigger
|
5
|
+
renders_many :items
|
6
|
+
|
7
|
+
def initialize(trigger_text: nil, anchor: :bottom_right, position: :bottom)
|
8
|
+
@trigger_text = trigger_text
|
9
|
+
@anchor = anchor
|
10
|
+
@position = position
|
11
|
+
end
|
12
|
+
|
13
|
+
def menu_classes
|
14
|
+
classes = %w[hidden transition transform p-1 origin-top-left absolute left-0 w-56 rounded-md shadow-lg bg-white ring-1 ring-zinc-950/10 focus:outline-none]
|
15
|
+
case @position.to_s
|
16
|
+
when "top"
|
17
|
+
classes << "mb-2 top-auto bottom-full"
|
18
|
+
when "bottom"
|
19
|
+
classes << "mt-2"
|
20
|
+
end
|
21
|
+
classes.join(" ")
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
<%= tag.div(
|
2
|
+
class: classes,
|
3
|
+
data:
|
4
|
+
) do %>
|
5
|
+
<% if @label %>
|
6
|
+
<%= tag.label(
|
7
|
+
@label,
|
8
|
+
class: "text-base/6 text-zinc-950 select-none data-disabled:opacity-50 sm:text-sm/6",
|
9
|
+
data: label_data
|
10
|
+
) %>
|
11
|
+
<% end %>
|
12
|
+
<% if @description %>
|
13
|
+
<%= tag.p(
|
14
|
+
@description,
|
15
|
+
class: "text-base/6 text-zinc-500 data-disabled:opacity-50 sm:text-sm/6",
|
16
|
+
data: description_data
|
17
|
+
) %>
|
18
|
+
<% end %>
|
19
|
+
<% if @form %>
|
20
|
+
<span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm dark:before:hidden after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
|
21
|
+
<% case @type %>
|
22
|
+
<% when :text %>
|
23
|
+
<%= @form.text_field(
|
24
|
+
@name,
|
25
|
+
data: input_data,
|
26
|
+
class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
|
27
|
+
disabled: @disabled,
|
28
|
+
autofocus: @autofocus,
|
29
|
+
placeholder: @placeholder
|
30
|
+
) %>
|
31
|
+
<% when :email %>
|
32
|
+
<%= @form.email_field(
|
33
|
+
@name,
|
34
|
+
data: input_data,
|
35
|
+
class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
|
36
|
+
disabled: @disabled,
|
37
|
+
autofocus: @autofocus
|
38
|
+
) %>
|
39
|
+
<% when :password %>
|
40
|
+
<%= @form.password_field(
|
41
|
+
@name,
|
42
|
+
data: input_data,
|
43
|
+
class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
|
44
|
+
disabled: @disabled,
|
45
|
+
autofocus: @autofocus
|
46
|
+
) %>
|
47
|
+
<% when :number %>
|
48
|
+
<%= @form.number_field(
|
49
|
+
@name,
|
50
|
+
data: input_data,
|
51
|
+
class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
|
52
|
+
disabled: @disabled,
|
53
|
+
autofocus: @autofocus
|
54
|
+
) %>
|
55
|
+
<% end %>
|
56
|
+
</span>
|
57
|
+
<% else %>
|
58
|
+
<span data-slot="control" class="relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm dark:before:hidden after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500 has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none has-data-invalid:before:shadow-red-500/10">
|
59
|
+
<% case @type %>
|
60
|
+
<% when :text %>
|
61
|
+
<%= text_field_tag(
|
62
|
+
@name,
|
63
|
+
@value,
|
64
|
+
data: input_data,
|
65
|
+
class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
|
66
|
+
disabled: @disabled,
|
67
|
+
autofocus: @autofocus
|
68
|
+
) %>
|
69
|
+
<% when :email %>
|
70
|
+
<%= email_field_tag(
|
71
|
+
@name,
|
72
|
+
@value,
|
73
|
+
data: input_data,
|
74
|
+
class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
|
75
|
+
disabled: @disabled,
|
76
|
+
autofocus: @autofocus
|
77
|
+
) %>
|
78
|
+
<% when :password %>
|
79
|
+
<%= password_field_tag(
|
80
|
+
@name,
|
81
|
+
@value,
|
82
|
+
data: input_data,
|
83
|
+
class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
|
84
|
+
disabled: @disabled,
|
85
|
+
autofocus: @autofocus
|
86
|
+
) %>
|
87
|
+
<% when :number %>
|
88
|
+
<%= number_field_tag(
|
89
|
+
@name,
|
90
|
+
@value,
|
91
|
+
data: input_data,
|
92
|
+
class: "relative block w-full appearance-none rounded-lg px-[calc(--spacing(3.5)-1px)] py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] text-base/6 text-zinc-950 placeholder:text-zinc-500 sm:text-sm/6 border border-zinc-950/10 data-hover:border-zinc-950/20 bg-transparent focus:outline-hidden data-invalid:border-red-500 data-invalid:data-hover:border-red-500 dark:data-invalid:data-hover:border-red-500 data-disabled:border-zinc-950/20",
|
93
|
+
disabled: @disabled,
|
94
|
+
autofocus: @autofocus
|
95
|
+
) %>
|
96
|
+
<% end %>
|
97
|
+
</span>
|
98
|
+
<% end %>
|
99
|
+
<% end %>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LightningUi::InputComponent < LightningUi::BaseComponent
|
4
|
+
def initialize(name:, value: nil, autofocus: false, label: nil, form: nil, type: :text, description: nil, disabled: false, placeholder: nil, **options)
|
5
|
+
@name = name
|
6
|
+
@value = value
|
7
|
+
@disabled = disabled
|
8
|
+
@autofocus = autofocus
|
9
|
+
@label = label
|
10
|
+
@form = form
|
11
|
+
@type = type
|
12
|
+
@description = description
|
13
|
+
@placeholder = placeholder
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def classes
|
18
|
+
merge_classes(["[&>[data-slot=label]+[data-slot=control]]:mt-3 [&>[data-slot=label]+[data-slot=description]]:mt-1 [&>[data-slot=description]+[data-slot=control]]:mt-3 [&>[data-slot=control]+[data-slot=description]]:mt-3 [&>[data-slot=control]+[data-slot=error]]:mt-3 *:data-[slot=label]:font-medium", @options[:class]].compact.join(" "))
|
19
|
+
end
|
20
|
+
|
21
|
+
def data
|
22
|
+
@options[:data] || {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def input_data
|
26
|
+
@options[:input_data] || {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def label_data
|
30
|
+
{slot: "label"}.merge(@options[:label_data] || {}).tap do |data|
|
31
|
+
if @disabled
|
32
|
+
data[:disabled] = "true"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def description_data
|
38
|
+
{slot: "description"}.merge(@options[:description_data] || {}).tap do |data|
|
39
|
+
if @disabled
|
40
|
+
data[:disabled] = "true"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<%= tag.dialog(id: @id, open: @open, data:) do %>
|
2
|
+
<div class="fixed inset-0 flex w-screen justify-center overflow-y-auto bg-zinc-950/25 px-2 py-2 transition duration-100 focus:outline-0 data-closed:opacity-0 data-enter:ease-out data-leave:ease-in sm:px-6 sm:py-8 lg:px-8 lg:py-16" aria-hidden="true" data-headlessui-state="open" data-open="" style=""></div>
|
3
|
+
<div class="fixed inset-0 w-screen overflow-y-auto pt-6 sm:pt-0">
|
4
|
+
<div class="grid min-h-full grid-rows-[1fr_auto] justify-items-center sm:grid-rows-[1fr_auto_3fr] sm:p-4">
|
5
|
+
<div class="sm:max-w-3xl row-start-2 w-full min-w-0 rounded-t-3xl bg-white p-(--gutter) ring-1 shadow-lg ring-zinc-950/10 [--gutter:--spacing(8)] sm:mb-auto sm:rounded-2xl forced-colors:outline transition duration-100 will-change-transform data-closed:translate-y-12 data-closed:opacity-0 data-enter:ease-out data-leave:ease-in sm:data-closed:translate-y-0 sm:data-closed:data-enter:scale-95" id="headlessui-dialog-panel-:r7:" data-headlessui-state="open" data-open="" style="">
|
6
|
+
<% if @title %>
|
7
|
+
<h2 class="text-lg/6 font-semibold text-balance text-zinc-950 sm:text-base/6" id="headlessui-dialog-title-:r8:" data-headlessui-state="open" data-open="">
|
8
|
+
<%= @title %>
|
9
|
+
</h2>
|
10
|
+
<% end %>
|
11
|
+
<% if @description %>
|
12
|
+
<p data-slot="text" id="headlessui-description-:r9:" data-headlessui-state="open" data-open="" class="mt-2 text-pretty text-base/6 text-zinc-500 sm:text-sm/6">
|
13
|
+
<%= @description %>
|
14
|
+
</p>
|
15
|
+
<% end %>
|
16
|
+
<%= body %>
|
17
|
+
<% if actions.any? %>
|
18
|
+
<div class="mt-8 flex flex-col-reverse items-center justify-end gap-3 *:w-full sm:flex-row sm:*:w-auto">
|
19
|
+
<% actions.each do |action| %>
|
20
|
+
<%= action %>
|
21
|
+
<% end %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LightningUi::ModalComponent < LightningUi::BaseComponent
|
4
|
+
renders_one :body
|
5
|
+
renders_many :actions
|
6
|
+
|
7
|
+
def initialize(id:, title:, description: nil, open: false, **options)
|
8
|
+
@id = id
|
9
|
+
@title = title
|
10
|
+
@description = description
|
11
|
+
@open = open
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def data
|
16
|
+
{
|
17
|
+
controller: "lui-modal",
|
18
|
+
lui_modal_target: "dialog"
|
19
|
+
}.merge(@options[:data] || {})
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<%= tag.div(class: "items-center gap-x-2 sm:flex") do %>
|
2
|
+
<% if @with_arrows %>
|
3
|
+
<%= link_to(url(@current_page - 1), class: link_classes, data: data(disabled: @current_page == 1)) do %>
|
4
|
+
<%= heroicon("chevron-left", options: { class: "w-5 h-5" }) %>
|
5
|
+
<% end %>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<% pages_with_gaps.each do |page| %>
|
9
|
+
<% if page == :gap %>
|
10
|
+
<span aria-hidden="true" class="w-[2.25rem] text-center text-sm/6 font-semibold text-zinc-950 select-none">…</span>
|
11
|
+
<% else %>
|
12
|
+
<%= link_to(page, url(page), class: link_classes, data: data(active: @current_page == page)) %>
|
13
|
+
<% end %>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<% if @with_arrows %>
|
17
|
+
<%= link_to(url(@current_page + 1), class: link_classes, data: data(disabled: @current_page == @total_pages)) do %>
|
18
|
+
<%= heroicon("chevron-right", options: {class: "w-5 h-5" }) %>
|
19
|
+
<% end %>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LightningUi::PaginationComponent < LightningUi::BaseComponent
|
4
|
+
def initialize(current_page:, total_pages:, path:, page_param: "page", with_arrows: false, **options)
|
5
|
+
@current_page = current_page
|
6
|
+
@total_pages = total_pages
|
7
|
+
@with_arrows = with_arrows
|
8
|
+
@path = path
|
9
|
+
@page_param = page_param
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def data(disabled: false, active: false)
|
14
|
+
{}.tap do |data|
|
15
|
+
data[:disabled] = true if disabled
|
16
|
+
data[:active] = true if active
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def link_classes
|
21
|
+
"min-w-[2.25rem] flex items-center justify-center rounded-lg border text-base/6 font-semibold px-[calc(--spacing(3.5)-1px)]\
|
22
|
+
py-[calc(--spacing(2.5)-1px)] sm:px-[calc(--spacing(3)-1px)] sm:py-[calc(--spacing(1.5)-1px)] sm:text-sm/6 \
|
23
|
+
focus:outline-hidden focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-blue-500\
|
24
|
+
data-disabled:opacity-50 data-disabled:pointer-events-none border-transparent text-zinc-950 data-active:bg-zinc-950/5 hover:bg-zinc-950/5"
|
25
|
+
end
|
26
|
+
|
27
|
+
def pages_with_gaps
|
28
|
+
return (1..@total_pages).to_a if @total_pages <= 7
|
29
|
+
|
30
|
+
(1..@total_pages).to_a.each_with_object([]) do |page, pages|
|
31
|
+
if page == 1 || page == @total_pages || page == @current_page || (page - @current_page).abs <= 2
|
32
|
+
pages << page
|
33
|
+
elsif pages.last != :gap
|
34
|
+
pages << :gap
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def url(page)
|
40
|
+
"#{@path}?#{@page_param}=#{page}"
|
41
|
+
end
|
42
|
+
end
|