lostcoords-ui 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +364 -96
- data/app/assets/tailwind/lostcoords/ui/avatar.css +6 -6
- data/app/assets/tailwind/lostcoords/ui/base.css +45 -0
- data/app/assets/tailwind/lostcoords/ui/card.css +23 -22
- data/app/assets/tailwind/lostcoords/ui/choice.css +39 -0
- data/app/assets/tailwind/lostcoords/ui/code-block.css +10 -0
- data/app/assets/tailwind/lostcoords/ui/{disclosure.css → collapsible.css} +6 -6
- data/app/assets/tailwind/lostcoords/ui/components/code-block.css +5 -0
- data/app/assets/tailwind/lostcoords/ui/components/collapsible.css +5 -0
- data/app/assets/tailwind/lostcoords/ui/components/dialog.css +9 -0
- data/app/assets/tailwind/lostcoords/ui/components/emoji-picker.css +5 -0
- data/app/assets/tailwind/lostcoords/ui/components/menu.css +5 -0
- data/app/assets/tailwind/lostcoords/ui/components/prose.css +3 -0
- data/app/assets/tailwind/lostcoords/ui/components/table.css +3 -0
- data/app/assets/tailwind/lostcoords/ui/components/toast.css +5 -0
- data/app/assets/tailwind/lostcoords/ui/components/tooltip.css +4 -0
- data/app/assets/tailwind/lostcoords/ui/components.css +12 -0
- data/app/assets/tailwind/lostcoords/ui/copy-button.css +9 -9
- data/app/assets/tailwind/lostcoords/ui/core.css +21 -0
- data/app/assets/tailwind/lostcoords/ui/dialog.css +17 -17
- data/app/assets/tailwind/lostcoords/ui/emoji-picker.css +86 -88
- data/app/assets/tailwind/lostcoords/ui/field.css +2 -2
- data/app/assets/tailwind/lostcoords/ui/glass.css +9 -8
- data/app/assets/tailwind/lostcoords/ui/layout.css +2 -2
- data/app/assets/tailwind/lostcoords/ui/popover.css +14 -14
- data/app/assets/tailwind/lostcoords/ui/prose.css +66 -0
- data/app/assets/tailwind/lostcoords/ui/sheet.css +62 -63
- data/app/assets/tailwind/lostcoords/ui/squircle.css +7 -27
- data/app/assets/tailwind/lostcoords/ui/table.css +51 -0
- data/app/assets/tailwind/lostcoords/ui/theme.css +209 -225
- data/app/assets/tailwind/lostcoords/ui/toast.css +23 -0
- data/app/assets/tailwind/lostcoords/ui/tooltip.css +51 -0
- data/app/assets/tailwind/lostcoords/ui/typography.css +31 -61
- data/app/assets/tailwind/lostcoords/ui/ui.css +2 -21
- data/app/assets/tailwind/lostcoords/ui/utilities.css +14 -22
- data/app/helpers/lostcoords/ui/badge_helper.rb +67 -110
- data/app/helpers/lostcoords/ui/button_helper.rb +44 -35
- data/app/helpers/lostcoords/ui/card_helper.rb +9 -52
- data/app/helpers/lostcoords/ui/choice_helper.rb +87 -0
- data/app/helpers/lostcoords/ui/code_block_helper.rb +22 -0
- data/app/helpers/lostcoords/ui/collapsible_helper.rb +9 -0
- data/app/helpers/lostcoords/ui/copy_button_helper.rb +7 -7
- data/app/helpers/lostcoords/ui/description_list_helper.rb +12 -0
- data/app/helpers/lostcoords/ui/dialog_helper.rb +15 -17
- data/app/helpers/lostcoords/ui/emoji_picker_helper.rb +6 -0
- data/app/helpers/lostcoords/ui/feedback_helper.rb +25 -6
- data/app/helpers/lostcoords/ui/form_helper.rb +36 -59
- data/app/helpers/lostcoords/ui/glass_helper.rb +7 -7
- data/app/helpers/lostcoords/ui/layout_helper.rb +13 -11
- data/app/helpers/lostcoords/ui/menu_helper.rb +45 -10
- data/app/helpers/lostcoords/ui/navigation_helper.rb +144 -0
- data/app/helpers/lostcoords/ui/primitives_helper.rb +39 -61
- data/app/helpers/lostcoords/ui/reading_helper.rb +2 -2
- data/app/helpers/lostcoords/ui/render_attributes_helper.rb +17 -1
- data/app/helpers/lostcoords/ui/sheet_helper.rb +7 -45
- data/app/helpers/lostcoords/ui/structures_helper.rb +26 -125
- data/app/helpers/lostcoords/ui/table_helper.rb +11 -0
- data/app/helpers/lostcoords/ui/tailwind_helper.rb +13 -4
- data/app/helpers/lostcoords/ui/toast_helper.rb +22 -0
- data/app/helpers/lostcoords/ui/tooltip_helper.rb +17 -0
- data/app/helpers/lostcoords/ui/typography_helper.rb +41 -11
- data/app/javascript/lostcoords/ui/controllers/lc_code_block_controller.js +23 -0
- data/app/javascript/lostcoords/ui/controllers/{disclosure_controller.js → lc_collapsible_controller.js} +4 -4
- data/app/javascript/lostcoords/ui/controllers/{confirm_dialog_controller.js → lc_confirm_dialog_controller.js} +4 -4
- data/app/javascript/lostcoords/ui/controllers/{modal_controller.js → lc_dialog_controller.js} +1 -1
- data/app/javascript/lostcoords/ui/controllers/{emoji_picker_controller.js → lc_emoji_picker_controller.js} +38 -10
- data/app/javascript/lostcoords/ui/controllers/lc_kbd_controller.js +12 -0
- data/app/javascript/lostcoords/ui/controllers/{row_menu_controller.js → lc_menu_controller.js} +25 -3
- data/app/javascript/lostcoords/ui/controllers/{ui_popover_controller.js → lc_popover_controller.js} +1 -1
- data/app/javascript/lostcoords/ui/controllers/{ui_selection_controller.js → lc_selection_controller.js} +7 -15
- data/app/javascript/lostcoords/ui/controllers/{sheet_controller.js → lc_sheet_controller.js} +17 -17
- data/app/javascript/lostcoords/ui/controllers/lc_shortcut_controller.js +45 -0
- data/app/javascript/lostcoords/ui/controllers/lc_tabs_controller.js +42 -0
- data/app/javascript/lostcoords/ui/controllers/{toast_controller.js → lc_toast_controller.js} +2 -2
- data/app/javascript/lostcoords/ui/controllers/lc_toast_region_controller.js +62 -0
- data/app/javascript/lostcoords/ui/controllers/lc_tooltip_controller.js +54 -0
- data/app/javascript/lostcoords/ui/helpers/catalog_picker.js +77 -25
- data/app/javascript/lostcoords/ui/helpers/context_dialog.js +6 -6
- data/app/javascript/lostcoords/ui/helpers/dialog.js +2 -2
- data/app/javascript/lostcoords/ui/helpers/dismiss_stack.js +30 -7
- data/app/javascript/lostcoords/ui/helpers/floating_panel.js +7 -8
- data/app/javascript/lostcoords/ui/helpers/menu_keys.js +19 -18
- data/app/javascript/lostcoords/ui/helpers/roving_focus.js +175 -0
- data/app/javascript/lostcoords/ui/helpers/sheet_motion.js +4 -4
- data/app/javascript/lostcoords/ui/helpers/shortcuts.js +162 -0
- data/app/javascript/lostcoords/ui/helpers/top_layer.js +48 -10
- data/app/javascript/lostcoords/ui/index.js +57 -0
- data/app/views/lostcoords/ui/components/_emoji_picker.html.erb +46 -46
- data/app/views/lostcoords/ui/components/_emoji_picker_grid.html.erb +3 -3
- data/app/views/lostcoords/ui/components/_lc_code_block.html.erb +18 -0
- data/app/views/lostcoords/ui/components/_lc_collapsible.html.erb +5 -0
- data/app/views/lostcoords/ui/components/_lc_dialog.html.erb +11 -8
- data/app/views/lostcoords/ui/components/_lc_floating_panel.html.erb +8 -0
- data/config/importmap.rb +10 -2
- data/config/locales/en.yml +23 -0
- data/config/locales/es.yml +23 -0
- data/config/locales/ja.yml +23 -0
- data/lib/lostcoords/ui/action_view.rb +6 -3
- data/lib/lostcoords/ui/builders.rb +2 -2
- data/lib/lostcoords/ui/form_builder.rb +126 -0
- data/lib/lostcoords/ui/parts.rb +154 -0
- data/lib/lostcoords/ui/stylesheets.rb +25 -0
- data/lib/lostcoords/ui/tokens.rb +24 -1
- data/lib/lostcoords/ui/version.rb +1 -1
- data/lib/tasks/lostcoords_ui.rake +11 -0
- metadata +54 -21
- data/app/assets/tailwind/lostcoords/ui/reading.css +0 -60
- data/app/views/lostcoords/ui/components/_form_alert.html.erb +0 -4
- data/app/views/lostcoords/ui/components/_lc_disclosure.html.erb +0 -5
- data/app/views/lostcoords/ui/components/_skeleton.html.erb +0 -4
- data/app/views/lostcoords/ui/components/_spinner.html.erb +0 -10
- data/app/views/lostcoords/ui/components/_ui_floating_panel.html.erb +0 -8
- data/config/helper_renames.json +0 -113
- data/script/migrate_helpers.rb +0 -136
- /data/app/javascript/lostcoords/ui/controllers/{ui_copy_controller.js → lc_copy_controller.js} +0 -0
- /data/app/javascript/lostcoords/ui/controllers/{ui_toggle_controller.js → lc_toggle_controller.js} +0 -0
data/config/locales/en.yml
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
en:
|
|
2
2
|
lostcoords:
|
|
3
3
|
ui:
|
|
4
|
+
pager:
|
|
5
|
+
label: "Pages"
|
|
6
|
+
previous: "Previous"
|
|
7
|
+
next: "Next"
|
|
8
|
+
skip_link:
|
|
9
|
+
label: "Skip to content"
|
|
10
|
+
spinner:
|
|
11
|
+
label: "Loading…"
|
|
12
|
+
dialog:
|
|
13
|
+
trigger: "Open dialog"
|
|
14
|
+
cancel: "Cancel"
|
|
15
|
+
close: "Close %{title}"
|
|
16
|
+
menu:
|
|
17
|
+
label: "Actions"
|
|
18
|
+
popover:
|
|
19
|
+
trigger: "Open popover"
|
|
20
|
+
breadcrumb:
|
|
21
|
+
label: "Breadcrumb"
|
|
22
|
+
code_block:
|
|
23
|
+
label: "Source code"
|
|
24
|
+
copy: "Copy %{label}"
|
|
25
|
+
expand: "Show all code"
|
|
26
|
+
collapse: "Show less code"
|
|
4
27
|
copy_button:
|
|
5
28
|
label: "Copy"
|
|
6
29
|
copied: "Copied"
|
data/config/locales/es.yml
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
es:
|
|
2
2
|
lostcoords:
|
|
3
3
|
ui:
|
|
4
|
+
pager:
|
|
5
|
+
label: "Páginas"
|
|
6
|
+
previous: "Anterior"
|
|
7
|
+
next: "Siguiente"
|
|
8
|
+
skip_link:
|
|
9
|
+
label: "Saltar al contenido"
|
|
10
|
+
spinner:
|
|
11
|
+
label: "Cargando…"
|
|
12
|
+
dialog:
|
|
13
|
+
trigger: "Abrir diálogo"
|
|
14
|
+
cancel: "Cancelar"
|
|
15
|
+
close: "Cerrar %{title}"
|
|
16
|
+
menu:
|
|
17
|
+
label: "Acciones"
|
|
18
|
+
popover:
|
|
19
|
+
trigger: "Abrir panel"
|
|
20
|
+
breadcrumb:
|
|
21
|
+
label: "Ruta de navegación"
|
|
22
|
+
code_block:
|
|
23
|
+
label: "Código fuente"
|
|
24
|
+
copy: "Copiar %{label}"
|
|
25
|
+
expand: "Mostrar todo el código"
|
|
26
|
+
collapse: "Mostrar menos código"
|
|
4
27
|
copy_button:
|
|
5
28
|
label: "Copiar"
|
|
6
29
|
copied: "Copiado"
|
data/config/locales/ja.yml
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
ja:
|
|
2
2
|
lostcoords:
|
|
3
3
|
ui:
|
|
4
|
+
pager:
|
|
5
|
+
label: "ページ"
|
|
6
|
+
previous: "前へ"
|
|
7
|
+
next: "次へ"
|
|
8
|
+
skip_link:
|
|
9
|
+
label: "本文へ移動"
|
|
10
|
+
spinner:
|
|
11
|
+
label: "読み込み中…"
|
|
12
|
+
dialog:
|
|
13
|
+
trigger: "ダイアログを開く"
|
|
14
|
+
cancel: "キャンセル"
|
|
15
|
+
close: "%{title}を閉じる"
|
|
16
|
+
menu:
|
|
17
|
+
label: "操作"
|
|
18
|
+
popover:
|
|
19
|
+
trigger: "パネルを開く"
|
|
20
|
+
breadcrumb:
|
|
21
|
+
label: "パンくずリスト"
|
|
22
|
+
code_block:
|
|
23
|
+
label: "ソースコード"
|
|
24
|
+
copy: "%{label}をコピー"
|
|
25
|
+
expand: "コードをすべて表示"
|
|
26
|
+
collapse: "コードを折りたたむ"
|
|
4
27
|
copy_button:
|
|
5
28
|
label: "コピー"
|
|
6
29
|
copied: "コピー済み"
|
|
@@ -3,8 +3,11 @@ require "action_view"
|
|
|
3
3
|
require "active_support/core_ext/hash"
|
|
4
4
|
require "active_support/core_ext/object/blank"
|
|
5
5
|
require "tailwind_merge"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
require_relative "resources"
|
|
7
|
+
require_relative "icons"
|
|
8
|
+
require_relative "parts"
|
|
9
|
+
require_relative "form_builder"
|
|
10
|
+
require_relative "helpers"
|
|
11
|
+
require_relative "stylesheets"
|
|
9
12
|
|
|
10
13
|
Lostcoords::Ui.load_translations!
|
|
@@ -10,8 +10,8 @@ module Lostcoords::Ui
|
|
|
10
10
|
|
|
11
11
|
def component?
|
|
12
12
|
!name.to_s.end_with?("_class") &&
|
|
13
|
-
!%i[lc_tw lc_sheet_data
|
|
14
|
-
helper != "EmojiPickerHelper"
|
|
13
|
+
!%i[lc_tw lc_sheet_data lc_shortcut_data lc_alert_icon].include?(name) &&
|
|
14
|
+
(helper != "EmojiPickerHelper" || name == :lc_emoji_picker)
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
module Lostcoords::Ui
|
|
2
|
+
# Rails form builder that renders the library's controls and connects model errors to them.
|
|
3
|
+
#
|
|
4
|
+
# <%= lc_form_with model: @project do |form| %>
|
|
5
|
+
# <%= form.field :name, hint: "Shown to members" %>
|
|
6
|
+
# <%= form.field :email, as: :email_field %>
|
|
7
|
+
# <%= form.switch :public %>
|
|
8
|
+
# <%= form.submit %>
|
|
9
|
+
# <% end %>
|
|
10
|
+
#
|
|
11
|
+
# Every standard helper keeps its Rails signature. A symbol `size:` (:sm, :md, :lg) picks the control
|
|
12
|
+
# size; an integer still sets the HTML size attribute.
|
|
13
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
|
14
|
+
INPUTS = %i[
|
|
15
|
+
text_field email_field password_field telephone_field phone_field url_field search_field number_field
|
|
16
|
+
date_field time_field datetime_field datetime_local_field month_field week_field
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
INPUTS.each do |selector|
|
|
20
|
+
define_method(selector) do |method, options = {}|
|
|
21
|
+
size = control_size!(options)
|
|
22
|
+
super(method, control_options(method, options, "input", @template.lc_input_class(size: size)))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def text_area(method, options = {})
|
|
27
|
+
super(method, control_options(method, options, "textarea", @template.lc_textarea_class))
|
|
28
|
+
end
|
|
29
|
+
alias textarea text_area
|
|
30
|
+
|
|
31
|
+
def select(method, choices = nil, options = {}, html_options = {}, &block)
|
|
32
|
+
size = control_size!(html_options)
|
|
33
|
+
super(method, choices, options, control_options(method, html_options, "select", @template.lc_select_class(size: size)), &block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
SELECT_OPTIONS = %i[include_blank prompt selected disabled].freeze
|
|
37
|
+
|
|
38
|
+
alias_method :rails_check_box, :check_box
|
|
39
|
+
private :rails_check_box
|
|
40
|
+
|
|
41
|
+
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
|
|
42
|
+
super(method, control_options(method, options, "checkbox-input", @template.lc_checkbox_class), checked_value, unchecked_value)
|
|
43
|
+
end
|
|
44
|
+
alias checkbox check_box
|
|
45
|
+
|
|
46
|
+
def radio_button(method, tag_value, options = {})
|
|
47
|
+
super(method, tag_value, control_options(method, options, "radio-input", @template.lc_radio_class))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# A checkbox drawn as a switch, with its label beside it.
|
|
51
|
+
def switch(method, label: nil, hint: nil, checked_value: "1", unchecked_value: "0", **options)
|
|
52
|
+
input = rails_check_box(method, control_options(method, { role: "switch" }.merge(options), "switch-input", @template.lc_switch_class, described: true), checked_value, unchecked_value)
|
|
53
|
+
choice_label(method, label, hint, "switch", input)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# A checkbox with its label beside it.
|
|
57
|
+
def choice(method, label: nil, hint: nil, checked_value: "1", unchecked_value: "0", **options)
|
|
58
|
+
input = rails_check_box(method, control_options(method, options, "checkbox-input", @template.lc_checkbox_class, described: true), checked_value, unchecked_value)
|
|
59
|
+
choice_label(method, label, hint, "checkbox", input)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def label(method, text = nil, options = {}, &block)
|
|
63
|
+
if text.is_a?(Hash)
|
|
64
|
+
options = text
|
|
65
|
+
text = nil
|
|
66
|
+
end
|
|
67
|
+
super(method, text, options.merge(class: @template.lc_field_label_class(extra: options[:class])), &block)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def submit(value = nil, options = {})
|
|
71
|
+
if value.is_a?(Hash)
|
|
72
|
+
options = value
|
|
73
|
+
value = nil
|
|
74
|
+
end
|
|
75
|
+
variant = options.delete(:variant) || :primary
|
|
76
|
+
size = options.delete(:size) || :md
|
|
77
|
+
data = { slot: "button", variant: variant, size: size }.merge(options.delete(:data) || {})
|
|
78
|
+
super(value, options.merge(class: @template.lc_button_class(variant: variant, size: size, extra: options[:class]), data: data))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Label, control, hint and the attribute's errors in one field. `as:` names the control helper;
|
|
82
|
+
# `as: :select` takes `choices:` and Rails' select options such as `include_blank:`. A block renders
|
|
83
|
+
# a custom control and receives its attributes to splat with `**control`.
|
|
84
|
+
def field(method, label: nil, hint: nil, as: :text_field, choices: nil, **options, &block)
|
|
85
|
+
@template.lc_field(label: label || human_name(method), id: field_id(method), hint: hint, error: errors_for(method)) do |control|
|
|
86
|
+
next @template.capture(control, &block) if block
|
|
87
|
+
|
|
88
|
+
attributes = @template.send(:lc_attributes, options.except(*SELECT_OPTIONS), control.to_hash.except(:id))
|
|
89
|
+
if as.to_sym == :select
|
|
90
|
+
select(method, choices, options.slice(*SELECT_OPTIONS), attributes)
|
|
91
|
+
else
|
|
92
|
+
public_send(as, method, attributes.merge(options.slice(*SELECT_OPTIONS)))
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
def control_size!(options)
|
|
99
|
+
options[:size].is_a?(Symbol) || options[:size].is_a?(String) && options[:size].match?(/\A[a-z]+\z/) ? options.delete(:size).to_sym : :md
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# described: points aria-describedby at the error a choice label renders, as lc_field does for its control.
|
|
103
|
+
def control_options(method, options, slot, classes, described: false)
|
|
104
|
+
options = options.dup
|
|
105
|
+
aria = (options.delete(:aria) || {}).dup
|
|
106
|
+
aria[:invalid] = true if errors_for(method).any? && !aria.key?(:invalid)
|
|
107
|
+
aria[:describedby] = [ aria[:describedby], "#{field_id(method)}-error" ].compact.join(" ") if described && errors_for(method).any?
|
|
108
|
+
data = { slot: slot }.merge(options.delete(:data) || {})
|
|
109
|
+
options.merge(class: @template.lc_tw(classes, options[:class]), aria: aria, data: data)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def choice_label(method, text, hint, slot, input)
|
|
113
|
+
@template.send(:lc_choice_label, slot, text || human_name(method), hint, input, errors: errors_for(method), error_id: "#{field_id(method)}-error", for: field_id(method))
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def errors_for(method)
|
|
117
|
+
return [] unless object.respond_to?(:errors)
|
|
118
|
+
|
|
119
|
+
object.errors.full_messages_for(method)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def human_name(method)
|
|
123
|
+
object.class.respond_to?(:human_attribute_name) ? object.class.human_attribute_name(method) : method.to_s.humanize
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
module Lostcoords::Ui
|
|
2
|
+
# Builders yielded by compound components, so their parts are scoped and discoverable:
|
|
3
|
+
#
|
|
4
|
+
# <%= lc_card do |card| %>
|
|
5
|
+
# <%= card.header title: "Members", description: "People in this workspace" %>
|
|
6
|
+
# <%= card.content do %>…<% end %>
|
|
7
|
+
# <%= card.footer do %>…<% end %>
|
|
8
|
+
# <% end %>
|
|
9
|
+
#
|
|
10
|
+
# Every part carries data-slot, so a context can style it without matching classes.
|
|
11
|
+
module Parts
|
|
12
|
+
class Base
|
|
13
|
+
def initialize(view)
|
|
14
|
+
@view = view
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
attr_reader :view
|
|
19
|
+
|
|
20
|
+
def merge(defaults, attributes)
|
|
21
|
+
view.send(:lc_attributes, defaults, attributes)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def slot(name, attributes, defaults = {})
|
|
25
|
+
merge(defaults.merge(data: { slot: name }), attributes)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def part(tag_name, name, classes, attributes, &block)
|
|
29
|
+
view.content_tag(tag_name, slot(name, attributes, class: classes), &block)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Card < Base
|
|
34
|
+
def header(title: nil, description: nil, level: 3, **attributes, &actions)
|
|
35
|
+
heading = [ (self.title(title, level: level) if title), (self.description(description) if description) ].compact
|
|
36
|
+
action = view.capture(&actions) if actions
|
|
37
|
+
part(:header, "card-header", "flex min-w-0 items-start justify-between gap-content", attributes) do
|
|
38
|
+
view.safe_join([
|
|
39
|
+
(view.tag.div(view.safe_join(heading), class: "flex min-w-0 flex-col gap-tight", data: { slot: "card-heading" }) if heading.any?),
|
|
40
|
+
(view.tag.div(action, class: "flex shrink-0 items-center gap-related", data: { slot: "card-action" }) if action.present?)
|
|
41
|
+
].compact)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def title(content = nil, level: 3, **attributes, &block)
|
|
46
|
+
view.lc_heading(content, level: level, size: :subheading, **slot("card-title", attributes), &block)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def description(content = nil, **attributes, &block)
|
|
50
|
+
view.lc_text(content, size: :small, tone: :muted, **slot("card-description", attributes), &block)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def content(**attributes, &block)
|
|
54
|
+
part(:div, "card-content", "flex min-w-0 flex-col gap-content", attributes, &block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def footer(**attributes, &block)
|
|
58
|
+
part(:div, "card-footer", "flex min-w-0 flex-wrap items-center justify-end gap-related", attributes, &block)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Splats into a control as `**field`, so the control gets the field's id and validation attributes.
|
|
63
|
+
class Field < Base
|
|
64
|
+
attr_reader :id
|
|
65
|
+
|
|
66
|
+
def initialize(view, id, control)
|
|
67
|
+
super(view)
|
|
68
|
+
@id = id
|
|
69
|
+
@control = control
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def to_hash = @control
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class Dialog < Base
|
|
76
|
+
def initialize(view, id)
|
|
77
|
+
super(view)
|
|
78
|
+
@id = id
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def footer(**attributes, &block)
|
|
82
|
+
part(:div, "dialog-footer", "flex flex-wrap items-center justify-end gap-related", attributes, &block)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def close(label = nil, variant: :secondary, **attributes)
|
|
86
|
+
label ||= view.send(:lc_t, "lostcoords.ui.dialog.cancel")
|
|
87
|
+
view.lc_button(label, variant: variant, **merge({ data: { action: "lc-dialog#close", slot: "dialog-close" } }, attributes))
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class Alert < Base
|
|
92
|
+
def actions(**attributes, &block)
|
|
93
|
+
part(:div, "alert-actions", "flex flex-wrap items-center gap-related pt-tight", attributes, &block)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class Tabs < Base
|
|
98
|
+
Panel = Data.define(:value, :label, :content, :attributes)
|
|
99
|
+
|
|
100
|
+
attr_reader :panels
|
|
101
|
+
|
|
102
|
+
def initialize(view)
|
|
103
|
+
super
|
|
104
|
+
@panels = []
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Records a panel; lc_tabs renders the tab list and panels after the block.
|
|
108
|
+
def panel(label, value: nil, **attributes, &block)
|
|
109
|
+
value = (value || label).to_s
|
|
110
|
+
raise ArgumentError, "tab label must not be blank" if label.blank?
|
|
111
|
+
raise ArgumentError, "duplicate tab value: #{value}" if @panels.any? { |panel| panel.value == value }
|
|
112
|
+
|
|
113
|
+
@panels << Panel.new(value, label, (view.capture(&block) if block), attributes)
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
class NavList < Base
|
|
119
|
+
def initialize(view, compact)
|
|
120
|
+
super(view)
|
|
121
|
+
@compact = compact
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# current: marks the page the reader is on with aria-current="page".
|
|
125
|
+
def link(label, url, current: false, indent: false, **attributes)
|
|
126
|
+
view.tag.li(class: ("ps-content" if indent), data: { slot: "nav-item" }) do
|
|
127
|
+
view.link_to(label, url, **merge({ class: view.lc_nav_link_class(compact: @compact), aria: { current: ("page" if current) }, data: { slot: "nav-link" } }, attributes))
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# The list is named with aria-label rather than an id, so the same navigation can render twice,
|
|
132
|
+
# as in a sidebar and a phone menu.
|
|
133
|
+
def group(title, **attributes, &block)
|
|
134
|
+
part(:li, "nav-group", "flex flex-col gap-tight not-first:pt-section", attributes) do
|
|
135
|
+
view.safe_join([
|
|
136
|
+
view.tag.span(title, class: "px-3 pb-tight text-caption font-semibold text-foreground", aria: { hidden: true }, data: { slot: "nav-group-title" }),
|
|
137
|
+
view.tag.ul(view.capture(&block), class: "m-0 flex list-none flex-col gap-px p-0", aria: { label: title })
|
|
138
|
+
])
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
class DescriptionList < Base
|
|
144
|
+
def item(term, detail = nil, **attributes, &block)
|
|
145
|
+
part(:div, "description-item", "grid min-w-0 gap-tight py-heading sm:grid-cols-[minmax(0,1fr)_minmax(0,2fr)] sm:gap-content", attributes) do
|
|
146
|
+
view.safe_join([
|
|
147
|
+
view.tag.dt(term, class: "text-label text-muted-foreground", data: { slot: "description-term" }),
|
|
148
|
+
view.tag.dd(block ? view.capture(&block) : detail, class: "m-0 min-w-0 text-body text-foreground [overflow-wrap:anywhere]", data: { slot: "description-detail" })
|
|
149
|
+
])
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "pathname"
|
|
3
|
+
|
|
4
|
+
module Lostcoords::Ui
|
|
5
|
+
# The Tailwind entries a host can import: the document defaults, core.css and one file per opt-in component.
|
|
6
|
+
module Stylesheets
|
|
7
|
+
ROOT = Pathname(File.expand_path("../../../app/assets/tailwind/lostcoords/ui", __dir__))
|
|
8
|
+
|
|
9
|
+
def self.entries
|
|
10
|
+
[ ROOT.join("base.css"), ROOT.join("core.css"), *ROOT.glob("components/*.css").sort ]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# tailwindcss-rails gives an engine one generated import, of engine.css. These proxies give every
|
|
14
|
+
# entry a stable path in the host, such as builds/tailwind/lostcoords_ui/components/dialog.css.
|
|
15
|
+
def self.write(builds)
|
|
16
|
+
directory = Pathname(builds).join("lostcoords_ui")
|
|
17
|
+
FileUtils.rm_rf(directory)
|
|
18
|
+
entries.each do |entry|
|
|
19
|
+
path = directory.join(entry.relative_path_from(ROOT))
|
|
20
|
+
FileUtils.mkdir_p(path.dirname)
|
|
21
|
+
path.write("/* Generated by lostcoords-ui before every Tailwind build; do not edit. */\n@import \"#{entry}\";\n")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/lostcoords/ui/tokens.rb
CHANGED
|
@@ -4,9 +4,32 @@ module Lostcoords::Ui
|
|
|
4
4
|
module Tokens
|
|
5
5
|
THEME_PATH = File.expand_path("../../../app/assets/tailwind/lostcoords/ui/theme.css", __dir__)
|
|
6
6
|
DECLARATION = /(?<![\w-])(--[\w-]+)\s*:/
|
|
7
|
+
COLORS = {
|
|
8
|
+
"--background" => "Page background",
|
|
9
|
+
"--foreground" => "Main text and headings",
|
|
10
|
+
"--card" => "Cards and grouped content",
|
|
11
|
+
"--card-foreground" => "Text on cards",
|
|
12
|
+
"--popover" => "Menus, popovers and dialogs",
|
|
13
|
+
"--popover-foreground" => "Text on menus, popovers and dialogs",
|
|
14
|
+
"--primary" => "Primary actions, links, selected states and keyboard focus",
|
|
15
|
+
"--primary-foreground" => "Text and icons on a solid primary background",
|
|
16
|
+
"--secondary" => "Secondary buttons",
|
|
17
|
+
"--secondary-foreground" => "Text on secondary buttons",
|
|
18
|
+
"--muted" => "Subtle backgrounds: badges, code, skeletons, table headers",
|
|
19
|
+
"--muted-foreground" => "Secondary text, hints and placeholders",
|
|
20
|
+
"--accent" => "Hovered rows, menu items and inline code",
|
|
21
|
+
"--accent-foreground" => "Text on accent backgrounds",
|
|
22
|
+
"--destructive" => "Errors and destructive actions",
|
|
23
|
+
"--success" => "Successful actions and positive feedback",
|
|
24
|
+
"--warning" => "Warnings and items needing attention",
|
|
25
|
+
"--border" => "Dividers and outlines",
|
|
26
|
+
"--input" => "Input and toggle fills",
|
|
27
|
+
"--ring" => "Keyboard focus ring; unset, it follows --primary"
|
|
28
|
+
}.freeze
|
|
29
|
+
FONTS = %w[--font-sans --font-display --font-mono --font-japanese].freeze
|
|
7
30
|
|
|
8
31
|
def self.names
|
|
9
|
-
|
|
32
|
+
(COLORS.keys + FONTS).sort
|
|
10
33
|
end
|
|
11
34
|
end
|
|
12
35
|
|
|
@@ -13,3 +13,14 @@ namespace :lostcoords do
|
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
# Runs with tailwindcss-rails' own engines step, before every tailwindcss:build and tailwindcss:watch.
|
|
18
|
+
# Apps without tailwindcss-rails import the gem's stylesheets by path instead.
|
|
19
|
+
if defined?(Tailwindcss::Engines)
|
|
20
|
+
namespace :tailwindcss do
|
|
21
|
+
task :engines do
|
|
22
|
+
require "lostcoords/ui/stylesheets"
|
|
23
|
+
Lostcoords::Ui::Stylesheets.write(Rails.root.join("app/assets/builds/tailwind"))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lostcoords-ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lost Coordinates
|
|
@@ -86,19 +86,36 @@ files:
|
|
|
86
86
|
- LICENSE
|
|
87
87
|
- README.md
|
|
88
88
|
- app/assets/tailwind/lostcoords/ui/avatar.css
|
|
89
|
+
- app/assets/tailwind/lostcoords/ui/base.css
|
|
89
90
|
- app/assets/tailwind/lostcoords/ui/card.css
|
|
91
|
+
- app/assets/tailwind/lostcoords/ui/choice.css
|
|
92
|
+
- app/assets/tailwind/lostcoords/ui/code-block.css
|
|
93
|
+
- app/assets/tailwind/lostcoords/ui/collapsible.css
|
|
94
|
+
- app/assets/tailwind/lostcoords/ui/components.css
|
|
95
|
+
- app/assets/tailwind/lostcoords/ui/components/code-block.css
|
|
96
|
+
- app/assets/tailwind/lostcoords/ui/components/collapsible.css
|
|
97
|
+
- app/assets/tailwind/lostcoords/ui/components/dialog.css
|
|
98
|
+
- app/assets/tailwind/lostcoords/ui/components/emoji-picker.css
|
|
99
|
+
- app/assets/tailwind/lostcoords/ui/components/menu.css
|
|
100
|
+
- app/assets/tailwind/lostcoords/ui/components/prose.css
|
|
101
|
+
- app/assets/tailwind/lostcoords/ui/components/table.css
|
|
102
|
+
- app/assets/tailwind/lostcoords/ui/components/toast.css
|
|
103
|
+
- app/assets/tailwind/lostcoords/ui/components/tooltip.css
|
|
90
104
|
- app/assets/tailwind/lostcoords/ui/copy-button.css
|
|
105
|
+
- app/assets/tailwind/lostcoords/ui/core.css
|
|
91
106
|
- app/assets/tailwind/lostcoords/ui/dialog.css
|
|
92
|
-
- app/assets/tailwind/lostcoords/ui/disclosure.css
|
|
93
107
|
- app/assets/tailwind/lostcoords/ui/emoji-picker.css
|
|
94
108
|
- app/assets/tailwind/lostcoords/ui/field.css
|
|
95
109
|
- app/assets/tailwind/lostcoords/ui/glass.css
|
|
96
110
|
- app/assets/tailwind/lostcoords/ui/layout.css
|
|
97
111
|
- app/assets/tailwind/lostcoords/ui/popover.css
|
|
98
|
-
- app/assets/tailwind/lostcoords/ui/
|
|
112
|
+
- app/assets/tailwind/lostcoords/ui/prose.css
|
|
99
113
|
- app/assets/tailwind/lostcoords/ui/sheet.css
|
|
100
114
|
- app/assets/tailwind/lostcoords/ui/squircle.css
|
|
115
|
+
- app/assets/tailwind/lostcoords/ui/table.css
|
|
101
116
|
- app/assets/tailwind/lostcoords/ui/theme.css
|
|
117
|
+
- app/assets/tailwind/lostcoords/ui/toast.css
|
|
118
|
+
- app/assets/tailwind/lostcoords/ui/tooltip.css
|
|
102
119
|
- app/assets/tailwind/lostcoords/ui/typography.css
|
|
103
120
|
- app/assets/tailwind/lostcoords/ui/ui.css
|
|
104
121
|
- app/assets/tailwind/lostcoords/ui/utilities.css
|
|
@@ -106,7 +123,11 @@ files:
|
|
|
106
123
|
- app/helpers/lostcoords/ui/badge_helper.rb
|
|
107
124
|
- app/helpers/lostcoords/ui/button_helper.rb
|
|
108
125
|
- app/helpers/lostcoords/ui/card_helper.rb
|
|
126
|
+
- app/helpers/lostcoords/ui/choice_helper.rb
|
|
127
|
+
- app/helpers/lostcoords/ui/code_block_helper.rb
|
|
128
|
+
- app/helpers/lostcoords/ui/collapsible_helper.rb
|
|
109
129
|
- app/helpers/lostcoords/ui/copy_button_helper.rb
|
|
130
|
+
- app/helpers/lostcoords/ui/description_list_helper.rb
|
|
110
131
|
- app/helpers/lostcoords/ui/dialog_helper.rb
|
|
111
132
|
- app/helpers/lostcoords/ui/emoji_picker_helper.rb
|
|
112
133
|
- app/helpers/lostcoords/ui/feedback_helper.rb
|
|
@@ -115,46 +136,56 @@ files:
|
|
|
115
136
|
- app/helpers/lostcoords/ui/icon_helper.rb
|
|
116
137
|
- app/helpers/lostcoords/ui/layout_helper.rb
|
|
117
138
|
- app/helpers/lostcoords/ui/menu_helper.rb
|
|
139
|
+
- app/helpers/lostcoords/ui/navigation_helper.rb
|
|
118
140
|
- app/helpers/lostcoords/ui/primitives_helper.rb
|
|
119
141
|
- app/helpers/lostcoords/ui/reading_helper.rb
|
|
120
142
|
- app/helpers/lostcoords/ui/render_attributes_helper.rb
|
|
121
143
|
- app/helpers/lostcoords/ui/sheet_helper.rb
|
|
122
144
|
- app/helpers/lostcoords/ui/structures_helper.rb
|
|
145
|
+
- app/helpers/lostcoords/ui/table_helper.rb
|
|
123
146
|
- app/helpers/lostcoords/ui/tailwind_helper.rb
|
|
147
|
+
- app/helpers/lostcoords/ui/toast_helper.rb
|
|
148
|
+
- app/helpers/lostcoords/ui/tooltip_helper.rb
|
|
124
149
|
- app/helpers/lostcoords/ui/translation_helper.rb
|
|
125
150
|
- app/helpers/lostcoords/ui/typography_helper.rb
|
|
126
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
127
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
128
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
129
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
130
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
131
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
132
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
133
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
134
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
135
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
136
|
-
- app/javascript/lostcoords/ui/controllers/
|
|
151
|
+
- app/javascript/lostcoords/ui/controllers/lc_code_block_controller.js
|
|
152
|
+
- app/javascript/lostcoords/ui/controllers/lc_collapsible_controller.js
|
|
153
|
+
- app/javascript/lostcoords/ui/controllers/lc_confirm_dialog_controller.js
|
|
154
|
+
- app/javascript/lostcoords/ui/controllers/lc_copy_controller.js
|
|
155
|
+
- app/javascript/lostcoords/ui/controllers/lc_dialog_controller.js
|
|
156
|
+
- app/javascript/lostcoords/ui/controllers/lc_emoji_picker_controller.js
|
|
157
|
+
- app/javascript/lostcoords/ui/controllers/lc_kbd_controller.js
|
|
158
|
+
- app/javascript/lostcoords/ui/controllers/lc_menu_controller.js
|
|
159
|
+
- app/javascript/lostcoords/ui/controllers/lc_popover_controller.js
|
|
160
|
+
- app/javascript/lostcoords/ui/controllers/lc_selection_controller.js
|
|
161
|
+
- app/javascript/lostcoords/ui/controllers/lc_sheet_controller.js
|
|
162
|
+
- app/javascript/lostcoords/ui/controllers/lc_shortcut_controller.js
|
|
163
|
+
- app/javascript/lostcoords/ui/controllers/lc_tabs_controller.js
|
|
164
|
+
- app/javascript/lostcoords/ui/controllers/lc_toast_controller.js
|
|
165
|
+
- app/javascript/lostcoords/ui/controllers/lc_toast_region_controller.js
|
|
166
|
+
- app/javascript/lostcoords/ui/controllers/lc_toggle_controller.js
|
|
167
|
+
- app/javascript/lostcoords/ui/controllers/lc_tooltip_controller.js
|
|
137
168
|
- app/javascript/lostcoords/ui/helpers/catalog_picker.js
|
|
138
169
|
- app/javascript/lostcoords/ui/helpers/context_dialog.js
|
|
139
170
|
- app/javascript/lostcoords/ui/helpers/dialog.js
|
|
140
171
|
- app/javascript/lostcoords/ui/helpers/dismiss_stack.js
|
|
141
172
|
- app/javascript/lostcoords/ui/helpers/floating_panel.js
|
|
142
173
|
- app/javascript/lostcoords/ui/helpers/menu_keys.js
|
|
174
|
+
- app/javascript/lostcoords/ui/helpers/roving_focus.js
|
|
143
175
|
- app/javascript/lostcoords/ui/helpers/sheet_motion.js
|
|
176
|
+
- app/javascript/lostcoords/ui/helpers/shortcuts.js
|
|
144
177
|
- app/javascript/lostcoords/ui/helpers/top_layer.js
|
|
145
178
|
- app/javascript/lostcoords/ui/helpers/viewport.js
|
|
179
|
+
- app/javascript/lostcoords/ui/index.js
|
|
146
180
|
- app/views/lostcoords/ui/components/_emoji_picker.html.erb
|
|
147
181
|
- app/views/lostcoords/ui/components/_emoji_picker_grid.html.erb
|
|
148
|
-
- app/views/lostcoords/ui/components/
|
|
182
|
+
- app/views/lostcoords/ui/components/_lc_code_block.html.erb
|
|
183
|
+
- app/views/lostcoords/ui/components/_lc_collapsible.html.erb
|
|
149
184
|
- app/views/lostcoords/ui/components/_lc_dialog.html.erb
|
|
150
|
-
- app/views/lostcoords/ui/components/
|
|
151
|
-
- app/views/lostcoords/ui/components/_skeleton.html.erb
|
|
152
|
-
- app/views/lostcoords/ui/components/_spinner.html.erb
|
|
153
|
-
- app/views/lostcoords/ui/components/_ui_floating_panel.html.erb
|
|
185
|
+
- app/views/lostcoords/ui/components/_lc_floating_panel.html.erb
|
|
154
186
|
- config/emoji_search_aliases.yml
|
|
155
187
|
- config/emoji_search_names.yml
|
|
156
188
|
- config/emoji_search_names_ja.yml
|
|
157
|
-
- config/helper_renames.json
|
|
158
189
|
- config/importmap.rb
|
|
159
190
|
- config/locales/en.yml
|
|
160
191
|
- config/locales/es.yml
|
|
@@ -164,14 +195,16 @@ files:
|
|
|
164
195
|
- lib/lostcoords/ui/builders.rb
|
|
165
196
|
- lib/lostcoords/ui/components.rb
|
|
166
197
|
- lib/lostcoords/ui/engine.rb
|
|
198
|
+
- lib/lostcoords/ui/form_builder.rb
|
|
167
199
|
- lib/lostcoords/ui/helpers.rb
|
|
168
200
|
- lib/lostcoords/ui/icons.rb
|
|
201
|
+
- lib/lostcoords/ui/parts.rb
|
|
169
202
|
- lib/lostcoords/ui/rails.rb
|
|
170
203
|
- lib/lostcoords/ui/resources.rb
|
|
204
|
+
- lib/lostcoords/ui/stylesheets.rb
|
|
171
205
|
- lib/lostcoords/ui/tokens.rb
|
|
172
206
|
- lib/lostcoords/ui/version.rb
|
|
173
207
|
- lib/tasks/lostcoords_ui.rake
|
|
174
|
-
- script/migrate_helpers.rb
|
|
175
208
|
- vendor/heroicons/LICENSE
|
|
176
209
|
- vendor/heroicons/icons.json
|
|
177
210
|
homepage: https://github.com/lostcoords/ui
|