unmagic-components 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +73 -1
- data/README.md +493 -1
- data/app/assets/javascripts/unmagic/components/autogrow.js +74 -0
- data/app/assets/javascripts/unmagic/components/clipboard.js +63 -0
- data/app/assets/javascripts/unmagic/components/confirm.js +102 -0
- data/app/assets/javascripts/unmagic/components/dialog.js +53 -0
- data/app/assets/javascripts/unmagic/components/menu.js +139 -0
- data/app/assets/javascripts/unmagic/components/modal.js +182 -0
- data/app/assets/javascripts/unmagic/components/tabs.js +94 -0
- data/app/assets/javascripts/unmagic/components/time.js +169 -0
- data/app/assets/javascripts/unmagic/components/toasts.js +171 -0
- data/app/assets/javascripts/unmagic/components/tooltip.js +133 -0
- data/app/assets/javascripts/unmagic/components/uuid_input.js +70 -0
- data/app/assets/javascripts/unmagic/components.js +19 -0
- data/app/assets/stylesheets/unmagic/components.css +927 -0
- data/config/importmap.rb +5 -1
- data/lib/unmagic/components/action_view_helpers.rb +373 -0
- data/lib/unmagic/components/autogrow.rb +13 -0
- data/lib/unmagic/components/badge.rb +21 -0
- data/lib/unmagic/components/button.rb +28 -0
- data/lib/unmagic/components/callout.rb +60 -0
- data/lib/unmagic/components/card.rb +67 -0
- data/lib/unmagic/components/configuration.rb +19 -1
- data/lib/unmagic/components/confirm_template.rb +46 -0
- data/lib/unmagic/components/copy_button.rb +50 -0
- data/lib/unmagic/components/detail_list.rb +13 -3
- data/lib/unmagic/components/dialog.rb +79 -0
- data/lib/unmagic/components/dialog_responder.rb +46 -0
- data/lib/unmagic/components/engine.rb +13 -4
- data/lib/unmagic/components/form_builder.rb +24 -0
- data/lib/unmagic/components/icons.rb +40 -0
- data/lib/unmagic/components/local_time.rb +64 -0
- data/lib/unmagic/components/menu.rb +82 -0
- data/lib/unmagic/components/modal.rb +75 -0
- data/lib/unmagic/components/page_header.rb +96 -0
- data/lib/unmagic/components/skeleton.rb +97 -0
- data/lib/unmagic/components/tabs.rb +95 -0
- data/lib/unmagic/components/toast.rb +54 -0
- data/lib/unmagic/components/toasts.rb +46 -0
- data/lib/unmagic/components/tooltip.rb +32 -0
- data/lib/unmagic/components/turbo_stream_actions.rb +19 -0
- data/lib/unmagic/components/uuid_input.rb +24 -0
- data/lib/unmagic/components/version.rb +1 -1
- data/lib/unmagic/components.rb +21 -0
- metadata +42 -7
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unmagic
|
|
4
|
+
module Components
|
|
5
|
+
# Placeholder shapes for blocking out an interface while it loads. The same
|
|
6
|
+
# object is what `skeleton do |s|` yields and what the standalone helpers
|
|
7
|
+
# (skeleton_text, skeleton_circle, …) call, so the two forms can't differ. See
|
|
8
|
+
# ActionViewHelpers#skeleton.
|
|
9
|
+
#
|
|
10
|
+
# Every shape is hidden from assistive technology; a group says "Loading…" once
|
|
11
|
+
# for all of them.
|
|
12
|
+
class Skeleton
|
|
13
|
+
BUTTON_SIZES = %i[small large].freeze
|
|
14
|
+
|
|
15
|
+
# The last line of a paragraph is shorter, so a block of text reads as one.
|
|
16
|
+
LAST_LINE_WIDTH = "60%"
|
|
17
|
+
|
|
18
|
+
# A status region around some shapes, announced once.
|
|
19
|
+
def self.group(view, label: nil, **options)
|
|
20
|
+
view.tag.div(**options, role: "status", class: view.class_names("UnmagicSkeletonGroup", options[:class])) do
|
|
21
|
+
view.safe_join [ hidden_label(view, label), yield ]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# The words a screen reader hears in place of the shapes.
|
|
26
|
+
def self.hidden_label(view, label = nil)
|
|
27
|
+
label ||= I18n.t("unmagic.components.skeleton.loading", default: "Loading…")
|
|
28
|
+
view.tag.span(label, class: "UnmagicVisuallyHidden")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def initialize(view)
|
|
32
|
+
@view = view
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# A line of text, sized by the font it sits in: it takes exactly one line box,
|
|
36
|
+
# so the text that replaces it lands in the same space. lines: renders a
|
|
37
|
+
# paragraph instead, with a shorter last line; width: then applies to the whole.
|
|
38
|
+
# class: and style: go on the line, width: on its bar.
|
|
39
|
+
def text(width: nil, lines: 1, **options)
|
|
40
|
+
raise ArgumentError, "skeleton text needs at least one line (got #{lines.inspect})" unless lines.to_i >= 1
|
|
41
|
+
|
|
42
|
+
return line(width: width, **options) if lines == 1
|
|
43
|
+
|
|
44
|
+
lines = Array.new(lines) { |index| line(width: (LAST_LINE_WIDTH if index == lines - 1)) }
|
|
45
|
+
view.tag.span(view.safe_join(lines), **options, "aria-hidden": "true",
|
|
46
|
+
class: view.class_names("UnmagicSkeletonText", options[:class]), style: style(options[:style], width: width))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# An avatar or a round icon.
|
|
50
|
+
def circle(size: "2.5rem", **options)
|
|
51
|
+
shape("circle", width: size, height: size, **options)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# An image, a chart, a map: anything rectangular. Full width unless given one.
|
|
55
|
+
def block(height: "8rem", width: nil, **options)
|
|
56
|
+
shape("block", width: width, height: height, **options)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The size of a button_classes button, so a row of actions keeps its height.
|
|
60
|
+
def button(size: nil, width: nil, **options)
|
|
61
|
+
unless size.nil? || BUTTON_SIZES.include?(size)
|
|
62
|
+
raise ArgumentError, "unknown skeleton button size #{size.inspect} (expected one of #{BUTTON_SIZES.inspect})"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
shape("button", width: width, modifier: ("UnmagicSkeleton--#{size}" if size), **options)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
attr_reader :view
|
|
71
|
+
|
|
72
|
+
# One line box of the surrounding font (1lh tall) with the bar centred in it.
|
|
73
|
+
# The bar is thinner than the text so it reads as a placeholder, and the line
|
|
74
|
+
# box, not margins around the bar, supplies the rest of the height: margins
|
|
75
|
+
# would collapse through their container and the skeleton would come out
|
|
76
|
+
# shorter than the text it stands in for.
|
|
77
|
+
def line(width: nil, **options)
|
|
78
|
+
view.tag.span(**options, "aria-hidden": "true",
|
|
79
|
+
class: view.class_names("UnmagicSkeletonLine", options[:class]), style: style(options[:style])) do
|
|
80
|
+
shape("text", width: width)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def shape(kind, width: nil, height: nil, modifier: nil, **options)
|
|
85
|
+
view.tag.span(**options, "aria-hidden": "true",
|
|
86
|
+
class: view.class_names("UnmagicSkeleton", "UnmagicSkeleton--#{kind}", modifier, options[:class]),
|
|
87
|
+
style: style(options[:style], width: width, height: height))
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def style(extra, **dimensions)
|
|
91
|
+
rules = dimensions.compact.map { |property, value| "#{property}: #{value}" }
|
|
92
|
+
rules << extra if extra.present?
|
|
93
|
+
rules.join("; ").presence
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module Unmagic
|
|
6
|
+
module Components
|
|
7
|
+
# A row of tabs: either panels switched in the page, or links to separate URLs.
|
|
8
|
+
# See ActionViewHelpers#tabs.
|
|
9
|
+
class Tabs
|
|
10
|
+
def initialize(view, id: nil, **options)
|
|
11
|
+
@view = view
|
|
12
|
+
@id = id
|
|
13
|
+
@base = id || "unmagic_tabs_#{SecureRandom.hex(4)}"
|
|
14
|
+
@options = options
|
|
15
|
+
@tabs = []
|
|
16
|
+
@panels = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def tab(label, disabled: nil, href: nil, active: false)
|
|
20
|
+
@tabs << { label: label, disabled: disabled, href: href, active: active }
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def panel(&block)
|
|
25
|
+
@panels << view.capture(&block)
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render
|
|
30
|
+
links = @tabs.any? { |tab| tab[:href] }
|
|
31
|
+
raise ArgumentError, "tabs can't mix href: tabs with panels" if links && @panels.any?
|
|
32
|
+
|
|
33
|
+
links ? links_bar : switcher
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
attr_reader :view
|
|
39
|
+
|
|
40
|
+
delegate :tag, :safe_join, to: :view, private: true
|
|
41
|
+
|
|
42
|
+
def enabled = @tabs.reject { |tab| tab[:disabled] }
|
|
43
|
+
|
|
44
|
+
# Panels line up with the enabled tabs in order; a disabled tab takes none.
|
|
45
|
+
def switcher
|
|
46
|
+
if enabled.size != @panels.size
|
|
47
|
+
raise ArgumentError, "tabs has #{enabled.size} enabled tabs but #{@panels.size} panels"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
selected = enabled.index { |tab| tab[:active] } || 0
|
|
51
|
+
index = -1
|
|
52
|
+
|
|
53
|
+
list = @tabs.map do |tab|
|
|
54
|
+
next disabled_tab(tab) if tab[:disabled]
|
|
55
|
+
|
|
56
|
+
index += 1
|
|
57
|
+
tag.button tab[:label], type: "button", role: "tab", id: "#{@base}_tab_#{index}", class: "UnmagicTabs__tab",
|
|
58
|
+
"aria-controls": "#{@base}_panel_#{index}", "aria-selected": (index == selected).to_s,
|
|
59
|
+
tabindex: (index == selected ? 0 : -1)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
panels = @panels.each_with_index.map do |content, i|
|
|
63
|
+
tag.div content, role: "tabpanel", id: "#{@base}_panel_#{i}", class: "UnmagicTabs__panel",
|
|
64
|
+
"aria-labelledby": "#{@base}_tab_#{i}", tabindex: 0, hidden: i != selected
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
view.content_tag("unmagic-tabs", **@options, id: @id, class: view.class_names("UnmagicTabs", @options[:class])) do
|
|
68
|
+
safe_join [ tag.div(safe_join(list), role: "tablist", class: "UnmagicTabs__list"), *panels ]
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Each tab its own URL, rendered on the server: navigation, not a widget, so it
|
|
73
|
+
# needs no script and marks the current page rather than a selected tab.
|
|
74
|
+
def links_bar
|
|
75
|
+
tag.nav(**@options, id: @id, class: view.class_names("UnmagicTabs", @options[:class])) do
|
|
76
|
+
tag.div class: "UnmagicTabs__list" do
|
|
77
|
+
safe_join(@tabs.map do |tab|
|
|
78
|
+
next disabled_tab(tab) if tab[:disabled]
|
|
79
|
+
|
|
80
|
+
view.link_to tab[:label], tab[:href], class: "UnmagicTabs__tab", "aria-current": ("page" if tab[:active])
|
|
81
|
+
end)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def disabled_tab(tab)
|
|
87
|
+
reason = tab[:disabled] unless tab[:disabled] == true
|
|
88
|
+
|
|
89
|
+
tag.span class: "UnmagicTabs__tab", "aria-disabled": "true" do
|
|
90
|
+
safe_join [ tab[:label], (tag.span("(#{reason})", class: "UnmagicTabs__reason") if reason) ].compact, " "
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unmagic
|
|
4
|
+
module Components
|
|
5
|
+
# One toast, and the inert <template> the <unmagic-toasts> element pops it from.
|
|
6
|
+
# A toast arrives as a template rather than as the toast itself so a morph or a
|
|
7
|
+
# stream can deliver it without it rendering in place; the element clones it
|
|
8
|
+
# into the stack. See ActionViewHelpers#flash_toasts.
|
|
9
|
+
class Toast
|
|
10
|
+
TONES = %i[good warn bad info].freeze
|
|
11
|
+
|
|
12
|
+
# The <unmagic-toasts> element's id, which a stream appends templates to.
|
|
13
|
+
TARGET = "unmagic_toasts"
|
|
14
|
+
|
|
15
|
+
def initialize(view, message, tone:)
|
|
16
|
+
unless TONES.include?(tone)
|
|
17
|
+
raise ArgumentError, "unknown toast tone #{tone.inspect} (expected one of #{TONES.inspect})"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@view = view
|
|
21
|
+
@message = message
|
|
22
|
+
@tone = tone
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def template
|
|
26
|
+
tag.template(render, data: { unmagic_toast_template: "" })
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# A bad toast interrupts (role="alert"); the rest are announced politely by
|
|
30
|
+
# the stack's own live region.
|
|
31
|
+
def render
|
|
32
|
+
tag.div class: "UnmagicToast UnmagicToast--#{@tone}", role: ("alert" if @tone == :bad),
|
|
33
|
+
data: { unmagic_toast: "" } do
|
|
34
|
+
safe_join [
|
|
35
|
+
Icons.svg(view, Icons::TONE_ICONS.fetch(@tone), class: "UnmagicToast__icon"),
|
|
36
|
+
tag.div(@message, class: "UnmagicToast__message"),
|
|
37
|
+
tag.button(
|
|
38
|
+
Icons.svg(view, :x),
|
|
39
|
+
type: "button", class: "UnmagicToast__dismiss",
|
|
40
|
+
"aria-label": I18n.t("unmagic.components.toast.dismiss", default: "Dismiss"),
|
|
41
|
+
data: { unmagic_toast_dismiss: "" }
|
|
42
|
+
)
|
|
43
|
+
]
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
attr_reader :view
|
|
50
|
+
|
|
51
|
+
delegate :tag, :safe_join, to: :view, private: true
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unmagic
|
|
4
|
+
module Components
|
|
5
|
+
# The mount point for toasts: the <unmagic-toasts> element, its stack, and a
|
|
6
|
+
# template for each of the request's flashes. See ActionViewHelpers#flash_toasts.
|
|
7
|
+
class Toasts
|
|
8
|
+
def initialize(view, flashes, duration:)
|
|
9
|
+
@view = view
|
|
10
|
+
@flashes = flashes
|
|
11
|
+
@duration = duration
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# The stack is data-turbo-permanent, so a toast on screen — and its dismiss
|
|
15
|
+
# timer — survives a Drive visit or a morph refresh. It is a manual popover
|
|
16
|
+
# so the element can lift it into the top layer, above an open dialog.
|
|
17
|
+
def render
|
|
18
|
+
view.content_tag("unmagic-toasts", id: Toast::TARGET, duration: @duration) do
|
|
19
|
+
safe_join [
|
|
20
|
+
tag.div(class: "UnmagicToasts__stack", id: "#{Toast::TARGET}_stack", popover: "manual",
|
|
21
|
+
"aria-live": "polite", data: { turbo_permanent: "" }),
|
|
22
|
+
*templates
|
|
23
|
+
]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
attr_reader :view
|
|
30
|
+
|
|
31
|
+
delegate :tag, :safe_join, to: :view, private: true
|
|
32
|
+
|
|
33
|
+
def templates
|
|
34
|
+
tones = Components.configuration.flash_tones
|
|
35
|
+
|
|
36
|
+
@flashes.each_with_object([]) do |(type, messages), templates|
|
|
37
|
+
tone = tones.fetch(type.to_s, :info)
|
|
38
|
+
|
|
39
|
+
Array(messages).each do |message|
|
|
40
|
+
templates << Toast.new(view, message, tone: tone).template if message.present?
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unmagic
|
|
4
|
+
module Components
|
|
5
|
+
# A hint shown on hover or focus. See ActionViewHelpers#tooltip.
|
|
6
|
+
class Tooltip
|
|
7
|
+
PLACEMENTS = %i[top bottom].freeze
|
|
8
|
+
|
|
9
|
+
def initialize(view, text:, placement:, term:, **options)
|
|
10
|
+
unless PLACEMENTS.include?(placement)
|
|
11
|
+
raise ArgumentError, "unknown tooltip placement #{placement.inspect} (expected one of #{PLACEMENTS.inspect})"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@view = view
|
|
15
|
+
@text = text
|
|
16
|
+
@placement = placement
|
|
17
|
+
@term = term
|
|
18
|
+
@options = options
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def render(content)
|
|
22
|
+
classes = view.class_names("UnmagicTooltip", { "UnmagicTooltip--term" => @term }, @options[:class])
|
|
23
|
+
|
|
24
|
+
view.content_tag("unmagic-tooltip", content, **@options, text: @text, placement: @placement, class: classes)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
attr_reader :view
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unmagic
|
|
4
|
+
module Components
|
|
5
|
+
# Mixed into turbo-rails' stream tag builder by the engine.
|
|
6
|
+
module TurboStreamActions
|
|
7
|
+
# Pops a toast from a stream response — for something that happened outside
|
|
8
|
+
# the flash cycle, where there is no redirect or render to carry a flash.
|
|
9
|
+
#
|
|
10
|
+
# render turbo_stream: turbo_stream.toast("Invitation sent.")
|
|
11
|
+
# render turbo_stream: turbo_stream.toast("Couldn't reach Slack.", tone: :bad)
|
|
12
|
+
#
|
|
13
|
+
# tone: :good (default), :warn, :bad or :info. Needs flash_toasts on the page.
|
|
14
|
+
def toast(message, tone: :good)
|
|
15
|
+
append Toast::TARGET, Toast.new(@view_context, message, tone: tone).template
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module Unmagic
|
|
6
|
+
module Components
|
|
7
|
+
# A hidden field holding a client-known UUIDv7. See FormBuilder#uuid_field.
|
|
8
|
+
class UuidInput
|
|
9
|
+
def initialize(view, name, **options)
|
|
10
|
+
@view = view
|
|
11
|
+
@name = name
|
|
12
|
+
@options = options
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# The server renders an id of its own so the form still submits one without
|
|
16
|
+
# the script; the element replaces it as it upgrades, and again on each reset.
|
|
17
|
+
def render
|
|
18
|
+
@view.content_tag("unmagic-uuid-input", name: @name, **@options) do
|
|
19
|
+
@view.hidden_field_tag(@name, SecureRandom.uuid_v7, id: nil, autocomplete: "off")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/unmagic/components.rb
CHANGED
|
@@ -14,8 +14,29 @@ require_relative "components/table_tag"
|
|
|
14
14
|
require_relative "components/table/column"
|
|
15
15
|
require_relative "components/table"
|
|
16
16
|
require_relative "components/form_builder"
|
|
17
|
+
require_relative "components/skeleton"
|
|
17
18
|
require_relative "components/detail_list/item"
|
|
18
19
|
require_relative "components/detail_list"
|
|
20
|
+
require_relative "components/icons"
|
|
21
|
+
require_relative "components/button"
|
|
22
|
+
require_relative "components/dialog"
|
|
23
|
+
require_relative "components/modal"
|
|
24
|
+
require_relative "components/confirm_template"
|
|
25
|
+
require_relative "components/dialog_responder"
|
|
26
|
+
require_relative "components/toast"
|
|
27
|
+
require_relative "components/toasts"
|
|
28
|
+
require_relative "components/turbo_stream_actions"
|
|
29
|
+
require_relative "components/badge"
|
|
30
|
+
require_relative "components/callout"
|
|
31
|
+
require_relative "components/card"
|
|
32
|
+
require_relative "components/page_header"
|
|
33
|
+
require_relative "components/local_time"
|
|
34
|
+
require_relative "components/tooltip"
|
|
35
|
+
require_relative "components/menu"
|
|
36
|
+
require_relative "components/tabs"
|
|
37
|
+
require_relative "components/copy_button"
|
|
38
|
+
require_relative "components/autogrow"
|
|
39
|
+
require_relative "components/uuid_input"
|
|
19
40
|
require_relative "components/action_view_helpers"
|
|
20
41
|
# ActionView pulls in a partial Rails namespace, so this checks for the constant it
|
|
21
42
|
# actually needs rather than for Rails.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: unmagic-components
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keith Pitt
|
|
@@ -79,11 +79,12 @@ dependencies:
|
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '13.0'
|
|
82
|
-
description:
|
|
83
|
-
index tables
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
description: UI components for Rails views in the spirit of form_for. Builders for
|
|
83
|
+
index tables (sortable, deferred, kept live by Turbo Streams), detail lists, forms,
|
|
84
|
+
cards, page headers and loading skeletons; a Turbo Frame modal, a confirm dialog
|
|
85
|
+
and flash toasts; menus, tabs, tooltips, local times, copy buttons, autogrowing
|
|
86
|
+
textareas and UUID inputs as self-registering custom elements. Plain helpers and
|
|
87
|
+
CSS themed through custom properties, with no Tailwind or Stimulus required.
|
|
87
88
|
email:
|
|
88
89
|
- keith@unreasonable-magic.com
|
|
89
90
|
executables: []
|
|
@@ -93,22 +94,55 @@ files:
|
|
|
93
94
|
- CHANGELOG.md
|
|
94
95
|
- LICENSE
|
|
95
96
|
- README.md
|
|
97
|
+
- app/assets/javascripts/unmagic/components.js
|
|
98
|
+
- app/assets/javascripts/unmagic/components/autogrow.js
|
|
99
|
+
- app/assets/javascripts/unmagic/components/clipboard.js
|
|
100
|
+
- app/assets/javascripts/unmagic/components/confirm.js
|
|
101
|
+
- app/assets/javascripts/unmagic/components/dialog.js
|
|
102
|
+
- app/assets/javascripts/unmagic/components/menu.js
|
|
103
|
+
- app/assets/javascripts/unmagic/components/modal.js
|
|
104
|
+
- app/assets/javascripts/unmagic/components/tabs.js
|
|
105
|
+
- app/assets/javascripts/unmagic/components/time.js
|
|
106
|
+
- app/assets/javascripts/unmagic/components/toasts.js
|
|
107
|
+
- app/assets/javascripts/unmagic/components/tooltip.js
|
|
96
108
|
- app/assets/javascripts/unmagic/components/upsert.js
|
|
109
|
+
- app/assets/javascripts/unmagic/components/uuid_input.js
|
|
97
110
|
- app/assets/stylesheets/unmagic/components.css
|
|
98
111
|
- config/importmap.rb
|
|
99
112
|
- lib/unmagic-components.rb
|
|
100
113
|
- lib/unmagic/components.rb
|
|
101
114
|
- lib/unmagic/components/action_view_helpers.rb
|
|
115
|
+
- lib/unmagic/components/autogrow.rb
|
|
116
|
+
- lib/unmagic/components/badge.rb
|
|
117
|
+
- lib/unmagic/components/button.rb
|
|
118
|
+
- lib/unmagic/components/callout.rb
|
|
119
|
+
- lib/unmagic/components/card.rb
|
|
102
120
|
- lib/unmagic/components/configuration.rb
|
|
121
|
+
- lib/unmagic/components/confirm_template.rb
|
|
122
|
+
- lib/unmagic/components/copy_button.rb
|
|
103
123
|
- lib/unmagic/components/detail_list.rb
|
|
104
124
|
- lib/unmagic/components/detail_list/item.rb
|
|
125
|
+
- lib/unmagic/components/dialog.rb
|
|
126
|
+
- lib/unmagic/components/dialog_responder.rb
|
|
105
127
|
- lib/unmagic/components/engine.rb
|
|
106
128
|
- lib/unmagic/components/form_builder.rb
|
|
129
|
+
- lib/unmagic/components/icons.rb
|
|
130
|
+
- lib/unmagic/components/local_time.rb
|
|
131
|
+
- lib/unmagic/components/menu.rb
|
|
132
|
+
- lib/unmagic/components/modal.rb
|
|
133
|
+
- lib/unmagic/components/page_header.rb
|
|
107
134
|
- lib/unmagic/components/renderers/empty_state.rb
|
|
108
135
|
- lib/unmagic/components/renderers/pagination.rb
|
|
136
|
+
- lib/unmagic/components/skeleton.rb
|
|
109
137
|
- lib/unmagic/components/table.rb
|
|
110
138
|
- lib/unmagic/components/table/column.rb
|
|
111
139
|
- lib/unmagic/components/table_tag.rb
|
|
140
|
+
- lib/unmagic/components/tabs.rb
|
|
141
|
+
- lib/unmagic/components/toast.rb
|
|
142
|
+
- lib/unmagic/components/toasts.rb
|
|
143
|
+
- lib/unmagic/components/tooltip.rb
|
|
144
|
+
- lib/unmagic/components/turbo_stream_actions.rb
|
|
145
|
+
- lib/unmagic/components/uuid_input.rb
|
|
112
146
|
- lib/unmagic/components/version.rb
|
|
113
147
|
homepage: https://github.com/unreasonable-magic/unmagic-components
|
|
114
148
|
licenses:
|
|
@@ -133,5 +167,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
133
167
|
requirements: []
|
|
134
168
|
rubygems_version: 4.0.10
|
|
135
169
|
specification_version: 4
|
|
136
|
-
summary:
|
|
170
|
+
summary: 'Server-rendered UI components for Rails: tables, forms, dialogs, toasts
|
|
171
|
+
and more'
|
|
137
172
|
test_files: []
|