lightning_ui_kit 0.2.3 → 0.2.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/README.md +1 -1
- data/Rakefile +9 -2
- data/app/assets/builds/lightning_ui_kit.css +588 -31
- data/app/assets/builds/lightning_ui_kit.js +9 -2
- data/app/assets/builds/lightning_ui_kit.js.map +4 -4
- data/app/assets/vendor/lightning_ui_kit.css +582 -88
- data/app/assets/vendor/lightning_ui_kit.js +9 -2
- data/app/components/lightning_ui_kit/button_component.html.erb +4 -0
- data/app/components/lightning_ui_kit/button_component.rb +24 -3
- data/app/components/lightning_ui_kit/combobox_component.html.erb +137 -0
- data/app/components/lightning_ui_kit/combobox_component.rb +205 -0
- data/app/components/lightning_ui_kit/dropdown_component.html.erb +1 -1
- data/app/components/lightning_ui_kit/dropdown_component.rb +1 -1
- data/app/components/lightning_ui_kit/dropzone_component.html.erb +13 -38
- data/app/components/lightning_ui_kit/dropzone_component.rb +43 -16
- data/app/components/lightning_ui_kit/file_input_component.html.erb +4 -34
- data/app/components/lightning_ui_kit/file_input_component.rb +54 -20
- data/app/components/lightning_ui_kit/input_component.html.erb +14 -98
- data/app/components/lightning_ui_kit/input_component.rb +154 -19
- data/app/components/lightning_ui_kit/layout_component.html.erb +118 -0
- data/app/components/lightning_ui_kit/layout_component.rb +26 -0
- data/app/components/lightning_ui_kit/modal_component.html.erb +2 -2
- data/app/components/lightning_ui_kit/select_component.html.erb +6 -27
- data/app/components/lightning_ui_kit/select_component.rb +65 -23
- data/app/components/lightning_ui_kit/sidebar_link_component.html.erb +6 -0
- data/app/components/lightning_ui_kit/sidebar_link_component.rb +33 -0
- data/app/components/lightning_ui_kit/sidebar_section_component.html.erb +8 -0
- data/app/components/lightning_ui_kit/sidebar_section_component.rb +18 -0
- data/app/components/lightning_ui_kit/textarea_component.html.erb +5 -37
- data/app/components/lightning_ui_kit/textarea_component.rb +50 -17
- data/app/components/lightning_ui_kit/tooltip_component.html.erb +15 -0
- data/app/components/lightning_ui_kit/tooltip_component.rb +26 -0
- data/app/javascript/lightning_ui_kit/controllers/combobox_controller.js +704 -0
- data/app/javascript/lightning_ui_kit/controllers/field_controller.js +23 -0
- data/app/javascript/lightning_ui_kit/controllers/layout_controller.js +19 -0
- data/app/javascript/lightning_ui_kit/controllers/modal_controller.js +7 -1
- data/app/javascript/lightning_ui_kit/controllers/tooltip_controller.js +235 -0
- data/app/javascript/lightning_ui_kit/index.js +8 -0
- data/config/deploy.yml +2 -5
- data/lib/lightning_ui_kit/engine.rb +1 -6
- data/lib/lightning_ui_kit/version.rb +1 -1
- metadata +17 -17
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<%= tag.div(data:, class: classes) do %>
|
|
2
|
+
<% if @label %>
|
|
3
|
+
<%= tag.label(
|
|
4
|
+
@label,
|
|
5
|
+
class: "lui:text-base/6 lui:text-zinc-950 lui:select-none lui:data-disabled:opacity-50 lui:sm:text-sm/6",
|
|
6
|
+
data: label_data
|
|
7
|
+
) %>
|
|
8
|
+
<% end %>
|
|
9
|
+
|
|
10
|
+
<% if @description %>
|
|
11
|
+
<%= tag.p(
|
|
12
|
+
@description,
|
|
13
|
+
class: "lui:text-base/6 lui:text-zinc-500 lui:data-disabled:opacity-50 lui:sm:text-sm/6",
|
|
14
|
+
data: description_data
|
|
15
|
+
) %>
|
|
16
|
+
<% end %>
|
|
17
|
+
|
|
18
|
+
<div data-slot="control" class="lui:relative lui:w-full">
|
|
19
|
+
<% if @multiple %>
|
|
20
|
+
<%# Multiple selection: tags + input in a styled container %>
|
|
21
|
+
<div
|
|
22
|
+
class="lui:relative lui:flex lui:flex-wrap lui:items-center lui:gap-1.5 lui:w-full lui:min-h-[42px] lui:sm:min-h-[38px] lui:rounded-lg lui:border lui:border-zinc-950/10 lui:data-[hover]:border-zinc-950/20 lui:bg-white lui:shadow-sm lui:px-2 lui:py-1.5 lui:focus-within:ring-2 lui:focus-within:ring-blue-500 lui:focus-within:ring-offset-0 <%= 'lui:opacity-50' if @disabled %> <%= 'lui:border-red-500' if has_errors? %>"
|
|
23
|
+
data-lui-combobox-target="inputWrapper selectedTags"
|
|
24
|
+
>
|
|
25
|
+
<input
|
|
26
|
+
type="text"
|
|
27
|
+
role="combobox"
|
|
28
|
+
aria-autocomplete="list"
|
|
29
|
+
aria-expanded="false"
|
|
30
|
+
aria-haspopup="listbox"
|
|
31
|
+
autocomplete="off"
|
|
32
|
+
class="lui:flex-1 lui:min-w-[120px] lui:border-0 lui:bg-transparent lui:p-0 lui:text-base/6 lui:text-zinc-950 lui:placeholder:text-zinc-500 lui:sm:text-sm/6 lui:focus:outline-none lui:focus:ring-0"
|
|
33
|
+
placeholder="<%= @placeholder %>"
|
|
34
|
+
data-lui-combobox-target="input"
|
|
35
|
+
data-action="input->lui-combobox#onInput focus->lui-combobox#onFocus keydown->lui-combobox#onKeydown"
|
|
36
|
+
<%= "disabled" if @disabled %>
|
|
37
|
+
<%= "data-disabled=true" if @disabled %>
|
|
38
|
+
<%= "data-invalid=true" if has_errors? %>
|
|
39
|
+
/>
|
|
40
|
+
<span class="lui:pointer-events-none lui:flex lui:items-center">
|
|
41
|
+
<svg class="lui:size-5 lui:stroke-zinc-500 lui:sm:size-4" viewBox="0 0 16 16" aria-hidden="true" fill="none">
|
|
42
|
+
<path d="M5.75 10.75L8 13L10.25 10.75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
43
|
+
<path d="M10.25 5.25L8 3L5.75 5.25" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
44
|
+
</svg>
|
|
45
|
+
</span>
|
|
46
|
+
</div>
|
|
47
|
+
<% else %>
|
|
48
|
+
<%# Single selection: standard input styling %>
|
|
49
|
+
<span class="lui:relative lui:block lui:w-full lui:before:absolute lui:before:inset-px lui:before:rounded-[7px] lui:before:bg-white lui:before:shadow-sm lui:after:pointer-events-none lui:after:absolute lui:after:inset-0 lui:after:rounded-lg lui:after:ring-transparent lui:after:ring-inset lui:sm:focus-within:after:ring-2 lui:sm:focus-within:after:ring-blue-500 lui:has-data-disabled:opacity-50 lui:has-data-disabled:before:bg-zinc-950/5 lui:has-data-disabled:before:shadow-none lui:has-data-invalid:before:shadow-red-500/10">
|
|
50
|
+
<input
|
|
51
|
+
type="text"
|
|
52
|
+
role="combobox"
|
|
53
|
+
aria-autocomplete="list"
|
|
54
|
+
aria-expanded="false"
|
|
55
|
+
aria-haspopup="listbox"
|
|
56
|
+
autocomplete="off"
|
|
57
|
+
class="<%= input_classes %>"
|
|
58
|
+
placeholder="<%= @placeholder %>"
|
|
59
|
+
data-lui-combobox-target="input"
|
|
60
|
+
data-action="input->lui-combobox#onInput focus->lui-combobox#onFocus keydown->lui-combobox#onKeydown"
|
|
61
|
+
<%= "disabled" if @disabled %>
|
|
62
|
+
<%= "data-disabled=true" if @disabled %>
|
|
63
|
+
<%= "data-invalid=true" if has_errors? %>
|
|
64
|
+
/>
|
|
65
|
+
<span class="lui:pointer-events-none lui:absolute lui:inset-y-0 lui:right-0 lui:flex lui:items-center lui:pr-2">
|
|
66
|
+
<svg class="lui:size-5 lui:stroke-zinc-500 lui:sm:size-4" viewBox="0 0 16 16" aria-hidden="true" fill="none">
|
|
67
|
+
<path d="M5.75 10.75L8 13L10.25 10.75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
68
|
+
<path d="M10.25 5.25L8 3L5.75 5.25" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
69
|
+
</svg>
|
|
70
|
+
</span>
|
|
71
|
+
</span>
|
|
72
|
+
<% end %>
|
|
73
|
+
|
|
74
|
+
<%# Dropdown listbox %>
|
|
75
|
+
<div
|
|
76
|
+
data-lui-combobox-target="listbox"
|
|
77
|
+
role="listbox"
|
|
78
|
+
aria-label="<%= @label || 'Options' %>"
|
|
79
|
+
class="lui:hidden lui:absolute lui:z-50 lui:mt-1 lui:w-full lui:max-h-60 lui:overflow-auto lui:rounded-lg lui:bg-white lui:shadow-lg lui:ring-1 lui:ring-zinc-950/10"
|
|
80
|
+
>
|
|
81
|
+
<template data-lui-combobox-target="optionTemplate">
|
|
82
|
+
<div
|
|
83
|
+
role="option"
|
|
84
|
+
data-lui-combobox-target="option"
|
|
85
|
+
class="lui:relative lui:cursor-pointer lui:select-none lui:py-2 lui:px-3 lui:text-zinc-900 lui:hover:bg-zinc-100 lui:data-[selected]:bg-blue-50 lui:data-[highlighted]:bg-zinc-100 lui:data-[disabled]:opacity-50 lui:data-[disabled]:cursor-not-allowed"
|
|
86
|
+
data-action="click->lui-combobox#selectOption mouseenter->lui-combobox#highlightOption"
|
|
87
|
+
>
|
|
88
|
+
<span data-label class="lui:block lui:truncate"></span>
|
|
89
|
+
<span data-checkmark class="lui:hidden lui:absolute lui:inset-y-0 lui:right-0 lui:flex lui:items-center lui:pr-3 lui:text-blue-600">
|
|
90
|
+
<svg class="lui:size-5" viewBox="0 0 20 20" fill="currentColor">
|
|
91
|
+
<path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd" />
|
|
92
|
+
</svg>
|
|
93
|
+
</span>
|
|
94
|
+
</div>
|
|
95
|
+
</template>
|
|
96
|
+
|
|
97
|
+
<div data-lui-combobox-target="optionsContainer"></div>
|
|
98
|
+
|
|
99
|
+
<div data-lui-combobox-target="loading" class="lui:hidden lui:py-2 lui:px-3 lui:text-zinc-500 lui:text-sm">
|
|
100
|
+
Loading...
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div data-lui-combobox-target="noResults" class="lui:hidden lui:py-2 lui:px-3 lui:text-zinc-500 lui:text-sm">
|
|
104
|
+
No results found
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<% if @allow_custom %>
|
|
108
|
+
<div
|
|
109
|
+
data-lui-combobox-target="createOption"
|
|
110
|
+
class="lui:hidden lui:cursor-pointer lui:py-2 lui:px-3 lui:text-blue-600 lui:hover:bg-blue-50 lui:border-t lui:border-zinc-100 lui:flex lui:items-center lui:gap-1.5"
|
|
111
|
+
data-action="click->lui-combobox#createCustomOption"
|
|
112
|
+
>
|
|
113
|
+
<%= heroicon "plus", variant: :solid, options: { class: "lui:size-4" } %>
|
|
114
|
+
Create "<span data-lui-combobox-target="createOptionText"></span>"
|
|
115
|
+
</div>
|
|
116
|
+
<% end %>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<% if @multiple %>
|
|
121
|
+
<div data-lui-combobox-target="hiddenFields" data-name="<%= hidden_field_name %>"></div>
|
|
122
|
+
<% else %>
|
|
123
|
+
<% if @form %>
|
|
124
|
+
<%= @form.hidden_field(@name, data: { "lui-combobox-target" => "hiddenField" }, value: @selected) %>
|
|
125
|
+
<% else %>
|
|
126
|
+
<%= hidden_field_tag(@name, @selected, data: { "lui-combobox-target" => "hiddenField" }) %>
|
|
127
|
+
<% end %>
|
|
128
|
+
<% end %>
|
|
129
|
+
|
|
130
|
+
<% if has_errors? %>
|
|
131
|
+
<%= tag.p(
|
|
132
|
+
error_messages,
|
|
133
|
+
class: "lui:text-base/6 lui:text-red-600 lui:data-disabled:opacity-50 lui:sm:text-sm/6",
|
|
134
|
+
data: error_data
|
|
135
|
+
) %>
|
|
136
|
+
<% end %>
|
|
137
|
+
<% end %>
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LightningUiKit::ComboboxComponent < LightningUiKit::BaseComponent
|
|
4
|
+
include LightningUiKit::Errors
|
|
5
|
+
|
|
6
|
+
def initialize(
|
|
7
|
+
name: nil,
|
|
8
|
+
form: nil,
|
|
9
|
+
label: nil,
|
|
10
|
+
description: nil,
|
|
11
|
+
placeholder: nil,
|
|
12
|
+
disabled: false,
|
|
13
|
+
error: nil,
|
|
14
|
+
multiple: false,
|
|
15
|
+
allow_custom: false,
|
|
16
|
+
options: [],
|
|
17
|
+
selected: nil,
|
|
18
|
+
url: nil,
|
|
19
|
+
min_chars: 0,
|
|
20
|
+
debounce: 300,
|
|
21
|
+
association: nil,
|
|
22
|
+
foreign_key: nil,
|
|
23
|
+
nested_model: nil,
|
|
24
|
+
collection: nil,
|
|
25
|
+
label_method: :to_s,
|
|
26
|
+
value_method: :id,
|
|
27
|
+
**options_hash
|
|
28
|
+
)
|
|
29
|
+
@form = form
|
|
30
|
+
@label = label
|
|
31
|
+
@description = description
|
|
32
|
+
@placeholder = placeholder
|
|
33
|
+
@disabled = disabled
|
|
34
|
+
@error = error
|
|
35
|
+
@allow_custom = allow_custom
|
|
36
|
+
@url = url
|
|
37
|
+
@min_chars = min_chars
|
|
38
|
+
@debounce = debounce
|
|
39
|
+
@options_hash = options_hash
|
|
40
|
+
|
|
41
|
+
@association = association
|
|
42
|
+
@foreign_key = foreign_key
|
|
43
|
+
@nested_model = nested_model
|
|
44
|
+
@collection = collection
|
|
45
|
+
@label_method = label_method
|
|
46
|
+
@value_method = value_method
|
|
47
|
+
|
|
48
|
+
if association_mode?
|
|
49
|
+
@name = nested_attributes_name
|
|
50
|
+
@multiple = true
|
|
51
|
+
@options = derive_options_from_collection if @collection
|
|
52
|
+
@selected = derive_selected_from_association if @form&.object
|
|
53
|
+
else
|
|
54
|
+
@name = name
|
|
55
|
+
@multiple = multiple
|
|
56
|
+
@options = options
|
|
57
|
+
@selected = selected
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def classes
|
|
62
|
+
merge_classes([
|
|
63
|
+
"lui:[&>[data-slot=label]+[data-slot=control]]:mt-3",
|
|
64
|
+
"lui:[&>[data-slot=label]+[data-slot=description]]:mt-1",
|
|
65
|
+
"lui:[&>[data-slot=description]+[data-slot=control]]:mt-3",
|
|
66
|
+
"lui:[&>[data-slot=control]+[data-slot=description]]:mt-3",
|
|
67
|
+
"lui:[&>[data-slot=control]+[data-slot=error]]:mt-3",
|
|
68
|
+
"lui:*:data-[slot=label]:font-medium",
|
|
69
|
+
@options_hash[:class]
|
|
70
|
+
].compact.join(" "))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def data
|
|
74
|
+
default_data = {
|
|
75
|
+
controller: "lui-combobox",
|
|
76
|
+
action: "click@window->lui-combobox#clickOutside"
|
|
77
|
+
}
|
|
78
|
+
default_data.merge(@options_hash[:data] || {}).merge(combobox_values)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def combobox_values
|
|
82
|
+
base = {
|
|
83
|
+
"lui-combobox-multiple-value" => @multiple,
|
|
84
|
+
"lui-combobox-allow-custom-value" => @allow_custom,
|
|
85
|
+
"lui-combobox-url-value" => @url,
|
|
86
|
+
"lui-combobox-min-chars-value" => @min_chars,
|
|
87
|
+
"lui-combobox-debounce-value" => @debounce,
|
|
88
|
+
"lui-combobox-options-value" => options_json,
|
|
89
|
+
"lui-combobox-selected-value" => selected_json
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if association_mode?
|
|
93
|
+
base["lui-combobox-foreign-key-value"] = @foreign_key.to_s
|
|
94
|
+
base["lui-combobox-nested-model-value"] = @nested_model.to_s if @nested_model
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
base.compact
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def association_mode?
|
|
101
|
+
@association.present? && @foreign_key.present?
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def input_data
|
|
105
|
+
{
|
|
106
|
+
"lui-combobox-target" => "input",
|
|
107
|
+
:action => "input->lui-combobox#onInput focus->lui-combobox#onFocus keydown->lui-combobox#onKeydown"
|
|
108
|
+
}.tap do |d|
|
|
109
|
+
d[:invalid] = "true" if has_errors?
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def input_classes
|
|
114
|
+
merge_classes([
|
|
115
|
+
"lui:relative lui:block lui:w-full lui:appearance-none lui:rounded-lg",
|
|
116
|
+
"lui:px-[calc(--spacing(3.5)-1px)] lui:py-[calc(--spacing(2.5)-1px)]",
|
|
117
|
+
"lui:sm:px-[calc(--spacing(3)-1px)] lui:sm:py-[calc(--spacing(1.5)-1px)]",
|
|
118
|
+
"lui:text-base/6 lui:text-zinc-950 lui:placeholder:text-zinc-500 lui:sm:text-sm/6",
|
|
119
|
+
"lui:border lui:border-zinc-950/10 lui:data-[hover]:border-zinc-950/20",
|
|
120
|
+
"lui:bg-transparent lui:focus:outline-hidden",
|
|
121
|
+
"lui:data-invalid:border-red-500 lui:data-invalid:data-[hover]:border-red-500/60",
|
|
122
|
+
"lui:data-disabled:border-zinc-950/20",
|
|
123
|
+
"lui:pr-10"
|
|
124
|
+
].join(" "))
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def label_data
|
|
128
|
+
{slot: "label"}.merge(@options_hash[:label_data] || {}).tap do |data|
|
|
129
|
+
data[:disabled] = "true" if @disabled
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def description_data
|
|
134
|
+
{slot: "description"}.merge(@options_hash[:description_data] || {}).tap do |data|
|
|
135
|
+
data[:disabled] = "true" if @disabled
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def error_data
|
|
140
|
+
{slot: "error"}.merge(@options_hash[:error_data] || {}).tap do |data|
|
|
141
|
+
data[:disabled] = "true" if @disabled
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def hidden_field_name
|
|
146
|
+
base_name = scoped_field_name
|
|
147
|
+
|
|
148
|
+
if association_mode?
|
|
149
|
+
base_name
|
|
150
|
+
elsif @multiple
|
|
151
|
+
"#{base_name}[]"
|
|
152
|
+
else
|
|
153
|
+
base_name
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def scoped_field_name
|
|
158
|
+
if @form
|
|
159
|
+
"#{@form.object_name}[#{@name}]"
|
|
160
|
+
else
|
|
161
|
+
@name.to_s
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def nested_attributes_name
|
|
168
|
+
"#{@association}_attributes"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def derive_options_from_collection
|
|
172
|
+
return [] unless @collection
|
|
173
|
+
|
|
174
|
+
@collection.map do |item|
|
|
175
|
+
{value: item.public_send(@value_method), label: item.public_send(@label_method)}
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def derive_selected_from_association
|
|
180
|
+
return [] unless @form&.object&.respond_to?(@association)
|
|
181
|
+
|
|
182
|
+
@form.object.public_send(@association).map do |join_record|
|
|
183
|
+
{
|
|
184
|
+
join_id: join_record.id,
|
|
185
|
+
value: join_record.public_send(@foreign_key)
|
|
186
|
+
}
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def options_json
|
|
191
|
+
@options.to_json
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def selected_json
|
|
195
|
+
return [].to_json if @selected.nil?
|
|
196
|
+
|
|
197
|
+
if association_mode?
|
|
198
|
+
@selected.to_json
|
|
199
|
+
elsif @selected.is_a?(Array)
|
|
200
|
+
@selected.to_json
|
|
201
|
+
else
|
|
202
|
+
[@selected].to_json
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
data-transition-leave-to="opacity-0 scale-95"
|
|
28
28
|
>
|
|
29
29
|
<% items.each do |item| %>
|
|
30
|
-
<div class="lui:px-4 lui:py-2 lui:text-sm/5 lui:font-medium lui:text-zinc-950 lui:hover:bg-zinc-950/5">
|
|
30
|
+
<div class="lui:px-4 lui:py-2 lui:text-sm/5 lui:font-medium lui:text-zinc-950 lui:hover:bg-zinc-950/5 lui:rounded-md">
|
|
31
31
|
<%= item %>
|
|
32
32
|
</div>
|
|
33
33
|
<% end %>
|
|
@@ -11,7 +11,7 @@ class LightningUiKit::DropdownComponent < LightningUiKit::BaseComponent
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def menu_classes
|
|
14
|
-
classes = %w[lui:hidden lui:transition lui:transform lui:p-1 lui:origin-top-left lui:absolute lui:left-0 lui:
|
|
14
|
+
classes = %w[lui:hidden lui:transition lui:transform lui:p-1 lui:origin-top-left lui:absolute lui:left-0 lui:rounded-md lui:shadow-lg lui:bg-white lui:ring-1 lui:ring-zinc-950/10 lui:focus:outline-none lui:z-50]
|
|
15
15
|
case @position.to_s
|
|
16
16
|
when "top"
|
|
17
17
|
classes << "lui:mb-2 lui:top-auto lui:bottom-full"
|
|
@@ -3,36 +3,30 @@
|
|
|
3
3
|
<div class="lui:w-full lui:grid lui:gap-1 lui:my-4" id="#NEW_FILE" data-lui-dropzone-target="file">
|
|
4
4
|
<div class="lui:flex lui:items-center lui:justify-between lui:gap-2">
|
|
5
5
|
<div class="lui:flex lui:items-center lui:gap-2">
|
|
6
|
-
<%= heroicon("document", variant: :outline, options: {class: "lui:w-10 lui:h-10 lui:text-
|
|
6
|
+
<%= heroicon("document", variant: :outline, options: {class: "lui:w-10 lui:h-10 lui:text-blue-500"}) %>
|
|
7
7
|
<div class="lui:grid lui:gap-1">
|
|
8
|
-
<h4 class="lui:text-
|
|
9
|
-
<h5 class="lui:text-
|
|
8
|
+
<h4 class="lui:text-zinc-950 lui:text-sm lui:font-normal lui:leading-snug" data-lui-dropzone-target="filename"></h4>
|
|
9
|
+
<h5 class="lui:text-zinc-500 lui:text-xs lui:font-normal lui:leading-[18px]" data-lui-dropzone-target="status"></h5>
|
|
10
10
|
</div>
|
|
11
11
|
</div>
|
|
12
12
|
<%= heroicon(
|
|
13
13
|
"x-circle",
|
|
14
14
|
variant: :outline,
|
|
15
15
|
options: {
|
|
16
|
-
class: "lui:w-6 lui:h-6 lui:text-zinc-
|
|
16
|
+
class: "lui:w-6 lui:h-6 lui:text-zinc-500 lui:hover:text-blue-500 lui:hover:cursor-pointer",
|
|
17
17
|
data_action: "click->lui-dropzone#removeFile"
|
|
18
18
|
}) %>
|
|
19
19
|
</div>
|
|
20
20
|
<div class="lui:relative lui:flex lui:items-center lui:gap-2.5 lui:py-1.5">
|
|
21
|
-
<div class="lui:relative lui:w-full lui:h-2.5 lui:overflow-hidden lui:rounded-3xl lui:bg-
|
|
22
|
-
<div data-lui-dropzone-target="progressbar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 0%" class="lui:flex lui:h-full lui:items-center lui:justify-center lui:bg-
|
|
21
|
+
<div class="lui:relative lui:w-full lui:h-2.5 lui:overflow-hidden lui:rounded-3xl lui:bg-zinc-100">
|
|
22
|
+
<div data-lui-dropzone-target="progressbar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 0%" class="lui:flex lui:h-full lui:items-center lui:justify-center lui:bg-blue-600 lui:text-white lui:rounded-3xl"></div>
|
|
23
23
|
</div>
|
|
24
|
-
<span data-lui-dropzone-target="percentage-progress" class="lui:ml-2 lui:bg-white lui:rounded-full lui:text-
|
|
24
|
+
<span data-lui-dropzone-target="percentage-progress" class="lui:ml-2 lui:bg-white lui:rounded-full lui:text-zinc-950 lui:text-xs lui:font-medium lui:flex lui:justify-center lui:items-center">0%</span>
|
|
25
25
|
</div>
|
|
26
26
|
</div>
|
|
27
27
|
</template>
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
<%= tag.label(
|
|
31
|
-
@label,
|
|
32
|
-
class: "lui:text-base/6 lui:text-zinc-950 lui:select-none lui:data-disabled:opacity-50 lui:sm:text-sm/6",
|
|
33
|
-
data: label_data
|
|
34
|
-
) %>
|
|
35
|
-
<% end %>
|
|
29
|
+
<%= render_label %>
|
|
36
30
|
<% if @description %>
|
|
37
31
|
<%= tag.p(
|
|
38
32
|
@description,
|
|
@@ -41,34 +35,15 @@
|
|
|
41
35
|
) %>
|
|
42
36
|
<% end %>
|
|
43
37
|
|
|
44
|
-
<div class="lui:py-9 lui:bg-zinc-50 lui:rounded-2xl lui:border lui:border-
|
|
38
|
+
<div class="lui:py-9 lui:bg-zinc-50 lui:rounded-2xl lui:border lui:border-zinc-950/10 lui:border-dotted lui:hover:cursor-pointer lui:hover:bg-zinc-100" data-action="click->lui-dropzone#selectFiles">
|
|
45
39
|
<div class="lui:mb-3 lui:flex lui:items-center lui:justify-center">
|
|
46
|
-
<%= heroicon("cloud-arrow-up", variant: :outline, options: {class: "lui:w-10 lui:h-10 lui:text-
|
|
40
|
+
<%= heroicon("cloud-arrow-up", variant: :outline, options: {class: "lui:w-10 lui:h-10 lui:text-blue-500"}) %>
|
|
47
41
|
</div>
|
|
48
|
-
<p class="lui:text-center lui:text-
|
|
42
|
+
<p class="lui:text-center lui:text-zinc-950 lui:text-base lui:font-medium lui:leading-snug"><%= t("dropzone.default_message") %></p>
|
|
49
43
|
<% if @placeholder %>
|
|
50
|
-
<p class="lui:text-center lui:text-
|
|
51
|
-
<% end %>
|
|
52
|
-
<% if @form %>
|
|
53
|
-
<%= @form.file_field(
|
|
54
|
-
@name,
|
|
55
|
-
multiple: @multiple,
|
|
56
|
-
class: "lui:hidden",
|
|
57
|
-
direct_upload: true,
|
|
58
|
-
data: input_data,
|
|
59
|
-
accept: @accept
|
|
60
|
-
) %>
|
|
61
|
-
<% else %>
|
|
62
|
-
<%= file_field_tag(
|
|
63
|
-
@name,
|
|
64
|
-
value: @value,
|
|
65
|
-
multiple: @multiple,
|
|
66
|
-
class: "lui:hidden",
|
|
67
|
-
direct_upload: true,
|
|
68
|
-
data: input_data,
|
|
69
|
-
accept: @accept
|
|
70
|
-
) %>
|
|
44
|
+
<p class="lui:text-center lui:text-zinc-500 lui:text-base lui:sm:text-sm/6 lui:font-normal lui:leading-4 lui:mt-1.5"><%= @placeholder %></p>
|
|
71
45
|
<% end %>
|
|
46
|
+
<%= render_file_input %>
|
|
72
47
|
</div>
|
|
73
48
|
|
|
74
49
|
<% if has_errors? %>
|
|
@@ -26,36 +26,63 @@ class LightningUiKit::DropzoneComponent < LightningUiKit::BaseComponent
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def input_data
|
|
29
|
-
(@options[:input_data] || {}).tap do |data|
|
|
29
|
+
(@options[:input_data] || {}).dup.tap do |data|
|
|
30
30
|
data[:lui_dropzone_target] = "input"
|
|
31
31
|
data[:action] = "dragover->lui-dropzone#activate drop->lui-dropzone#uploadFiles change->lui-dropzone#uploadFiles"
|
|
32
|
-
if has_errors?
|
|
33
|
-
data[:invalid] = "true"
|
|
34
|
-
end
|
|
32
|
+
data[:invalid] = "true" if has_errors?
|
|
35
33
|
end
|
|
36
34
|
end
|
|
37
35
|
|
|
38
36
|
def label_data
|
|
39
|
-
{slot: "label"}.merge(@options[:label_data] || {}).tap do |data|
|
|
40
|
-
if @disabled
|
|
41
|
-
data[:disabled] = "true"
|
|
42
|
-
end
|
|
37
|
+
{slot: "label"}.merge(@options[:label_data] || {}).dup.tap do |data|
|
|
38
|
+
data[:disabled] = "true" if @disabled
|
|
43
39
|
end
|
|
44
40
|
end
|
|
45
41
|
|
|
46
42
|
def description_data
|
|
47
|
-
{slot: "description"}.merge(@options[:description_data] || {}).tap do |data|
|
|
48
|
-
if @disabled
|
|
49
|
-
data[:disabled] = "true"
|
|
50
|
-
end
|
|
43
|
+
{slot: "description"}.merge(@options[:description_data] || {}).dup.tap do |data|
|
|
44
|
+
data[:disabled] = "true" if @disabled
|
|
51
45
|
end
|
|
52
46
|
end
|
|
53
47
|
|
|
54
48
|
def error_data
|
|
55
|
-
{slot: "error"}.merge(@options[:error_data] || {}).tap do |data|
|
|
56
|
-
if @disabled
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
{slot: "error"}.merge(@options[:error_data] || {}).dup.tap do |data|
|
|
50
|
+
data[:disabled] = "true" if @disabled
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def label_html_options
|
|
55
|
+
{
|
|
56
|
+
class: "lui:text-base/6 lui:text-zinc-950 lui:select-none lui:data-disabled:opacity-50 lui:sm:text-sm/6",
|
|
57
|
+
data: label_data
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def render_label
|
|
62
|
+
return unless @label
|
|
63
|
+
|
|
64
|
+
if @form
|
|
65
|
+
@form.label(@name, @label, **label_html_options)
|
|
66
|
+
else
|
|
67
|
+
helpers.label_tag(@name, @label, **label_html_options)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def file_input_html_options
|
|
72
|
+
{
|
|
73
|
+
multiple: @multiple,
|
|
74
|
+
class: "lui:hidden",
|
|
75
|
+
direct_upload: true,
|
|
76
|
+
data: input_data,
|
|
77
|
+
accept: @accept
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def render_file_input
|
|
82
|
+
if @form
|
|
83
|
+
@form.file_field(@name, **file_input_html_options)
|
|
84
|
+
else
|
|
85
|
+
helpers.file_field_tag(@name, value: @value, **file_input_html_options)
|
|
59
86
|
end
|
|
60
87
|
end
|
|
61
88
|
end
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
<%= tag.div(class: classes, data:) do %>
|
|
2
|
-
|
|
3
|
-
<%= label_tag(
|
|
4
|
-
@name,
|
|
5
|
-
@label.is_a?(String) ? @label : nil,
|
|
6
|
-
class: "lui:text-base/6 lui:text-zinc-950 lui:select-none lui:data-disabled:opacity-50 lui:sm:text-sm/6",
|
|
7
|
-
data: label_data
|
|
8
|
-
) %>
|
|
9
|
-
<% end %>
|
|
1
|
+
<%= tag.div(class: classes, data: data) do %>
|
|
2
|
+
<%= render_label %>
|
|
10
3
|
<% if @description %>
|
|
11
4
|
<%= tag.p(
|
|
12
5
|
@description,
|
|
@@ -14,31 +7,8 @@
|
|
|
14
7
|
data: description_data
|
|
15
8
|
) %>
|
|
16
9
|
<% end %>
|
|
17
|
-
<span data-slot="control" class="
|
|
18
|
-
|
|
19
|
-
<%= @form.file_field(
|
|
20
|
-
@name,
|
|
21
|
-
data: input_data,
|
|
22
|
-
class: "lui:relative lui:block lui:w-full lui:appearance-none lui:rounded-lg lui:px-[calc(--spacing(3.5)-1px)] lui:py-[calc(--spacing(2.5)-1px)] lui:sm:px-[calc(--spacing(3)-1px)] lui:lui:py-[calc(--spacing(1.5)-1px)] lui:text-base/6 lui:text-zinc-950 lui:placeholder:text-zinc-500 lui:sm:text-sm/6 lui:border lui:border-zinc-950/10 lui:hover:border-zinc-950/20 lui:data-disabled:hover:none lui:data-disabled:cursor-not-allowed lui:bg-transparent lui:focus:outline-hidden lui:data-invalid:border-red-500 lui:data-invalid:hover:border-red-500 lui:data-disabled:border-zinc-950/20",
|
|
23
|
-
disabled: @disabled,
|
|
24
|
-
autofocus: @autofocus,
|
|
25
|
-
multiple: @multiple,
|
|
26
|
-
accept: @accept,
|
|
27
|
-
placeholder: @placeholder
|
|
28
|
-
) %>
|
|
29
|
-
<% else %>
|
|
30
|
-
<%= file_field_tag(
|
|
31
|
-
@name,
|
|
32
|
-
value: @value,
|
|
33
|
-
data: input_data,
|
|
34
|
-
class: "lui:relative lui:block lui:w-full lui:appearance-none lui:rounded-lg lui:text-base/6 lui:text-zinc-950 lui:placeholder:text-zinc-500 lui:sm:text-sm/6 lui:border lui:border-zinc-950/10 lui:hover:border-zinc-950/20 lui:bg-transparent lui:focus:outline-hidden lui:data-invalid:border-red-500 lui:data-invalid:hover:border-red-500 lui:data-disabled:hover:none lui:data-disabled:cursor-not-allowed lui:data-disabled:border-zinc-950/20 lui:data-disabled:file:cursor-not-allowed lui:data-disabled:file:cursor-not-allowed lui:file:cursor-pointer lui:cursor-pointer lui:file:border-0 lui:file:px-[calc(--spacing(3.5)-1px)] lui:file:py-[calc(--spacing(2.5)-1px)] lui:file:sm:px-[calc(--spacing(3)-1px)] lui:file:sm:py-[calc(--spacing(1.5)-1px)] lui:file:mr-4 lui:file:bg-zinc-100",
|
|
35
|
-
disabled: @disabled,
|
|
36
|
-
autofocus: @autofocus,
|
|
37
|
-
multiple: @multiple,
|
|
38
|
-
accept: @accept,
|
|
39
|
-
placeholder: @placeholder
|
|
40
|
-
) %>
|
|
41
|
-
<% end %>
|
|
10
|
+
<span data-slot="control" class="<%= control_classes %>">
|
|
11
|
+
<%= render_file_input %>
|
|
42
12
|
</span>
|
|
43
13
|
<% if has_errors? %>
|
|
44
14
|
<%= tag.p(
|
|
@@ -22,41 +22,75 @@ class LightningUiKit::FileInputComponent < LightningUiKit::BaseComponent
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def data
|
|
25
|
-
@options[:data] || {}
|
|
25
|
+
{controller: "lui-field"}.merge(@options[:data] || {})
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def input_data
|
|
29
|
-
(@options[:input_data] || {}).tap do |data|
|
|
30
|
-
if @disabled
|
|
31
|
-
|
|
32
|
-
end
|
|
33
|
-
if has_errors?
|
|
34
|
-
data[:invalid] = "true"
|
|
35
|
-
end
|
|
29
|
+
{lui_field_target: "field"}.merge(@options[:input_data] || {}).dup.tap do |data|
|
|
30
|
+
data[:disabled] = "true" if @disabled
|
|
31
|
+
data[:invalid] = "true" if has_errors?
|
|
36
32
|
end
|
|
37
33
|
end
|
|
38
34
|
|
|
39
35
|
def label_data
|
|
40
|
-
{slot: "label"}.merge(@options[:label_data] || {}).tap do |data|
|
|
41
|
-
if @disabled
|
|
42
|
-
data[:disabled] = "true"
|
|
43
|
-
end
|
|
36
|
+
{slot: "label"}.merge(@options[:label_data] || {}).dup.tap do |data|
|
|
37
|
+
data[:disabled] = "true" if @disabled
|
|
44
38
|
end
|
|
45
39
|
end
|
|
46
40
|
|
|
47
41
|
def description_data
|
|
48
|
-
{slot: "description"}.merge(@options[:description_data] || {}).tap do |data|
|
|
49
|
-
if @disabled
|
|
50
|
-
data[:disabled] = "true"
|
|
51
|
-
end
|
|
42
|
+
{slot: "description"}.merge(@options[:description_data] || {}).dup.tap do |data|
|
|
43
|
+
data[:disabled] = "true" if @disabled
|
|
52
44
|
end
|
|
53
45
|
end
|
|
54
46
|
|
|
55
47
|
def error_data
|
|
56
|
-
{slot: "error"}.merge(@options[:error_data] || {}).tap do |data|
|
|
57
|
-
if @disabled
|
|
58
|
-
data[:disabled] = "true"
|
|
59
|
-
end
|
|
48
|
+
{slot: "error"}.merge(@options[:error_data] || {}).dup.tap do |data|
|
|
49
|
+
data[:disabled] = "true" if @disabled
|
|
60
50
|
end
|
|
61
51
|
end
|
|
52
|
+
|
|
53
|
+
def label_html_options
|
|
54
|
+
{
|
|
55
|
+
class: "lui:text-base/6 lui:text-zinc-950 lui:select-none lui:data-disabled:opacity-50 lui:sm:text-sm/6",
|
|
56
|
+
data: label_data
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def render_label
|
|
61
|
+
return unless @label
|
|
62
|
+
|
|
63
|
+
if @form
|
|
64
|
+
@form.label(@name, @label, **label_html_options)
|
|
65
|
+
else
|
|
66
|
+
helpers.label_tag(@name, @label, **label_html_options)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def file_input_classes
|
|
71
|
+
"lui:relative lui:block lui:w-full lui:appearance-none lui:rounded-lg lui:text-base/6 lui:text-zinc-950 lui:placeholder:text-zinc-500 lui:sm:text-sm/6 lui:border lui:border-zinc-950/10 lui:data-[hover]:border-zinc-950/20 lui:bg-transparent lui:focus:outline-hidden lui:data-invalid:border-red-500 lui:data-invalid:data-[hover]:border-red-500 lui:data-disabled:cursor-not-allowed lui:data-disabled:border-zinc-950/20 lui:data-disabled:file:cursor-not-allowed lui:file:cursor-pointer lui:cursor-pointer lui:file:border-0 lui:file:px-[calc(--spacing(3.5)-1px)] lui:file:py-[calc(--spacing(2.5)-1px)] lui:file:sm:px-[calc(--spacing(3)-1px)] lui:file:sm:py-[calc(--spacing(1.5)-1px)] lui:file:mr-4 lui:file:bg-zinc-100 lui:file:rounded-l-[calc(var(--radius-lg)-1px)]"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def file_input_html_options
|
|
75
|
+
{
|
|
76
|
+
data: input_data,
|
|
77
|
+
class: file_input_classes,
|
|
78
|
+
disabled: @disabled,
|
|
79
|
+
autofocus: @autofocus,
|
|
80
|
+
multiple: @multiple,
|
|
81
|
+
accept: @accept
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def render_file_input
|
|
86
|
+
if @form
|
|
87
|
+
@form.file_field(@name, **file_input_html_options)
|
|
88
|
+
else
|
|
89
|
+
helpers.file_field_tag(@name, value: @value, **file_input_html_options)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def control_classes
|
|
94
|
+
"lui:relative lui:block lui:w-full lui:after:pointer-events-none lui:after:absolute lui:after:inset-0 lui:after:rounded-lg lui:after:ring-transparent lui:after:ring-inset lui:sm:focus-within:after:ring-2 lui:sm:focus-within:after:ring-blue-500 lui:has-data-disabled:opacity-50"
|
|
95
|
+
end
|
|
62
96
|
end
|