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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/Rakefile +9 -2
  4. data/app/assets/builds/lightning_ui_kit.css +588 -31
  5. data/app/assets/builds/lightning_ui_kit.js +9 -2
  6. data/app/assets/builds/lightning_ui_kit.js.map +4 -4
  7. data/app/assets/vendor/lightning_ui_kit.css +582 -88
  8. data/app/assets/vendor/lightning_ui_kit.js +9 -2
  9. data/app/components/lightning_ui_kit/button_component.html.erb +4 -0
  10. data/app/components/lightning_ui_kit/button_component.rb +24 -3
  11. data/app/components/lightning_ui_kit/combobox_component.html.erb +137 -0
  12. data/app/components/lightning_ui_kit/combobox_component.rb +205 -0
  13. data/app/components/lightning_ui_kit/dropdown_component.html.erb +1 -1
  14. data/app/components/lightning_ui_kit/dropdown_component.rb +1 -1
  15. data/app/components/lightning_ui_kit/dropzone_component.html.erb +13 -38
  16. data/app/components/lightning_ui_kit/dropzone_component.rb +43 -16
  17. data/app/components/lightning_ui_kit/file_input_component.html.erb +4 -34
  18. data/app/components/lightning_ui_kit/file_input_component.rb +54 -20
  19. data/app/components/lightning_ui_kit/input_component.html.erb +14 -98
  20. data/app/components/lightning_ui_kit/input_component.rb +154 -19
  21. data/app/components/lightning_ui_kit/layout_component.html.erb +118 -0
  22. data/app/components/lightning_ui_kit/layout_component.rb +26 -0
  23. data/app/components/lightning_ui_kit/modal_component.html.erb +2 -2
  24. data/app/components/lightning_ui_kit/select_component.html.erb +6 -27
  25. data/app/components/lightning_ui_kit/select_component.rb +65 -23
  26. data/app/components/lightning_ui_kit/sidebar_link_component.html.erb +6 -0
  27. data/app/components/lightning_ui_kit/sidebar_link_component.rb +33 -0
  28. data/app/components/lightning_ui_kit/sidebar_section_component.html.erb +8 -0
  29. data/app/components/lightning_ui_kit/sidebar_section_component.rb +18 -0
  30. data/app/components/lightning_ui_kit/textarea_component.html.erb +5 -37
  31. data/app/components/lightning_ui_kit/textarea_component.rb +50 -17
  32. data/app/components/lightning_ui_kit/tooltip_component.html.erb +15 -0
  33. data/app/components/lightning_ui_kit/tooltip_component.rb +26 -0
  34. data/app/javascript/lightning_ui_kit/controllers/combobox_controller.js +704 -0
  35. data/app/javascript/lightning_ui_kit/controllers/field_controller.js +23 -0
  36. data/app/javascript/lightning_ui_kit/controllers/layout_controller.js +19 -0
  37. data/app/javascript/lightning_ui_kit/controllers/modal_controller.js +7 -1
  38. data/app/javascript/lightning_ui_kit/controllers/tooltip_controller.js +235 -0
  39. data/app/javascript/lightning_ui_kit/index.js +8 -0
  40. data/config/deploy.yml +2 -5
  41. data/lib/lightning_ui_kit/engine.rb +1 -6
  42. data/lib/lightning_ui_kit/version.rb +1 -1
  43. 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:w-56 lui:rounded-md lui:shadow-lg lui:bg-white lui:ring-1 lui:ring-zinc-950/10 lui:focus:outline-none]
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-indigo-500"}) %>
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-gray-900 lui:text-sm lui:font-normal lui:leading-snug" data-lui-dropzone-target="filename"></h4>
9
- <h5 class="lui:text-gray-400 lui:text-xs lui:font-normal lui:leading-[18px]" data-lui-dropzone-target="status"></h5>
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-300 lui:hover:text-indigo-500 lui:hover:cursor-pointer",
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-gray-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-indigo-600 lui:text-white lui:rounded-3xl"></div>
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-gray-800 lui:text-xs lui:font-medium lui:flex lui:justify-center lui:items-center">0%</span>
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
- <% if @label %>
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-gray-300 lui:border-dotted lui:hover:cursor-pointer lui:hover:bg-zinc-100" data-action="click->lui-dropzone#selectFiles">
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-indigo-500"}) %>
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-gray-900 lui:text-base lui:font-medium lui:leading-snug"><%= t("dropzone.default_message") %></p>
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-gray-400 lui:text-base lui:sm:text-sm/6 lui:font-normal lui:leading-4 lui:mt-1.5"><%= @placeholder %></p>
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
- data[:disabled] = "true"
58
- end
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
- <% if @label %>
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="lui:relative lui:block lui:w-full lui:before:absolute lui:before:inset-px lui:before:rounded-[calc(var(--radius-lg)-1px)] 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">
18
- <% if @form %>
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
- data[:disabled] = "true"
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