ariadne_view_components 0.0.47-x86_64-linux → 0.0.49-x86_64-linux
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 +29 -25
- data/app/assets/javascripts/ariadne_view_components.js +2 -2
- data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
- data/app/assets/javascripts/components/ariadne/accumulator_controller/accumulator_controller.d.ts +22 -0
- data/app/assets/javascripts/components/ariadne/dropdown/menu_component.d.ts +1 -0
- data/app/assets/javascripts/components/ariadne/events_controller/events_controller.d.ts +4 -0
- data/app/assets/javascripts/components/ariadne/options_controller/options_controller.d.ts +40 -0
- data/app/assets/javascripts/components/ariadne/outlet_manager_controller/outlet_manager_controller.d.ts +42 -0
- data/app/assets/javascripts/components/ariadne/string_match_controller/string_match_controller.d.ts +27 -0
- data/app/assets/javascripts/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.d.ts +44 -0
- data/app/assets/javascripts/components/ariadne/toggleable_controller/toggleable_controller.d.ts +34 -0
- data/app/assets/stylesheets/ariadne_view_components.css +3 -3
- data/app/components/ariadne/accumulator_controller/accumulator_controller.d.ts +22 -0
- data/app/components/ariadne/accumulator_controller/accumulator_controller.js +39 -0
- data/app/components/ariadne/accumulator_controller/accumulator_controller.ts +48 -0
- data/app/components/ariadne/action_card_component.html.erb +11 -0
- data/app/components/ariadne/action_card_component.rb +45 -0
- data/app/components/ariadne/ariadne.js +10 -0
- data/app/components/ariadne/ariadne.ts +10 -0
- data/app/components/ariadne/bottom_tab_component.html.erb +4 -0
- data/app/components/ariadne/bottom_tab_component.rb +44 -0
- data/app/components/ariadne/bottom_tab_nav_component.html.erb +5 -0
- data/app/components/ariadne/bottom_tab_nav_component.rb +33 -0
- data/app/components/ariadne/breadcrumbs_component.html.erb +13 -0
- data/app/components/ariadne/breadcrumbs_component.rb +31 -0
- data/app/components/ariadne/checkbox_component.html.erb +5 -0
- data/app/components/ariadne/checkbox_component.rb +43 -0
- data/app/components/ariadne/close_button_component.html.erb +4 -0
- data/app/components/ariadne/close_button_component.rb +33 -0
- data/app/components/ariadne/combobox_component.html.erb +14 -0
- data/app/components/ariadne/combobox_component.rb +76 -0
- data/app/components/ariadne/dropdown/menu_component.d.ts +1 -0
- data/app/components/ariadne/dropdown/menu_component.js +1 -0
- data/app/components/ariadne/events_controller/events_controller.d.ts +4 -0
- data/app/components/ariadne/events_controller/events_controller.js +6 -0
- data/app/components/ariadne/events_controller/events_controller.ts +7 -0
- data/app/components/ariadne/layout_component.html.erb +21 -0
- data/app/components/ariadne/layout_component.rb +69 -0
- data/app/components/ariadne/modal_component.html.erb +11 -0
- data/app/components/ariadne/modal_component.rb +88 -0
- data/app/components/ariadne/options_controller/options_controller.d.ts +40 -0
- data/app/components/ariadne/options_controller/options_controller.js +98 -0
- data/app/components/ariadne/options_controller/options_controller.ts +132 -0
- data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.d.ts +42 -0
- data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.js +237 -0
- data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.ts +278 -0
- data/app/components/ariadne/popover_component.html.erb +10 -0
- data/app/components/ariadne/popover_component.rb +81 -0
- data/app/components/ariadne/progress_bar_component.html.erb +5 -0
- data/app/components/ariadne/progress_bar_component.rb +63 -0
- data/app/components/ariadne/relative_time_component.html.erb +3 -0
- data/app/components/ariadne/relative_time_component.rb +61 -0
- data/app/components/ariadne/show_more_button_component.html.erb +11 -0
- data/app/components/ariadne/show_more_button_component.rb +47 -0
- data/app/components/ariadne/spinner_component.html.erb +16 -0
- data/app/components/ariadne/spinner_component.rb +45 -0
- data/app/components/ariadne/string_match_controller/string_match_controller.d.ts +27 -0
- data/app/components/ariadne/string_match_controller/string_match_controller.js +51 -0
- data/app/components/ariadne/string_match_controller/string_match_controller.ts +64 -0
- data/app/components/ariadne/subheader_component.html.erb +11 -0
- data/app/components/ariadne/subheader_component.rb +65 -0
- data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.d.ts +44 -0
- data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.js +153 -0
- data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.ts +192 -0
- data/app/components/ariadne/toggle_component/toggle_component.html.erb +15 -0
- data/app/components/ariadne/toggle_component.rb +95 -0
- data/app/components/ariadne/toggleable_controller/toggleable_controller.d.ts +34 -0
- data/app/components/ariadne/toggleable_controller/toggleable_controller.js +54 -0
- data/app/components/ariadne/toggleable_controller/toggleable_controller.ts +77 -0
- data/lib/ariadne/view_components/version.rb +1 -1
- data/static/arguments.yml +50 -0
- data/static/audited_at.json +17 -0
- data/static/classes.yml +209 -173
- data/static/constants.json +356 -0
- data/static/statuses.json +17 -0
- data/tailwind.config.js +7 -7
- metadata +75 -12
- /data/app/assets/javascripts/{ariadne-form.d.ts → components/ariadne/ariadne-form.d.ts} +0 -0
- /data/app/assets/javascripts/{ariadne.d.ts → components/ariadne/ariadne.d.ts} +0 -0
- /data/app/assets/javascripts/{clipboard_copy_component → components/ariadne/clipboard_copy_component}/clipboard-copy-component.d.ts +0 -0
- /data/app/assets/javascripts/{rich_text_area_component → components/ariadne/rich_text_area_component}/rich-text-area-component.d.ts +0 -0
- /data/app/assets/javascripts/{slideover_component → components/ariadne/slideover_component}/slideover-component.d.ts +0 -0
- /data/app/assets/javascripts/{tab_container_component → components/ariadne/tab_container_component}/tab-container-component.d.ts +0 -0
- /data/app/assets/javascripts/{tab_nav_component → components/ariadne/tab_nav_component}/tab-nav-component.d.ts +0 -0
- /data/app/assets/javascripts/{time_ago_component → components/ariadne/time_ago_component}/time-ago-component.d.ts +0 -0
- /data/app/assets/javascripts/{tooltip_component → components/ariadne/tooltip_component}/tooltip-component.d.ts +0 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Simple button with X icon to close models and such
|
5
|
+
class CloseButtonComponent < Ariadne::Component
|
6
|
+
DEFAULT_TAG = :button
|
7
|
+
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
8
|
+
|
9
|
+
DEFAULT_CLASSES = "ariadne-border-none ariadne-bg-transparent ariadne-shadow-none hover:ariadne-bg-transparent"
|
10
|
+
|
11
|
+
# @example Default
|
12
|
+
#
|
13
|
+
# <%= render(Ariadne::CloseButtonComponent.new) { "Example" } %>
|
14
|
+
#
|
15
|
+
# @param tag [Symbol, String] The rendered tag name.
|
16
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
17
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
18
|
+
def initialize(tag: DEFAULT_TAG, classes: "", icon_classes: "", size: :xs, aria_label: nil, scheme: :none, attributes: {})
|
19
|
+
raise ArgumentError, "An 'aria_label' argument is required to use a Close Button. Include as much detail as you can about what the button will be closing." if aria_label.blank?
|
20
|
+
|
21
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
22
|
+
@attributes = { "aria-label": aria_label }.merge(attributes)
|
23
|
+
@classes = merge_class_names(
|
24
|
+
DEFAULT_CLASSES,
|
25
|
+
classes,
|
26
|
+
)
|
27
|
+
|
28
|
+
@scheme = scheme
|
29
|
+
@size = size
|
30
|
+
@icon_classses = icon_classes
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do %>
|
2
|
+
<%= render Ariadne::BaseComponent.new(tag: :input, classes: @input_classes, attributes: @input_attributes) do %>
|
3
|
+
<% if icon? %>
|
4
|
+
<%= icon %>
|
5
|
+
<% else %>
|
6
|
+
<%= Ariadne::HeroiconComponent.new(icon: :cube, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE) %>
|
7
|
+
<% end %>
|
8
|
+
<% end %>
|
9
|
+
<%= render Ariadne::BaseComponent.new(tag: @options_wrapper_tag, classes: @options_wrapper_classes, attributes: @options_wrapper_attributes) do %>
|
10
|
+
<% options.each do |option| %>
|
11
|
+
<%= option %>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
<% end %>
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Input with a dropdown selection
|
5
|
+
class ComboboxComponent < Ariadne::Component
|
6
|
+
DEFAULT_TAG = :div
|
7
|
+
DEFAULT_OPTIONS_WRAPPER_TAG = :div
|
8
|
+
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
9
|
+
|
10
|
+
DEFAULT_CLASSES = {
|
11
|
+
wrapper: "ariadne-group ariadne-w-fit ariadne-relative",
|
12
|
+
options_wrapper: "group-data-[toggleable-state-value=false]:ariadne-hidden ariadne-absolute ariadne-w-full ariadne-z-10",
|
13
|
+
input: "",
|
14
|
+
}
|
15
|
+
|
16
|
+
DEFAULT_ATTRIBUTES = {
|
17
|
+
wrapper: {
|
18
|
+
"data-controller": "options toggleable string-match",
|
19
|
+
},
|
20
|
+
options_wrapper: {},
|
21
|
+
input: {
|
22
|
+
type: "text",
|
23
|
+
autocomplete: "off",
|
24
|
+
"data-action": "input->string-match#change focusin->toggleable#on",
|
25
|
+
},
|
26
|
+
}
|
27
|
+
|
28
|
+
renders_many :options
|
29
|
+
|
30
|
+
renders_one :icon, Ariadne::HeroiconComponent
|
31
|
+
|
32
|
+
# @example Default
|
33
|
+
#
|
34
|
+
# <%= render(Ariadne::ComboboxComponent.new) { "Example" } %>
|
35
|
+
#
|
36
|
+
# @param tag [Symbol, String] The rendered tag name.
|
37
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
38
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
39
|
+
def initialize(
|
40
|
+
tag: DEFAULT_TAG,
|
41
|
+
classes: "",
|
42
|
+
attributes: {},
|
43
|
+
options_tag: DEFAULT_OPTIONS_WRAPPER_TAG,
|
44
|
+
options_wrapper_classes: "",
|
45
|
+
options_wrapper_attributes: {},
|
46
|
+
close_on_outside_click: true,
|
47
|
+
close_on_select: false,
|
48
|
+
initially_open: false,
|
49
|
+
single_selection: false,
|
50
|
+
toggleable_options: true, # Do options unselect if clicked a second time
|
51
|
+
input_classes: "",
|
52
|
+
input_attributes: {}
|
53
|
+
)
|
54
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
55
|
+
@classes = merge_class_names(DEFAULT_CLASSES[:wrapper], classes)
|
56
|
+
@attributes = DEFAULT_ATTRIBUTES[:wrapper]
|
57
|
+
.merge({
|
58
|
+
"data-options-is-multi-value": !single_selection,
|
59
|
+
"data-options-toggleable-value": toggleable_options,
|
60
|
+
"data-toggleable-close-on-outside-click-value": close_on_outside_click,
|
61
|
+
"data-toggleable-state-value": initially_open,
|
62
|
+
|
63
|
+
})
|
64
|
+
.merge(attributes)
|
65
|
+
|
66
|
+
@input_classes = merge_class_names(DEFAULT_CLASSES[:input], input_classes)
|
67
|
+
@input_attributes = DEFAULT_ATTRIBUTES[:input].merge(input_attributes)
|
68
|
+
|
69
|
+
@options_wrapper_tag = check_incoming_tag(DEFAULT_OPTIONS_WRAPPER_TAG, options_tag)
|
70
|
+
@options_wrapper_classes = merge_class_names(DEFAULT_CLASSES[:options_wrapper], options_wrapper_classes)
|
71
|
+
@options_wrapper_attributes = DEFAULT_ATTRIBUTES[:options_wrapper]
|
72
|
+
.merge({ "data-action": close_on_select ? "click->toggleable#off" : "" })
|
73
|
+
.merge(options_wrapper_attributes)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
import '@github/details-menu-element';
|
@@ -0,0 +1 @@
|
|
1
|
+
import '@github/details-menu-element';
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do %>
|
2
|
+
<% if sidecars? %>
|
3
|
+
<%= render Ariadne::BaseComponent.new(tag: @sidecar_tag, classes: @sidecar_classes, attributes: @sidecar_attributes) do %>
|
4
|
+
<% sidecars.each do |sidecar| %>
|
5
|
+
<%= sidecar %>
|
6
|
+
<% end %>
|
7
|
+
<% end %>
|
8
|
+
<% end %>
|
9
|
+
<%= render Ariadne::BaseComponent.new(tag: @main_tag, classes: @main_classes, attributes: @main_attributes) do %>
|
10
|
+
<% mains.each do |main| %>
|
11
|
+
<%= main %>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
<% if asides? %>
|
15
|
+
<%= render Ariadne::BaseComponent.new(tag: @aside_tag, classes: @aside_classes, attributes: @aside_attributes) do %>
|
16
|
+
<% asides.each do |aside| %>
|
17
|
+
<%= aside %>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Layout component to control the widths of 1-3 columns
|
5
|
+
class LayoutComponent < Ariadne::Component
|
6
|
+
DEFAULT_TAG = :div
|
7
|
+
DEFAULT_SIDECAR_TAG = :div
|
8
|
+
DEFAULT_ASIDE_TAG = :div
|
9
|
+
DEFAULT_MAIN_TAG = :div
|
10
|
+
|
11
|
+
DEFAULT_CLASSES = {
|
12
|
+
wrapper: "ariadne-group ariadne-flex ariadne-gap-2 ariadne-flex-col md:ariadne-flex-row",
|
13
|
+
sidecar: "ariadne-w-full md:ariadne-w-1/6",
|
14
|
+
main: "ariadne-grow",
|
15
|
+
aside: "ariadne-w-full md:ariadne-w-1/6",
|
16
|
+
}
|
17
|
+
|
18
|
+
DEFAULT_ATTRIBUTES = {
|
19
|
+
wrapper: {},
|
20
|
+
sidecar: {},
|
21
|
+
main: {},
|
22
|
+
aside: {},
|
23
|
+
}
|
24
|
+
|
25
|
+
renders_many :sidecars
|
26
|
+
|
27
|
+
renders_many :mains
|
28
|
+
|
29
|
+
renders_many :asides
|
30
|
+
|
31
|
+
# @example Default
|
32
|
+
#
|
33
|
+
# <%= render(Ariadne::LayoutComponent.new) { "Example" } %>
|
34
|
+
#
|
35
|
+
# @param tag [Symbol, String] The rendered tag name.
|
36
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
37
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
38
|
+
def initialize(
|
39
|
+
tag: DEFAULT_TAG,
|
40
|
+
classes: "",
|
41
|
+
attributes: {},
|
42
|
+
main_tag: DEFAULT_MAIN_TAG,
|
43
|
+
main_classes: "",
|
44
|
+
main_attributes: {},
|
45
|
+
sidecar_tag: DEFAULT_SIDECAR_TAG,
|
46
|
+
sidecar_classes: "",
|
47
|
+
sidecar_attributes: {},
|
48
|
+
aside_tag: DEFAULT_ASIDE_TAG,
|
49
|
+
aside_classes: "",
|
50
|
+
aside_attributes: {}
|
51
|
+
)
|
52
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
53
|
+
@classes = merge_class_names(DEFAULT_CLASSES[:wrapper], classes)
|
54
|
+
@attributes = DEFAULT_ATTRIBUTES[:wrapper].merge(attributes)
|
55
|
+
|
56
|
+
@sidecar_tag = check_incoming_tag(DEFAULT_SIDECAR_TAG, tag)
|
57
|
+
@sidecar_attributes = DEFAULT_ATTRIBUTES[:sidecar].merge(sidecar_attributes)
|
58
|
+
@sidecar_classes = merge_class_names(DEFAULT_CLASSES[:sidecar], sidecar_classes)
|
59
|
+
|
60
|
+
@main_tag = check_incoming_tag(DEFAULT_MAIN_TAG, tag)
|
61
|
+
@main_attributes = DEFAULT_ATTRIBUTES[:main].merge(main_attributes)
|
62
|
+
@main_classes = merge_class_names(DEFAULT_CLASSES[:main], main_classes)
|
63
|
+
|
64
|
+
@aside_tag = check_incoming_tag(DEFAULT_ASIDE_TAG, tag)
|
65
|
+
@aside_attributes = DEFAULT_ATTRIBUTES[:aside].merge(aside_attributes)
|
66
|
+
@aside_classes = merge_class_names(DEFAULT_CLASSES[:aside], aside_classes)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= render Ariadne::BaseComponent.new(tag: @tag, classes: @classes, attributes: @attributes) do %>
|
2
|
+
<%= base %>
|
3
|
+
<%= render Ariadne::BaseComponent.new(tag: @shadow_tag, classes: @shadow_classes, attributes: @shadow_attributes) do %>
|
4
|
+
<div class="<%= @content_classes %>" data-controller="events" data-action="click->events#stopPropagation">
|
5
|
+
<%= render Ariadne::CloseButtonComponent.new(classes: @close_button_classes, aria_label: @close_button_aria_label, attributes: { "data-action": "click->toggleable#toggle" }) %>
|
6
|
+
<% items.each do |item| %>
|
7
|
+
<%= item %>
|
8
|
+
<% end %>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ariadne
|
4
|
+
# Renders a full screen modal
|
5
|
+
class ModalComponent < Ariadne::Component
|
6
|
+
DEFAULT_TAG = :div
|
7
|
+
DEFAULT_SHADOW_TAG = :div
|
8
|
+
TAG_OPTIONS = [DEFAULT_TAG].freeze
|
9
|
+
|
10
|
+
DEFAULT_CLASSES = {
|
11
|
+
wrapper: "ariadne-group",
|
12
|
+
shadow: "ariadne-w-screen ariadne-h-screen ariadne-bg-black/20 ariadne-fixed ariadne-top-0 ariadne-left-0 ariadne-flex ariadne-justify-center ariadne-items-center data-[close-on-click=true]:ariadne-cursor-pointer",
|
13
|
+
content: "ariadne-bg-white ariadne-p-2 ariadne-relative ariadne-cursor-default",
|
14
|
+
close_button: "ariadne-absolute ariadne-right-2 ariadne-pt-1 ariadne-pb-1 ariadne-pl-1 ariadne-pr-1",
|
15
|
+
}
|
16
|
+
|
17
|
+
DEFAULT_ATTRIBUTES = {
|
18
|
+
wrapper: {
|
19
|
+
"data-controller": "toggleable",
|
20
|
+
"data-action": "click->toggleable#toggle",
|
21
|
+
"data-toggleable-anti-attrs-value": '["aria-hidden"]',
|
22
|
+
},
|
23
|
+
shadow: {},
|
24
|
+
content: { role: :dialog },
|
25
|
+
close_button: {},
|
26
|
+
}
|
27
|
+
|
28
|
+
SHADOW_VISIBILITY_CLASSES = "group-aria-hidden:ariadne-hidden"
|
29
|
+
|
30
|
+
renders_one :base
|
31
|
+
|
32
|
+
renders_many :items
|
33
|
+
|
34
|
+
# @example Default
|
35
|
+
#
|
36
|
+
# <%= render(Ariadne::ModalComponent.new) { "Example" } %>
|
37
|
+
#
|
38
|
+
# @param tag [Symbol, String] The rendered tag name.
|
39
|
+
# @param classes [String] <%= link_to_classes_docs %>
|
40
|
+
# @param attributes [Hash] <%= link_to_attributes_docs %>
|
41
|
+
def initialize(
|
42
|
+
tag: DEFAULT_TAG,
|
43
|
+
classes: "",
|
44
|
+
attributes: {},
|
45
|
+
initial_visible: false,
|
46
|
+
shadow_tag: DEFAULT_SHADOW_TAG,
|
47
|
+
shadow_classes: "",
|
48
|
+
shadow_attributes: {},
|
49
|
+
content_classes: "",
|
50
|
+
content_attributes: {},
|
51
|
+
close_button_classes: "",
|
52
|
+
close_button_attributes: {},
|
53
|
+
close_button_aria_label: "Close modal button",
|
54
|
+
close_on_shadow_click: true
|
55
|
+
)
|
56
|
+
@tag = check_incoming_tag(DEFAULT_TAG, tag)
|
57
|
+
@classes = merge_class_names(DEFAULT_CLASSES[:wrapper], classes)
|
58
|
+
@attributes = DEFAULT_ATTRIBUTES[:wrapper]
|
59
|
+
.merge({
|
60
|
+
"aria-hidden": true,
|
61
|
+
"data-toggleable-state-value": false,
|
62
|
+
})
|
63
|
+
.merge(attributes)
|
64
|
+
|
65
|
+
@initial_visible = initial_visible
|
66
|
+
@shadow_tag = check_incoming_tag(DEFAULT_SHADOW_TAG, shadow_tag)
|
67
|
+
@shadow_classes = merge_class_names(
|
68
|
+
DEFAULT_CLASSES[:shadow],
|
69
|
+
SHADOW_VISIBILITY_CLASSES,
|
70
|
+
shadow_classes,
|
71
|
+
)
|
72
|
+
@shadow_attributes = DEFAULT_ATTRIBUTES[:shadow]
|
73
|
+
.merge({
|
74
|
+
"data-controller": "events",
|
75
|
+
"data-close-on-click": close_on_shadow_click,
|
76
|
+
"data-action": close_on_shadow_click ? "" : "click->events#stopPropagation",
|
77
|
+
})
|
78
|
+
.merge(shadow_attributes)
|
79
|
+
|
80
|
+
@content_classes = merge_class_names(DEFAULT_CLASSES[:content], content_classes)
|
81
|
+
@content_attributes = DEFAULT_ATTRIBUTES[:content].merge(content_attributes)
|
82
|
+
|
83
|
+
@close_button_aria_label = close_button_aria_label
|
84
|
+
@close_button_classes = merge_class_names(DEFAULT_CLASSES[:close_button], close_button_classes)
|
85
|
+
@close_button_attributes = DEFAULT_ATTRIBUTES[:content].merge(content_attributes)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { TOutletChangeData } from '../outlet_manager_controller/outlet_manager_controller';
|
2
|
+
import SyncedBooleanAttributesController from '../synced_boolean_attributes_controller/synced_boolean_attributes_controller';
|
3
|
+
type TOptionKey = string | number;
|
4
|
+
type TActiveOptions = {
|
5
|
+
[k: TOptionKey]: boolean;
|
6
|
+
};
|
7
|
+
export interface OptionsOutlet extends SyncedBooleanAttributesController<TActiveOptions> {
|
8
|
+
select: (e: Event, updateTo?: TOutletChangeData<TActiveOptions>) => void;
|
9
|
+
}
|
10
|
+
export default class OptionsController extends SyncedBooleanAttributesController<TActiveOptions> implements OptionsOutlet {
|
11
|
+
#private;
|
12
|
+
static outlets: string[];
|
13
|
+
static targets: string[];
|
14
|
+
static values: {
|
15
|
+
activeOptions: ObjectConstructor;
|
16
|
+
isMulti: {
|
17
|
+
type: BooleanConstructor;
|
18
|
+
default: boolean;
|
19
|
+
};
|
20
|
+
toggleable: {
|
21
|
+
type: BooleanConstructor;
|
22
|
+
default: boolean;
|
23
|
+
};
|
24
|
+
syncedAttrs: ArrayConstructor;
|
25
|
+
antiAttrs: ArrayConstructor;
|
26
|
+
protectAttrs: BooleanConstructor;
|
27
|
+
outletEvents: ArrayConstructor;
|
28
|
+
};
|
29
|
+
readonly optionTargets: Array<Element>;
|
30
|
+
activeOptionsValue: TActiveOptions;
|
31
|
+
readonly isMultiValue: boolean;
|
32
|
+
readonly toggleableValue: boolean;
|
33
|
+
optionTargetLookup: Map<Element, TOptionKey>;
|
34
|
+
connect(): void;
|
35
|
+
select(event: Event, updateTo?: TOutletChangeData<TActiveOptions>): void;
|
36
|
+
getValueForElement(element: Element): boolean | null;
|
37
|
+
getState(): TActiveOptions;
|
38
|
+
outletUpdate: (event: Event, updateTo?: TOutletChangeData<TActiveOptions>) => void;
|
39
|
+
}
|
40
|
+
export {};
|
@@ -0,0 +1,98 @@
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
5
|
+
};
|
6
|
+
var _OptionsController_instances, _OptionsController_shouldChangeState, _OptionsController_activateIndex, _OptionsController_deactivateIndex, _OptionsController_getElementKey, _OptionsController_buildActiveTargets;
|
7
|
+
import SyncedBooleanAttributesController from '../synced_boolean_attributes_controller/synced_boolean_attributes_controller';
|
8
|
+
class OptionsController extends SyncedBooleanAttributesController {
|
9
|
+
constructor() {
|
10
|
+
super(...arguments);
|
11
|
+
_OptionsController_instances.add(this);
|
12
|
+
this.optionTargetLookup = new Map();
|
13
|
+
this.outletUpdate = this.select;
|
14
|
+
}
|
15
|
+
connect() {
|
16
|
+
__classPrivateFieldGet(this, _OptionsController_instances, "m", _OptionsController_buildActiveTargets).call(this);
|
17
|
+
this.syncElementAttributes();
|
18
|
+
}
|
19
|
+
select(event, updateTo = {}) {
|
20
|
+
var _a;
|
21
|
+
const activeOptions = updateTo.data;
|
22
|
+
for (let index in this.optionTargets) {
|
23
|
+
const target = this.optionTargets[index];
|
24
|
+
const wasSelected = target === event.currentTarget;
|
25
|
+
const isCurrentlyActive = (_a = this.getValueForElement(target)) !== null && _a !== void 0 ? _a : false;
|
26
|
+
const optionKey = __classPrivateFieldGet(this, _OptionsController_instances, "m", _OptionsController_getElementKey).call(this, target);
|
27
|
+
const shouldChangeState = activeOptions !== undefined
|
28
|
+
? !!activeOptions[optionKey] !== isCurrentlyActive
|
29
|
+
: __classPrivateFieldGet(this, _OptionsController_instances, "m", _OptionsController_shouldChangeState).call(this, isCurrentlyActive, wasSelected);
|
30
|
+
if (shouldChangeState) {
|
31
|
+
const willBeActive = !isCurrentlyActive;
|
32
|
+
this.updateAttributesForElement(target, willBeActive);
|
33
|
+
willBeActive ? __classPrivateFieldGet(this, _OptionsController_instances, "m", _OptionsController_activateIndex).call(this, optionKey) : __classPrivateFieldGet(this, _OptionsController_instances, "m", _OptionsController_deactivateIndex).call(this, optionKey);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
this.sendToOutlets(event, Object.assign(Object.assign({}, updateTo), { data: this.activeOptionsValue }));
|
37
|
+
}
|
38
|
+
getValueForElement(element) {
|
39
|
+
var _a;
|
40
|
+
if (!this.optionTargetLookup.has(element)) {
|
41
|
+
return null;
|
42
|
+
}
|
43
|
+
const optionKey = __classPrivateFieldGet(this, _OptionsController_instances, "m", _OptionsController_getElementKey).call(this, element);
|
44
|
+
return (_a = this.activeOptionsValue[optionKey]) !== null && _a !== void 0 ? _a : false;
|
45
|
+
}
|
46
|
+
getState() {
|
47
|
+
return this.activeOptionsValue;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
_OptionsController_instances = new WeakSet(), _OptionsController_shouldChangeState = function _OptionsController_shouldChangeState(isCurrentlyActive, wasSelected) {
|
51
|
+
if (!wasSelected && !isCurrentlyActive) {
|
52
|
+
// Not currently on and wasn't selected, no reason to change
|
53
|
+
return false;
|
54
|
+
}
|
55
|
+
if (!wasSelected && isCurrentlyActive && this.isMultiValue) {
|
56
|
+
// It wasn't selected and it is on. However, multi value is on so just leave it
|
57
|
+
return false;
|
58
|
+
}
|
59
|
+
if (wasSelected && isCurrentlyActive && !this.toggleableValue) {
|
60
|
+
// It was selected and it's on. But toggle isn't on so just leave it
|
61
|
+
return false;
|
62
|
+
}
|
63
|
+
// Wasn't selected and it's active but only one can be active
|
64
|
+
// Was selected and it's active but toggling behavior means it should deactivate
|
65
|
+
// Was selected and it's not on, so it just needs to be turned on
|
66
|
+
return true;
|
67
|
+
}, _OptionsController_activateIndex = function _OptionsController_activateIndex(key) {
|
68
|
+
this.activeOptionsValue = Object.assign(Object.assign({}, this.activeOptionsValue), { [key]: true });
|
69
|
+
}, _OptionsController_deactivateIndex = function _OptionsController_deactivateIndex(key) {
|
70
|
+
const copy = Object.assign({}, this.activeOptionsValue);
|
71
|
+
delete copy[key];
|
72
|
+
this.activeOptionsValue = copy;
|
73
|
+
}, _OptionsController_getElementKey = function _OptionsController_getElementKey(element) {
|
74
|
+
const elementValue = element.getAttribute('data-option-value');
|
75
|
+
if (elementValue) {
|
76
|
+
return elementValue;
|
77
|
+
}
|
78
|
+
return element.innerText.trim();
|
79
|
+
}, _OptionsController_buildActiveTargets = function _OptionsController_buildActiveTargets() {
|
80
|
+
this.optionTargetLookup = new Map();
|
81
|
+
for (let index in this.optionTargets) {
|
82
|
+
const target = this.optionTargets[index];
|
83
|
+
const optionKey = __classPrivateFieldGet(this, _OptionsController_instances, "m", _OptionsController_getElementKey).call(this, target);
|
84
|
+
this.optionTargetLookup.set(target, optionKey);
|
85
|
+
const onSyncAttr = !!this.syncedAttrsValue.find(attr => target.getAttribute(attr) === 'true');
|
86
|
+
if (onSyncAttr) {
|
87
|
+
__classPrivateFieldGet(this, _OptionsController_instances, "m", _OptionsController_activateIndex).call(this, optionKey);
|
88
|
+
}
|
89
|
+
else {
|
90
|
+
const hasOffAttr = !!this.antiAttrsValue.find(attr => target.getAttribute(attr) === 'false');
|
91
|
+
hasOffAttr && __classPrivateFieldGet(this, _OptionsController_instances, "m", _OptionsController_activateIndex).call(this, optionKey);
|
92
|
+
}
|
93
|
+
}
|
94
|
+
};
|
95
|
+
OptionsController.outlets = SyncedBooleanAttributesController.outlets;
|
96
|
+
OptionsController.targets = ['option'];
|
97
|
+
OptionsController.values = Object.assign(Object.assign({}, SyncedBooleanAttributesController.values), { activeOptions: Object, isMulti: { type: Boolean, default: false }, toggleable: { type: Boolean, default: false } });
|
98
|
+
export default OptionsController;
|
@@ -0,0 +1,132 @@
|
|
1
|
+
import {TOutletChangeData} from '../outlet_manager_controller/outlet_manager_controller'
|
2
|
+
import SyncedBooleanAttributesController from '../synced_boolean_attributes_controller/synced_boolean_attributes_controller'
|
3
|
+
|
4
|
+
type TOptionKey = string | number
|
5
|
+
type TActiveOptions = {[k: TOptionKey]: boolean}
|
6
|
+
export interface OptionsOutlet extends SyncedBooleanAttributesController<TActiveOptions> {
|
7
|
+
select: (e: Event, updateTo?: TOutletChangeData<TActiveOptions>) => void
|
8
|
+
}
|
9
|
+
|
10
|
+
export default class OptionsController
|
11
|
+
extends SyncedBooleanAttributesController<TActiveOptions>
|
12
|
+
implements OptionsOutlet
|
13
|
+
{
|
14
|
+
static outlets = SyncedBooleanAttributesController.outlets
|
15
|
+
static targets = ['option']
|
16
|
+
|
17
|
+
static values = {
|
18
|
+
...SyncedBooleanAttributesController.values,
|
19
|
+
activeOptions: Object, // The currently active elements in TActiveOptions format
|
20
|
+
isMulti: {type: Boolean, default: false}, // Allows more than one selection
|
21
|
+
toggleable: {type: Boolean, default: false}, // If true, selecting the same value twice will turn it off. If false, nothing happens
|
22
|
+
}
|
23
|
+
|
24
|
+
// Targets
|
25
|
+
declare readonly optionTargets: Array<Element>
|
26
|
+
|
27
|
+
// Values
|
28
|
+
declare activeOptionsValue: TActiveOptions
|
29
|
+
declare readonly isMultiValue: boolean
|
30
|
+
declare readonly toggleableValue: boolean
|
31
|
+
|
32
|
+
optionTargetLookup: Map<Element, TOptionKey> = new Map()
|
33
|
+
|
34
|
+
connect(): void {
|
35
|
+
this.#buildActiveTargets()
|
36
|
+
this.syncElementAttributes()
|
37
|
+
}
|
38
|
+
|
39
|
+
select(event: Event, updateTo: TOutletChangeData<TActiveOptions> = {}) {
|
40
|
+
const activeOptions = updateTo.data
|
41
|
+
for (let index in this.optionTargets) {
|
42
|
+
const target = this.optionTargets[index]
|
43
|
+
const wasSelected = target === event.currentTarget
|
44
|
+
const isCurrentlyActive = this.getValueForElement(target) ?? false
|
45
|
+
const optionKey = this.#getElementKey(target)
|
46
|
+
const shouldChangeState =
|
47
|
+
activeOptions !== undefined
|
48
|
+
? !!activeOptions[optionKey] !== isCurrentlyActive
|
49
|
+
: this.#shouldChangeState(isCurrentlyActive, wasSelected)
|
50
|
+
|
51
|
+
if (shouldChangeState) {
|
52
|
+
const willBeActive = !isCurrentlyActive
|
53
|
+
this.updateAttributesForElement(target, willBeActive)
|
54
|
+
willBeActive ? this.#activateIndex(optionKey) : this.#deactivateIndex(optionKey)
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
this.sendToOutlets(event, {...updateTo, data: this.activeOptionsValue})
|
59
|
+
}
|
60
|
+
|
61
|
+
#shouldChangeState(isCurrentlyActive: boolean, wasSelected: boolean) {
|
62
|
+
if (!wasSelected && !isCurrentlyActive) {
|
63
|
+
// Not currently on and wasn't selected, no reason to change
|
64
|
+
return false
|
65
|
+
}
|
66
|
+
|
67
|
+
if (!wasSelected && isCurrentlyActive && this.isMultiValue) {
|
68
|
+
// It wasn't selected and it is on. However, multi value is on so just leave it
|
69
|
+
return false
|
70
|
+
}
|
71
|
+
|
72
|
+
if (wasSelected && isCurrentlyActive && !this.toggleableValue) {
|
73
|
+
// It was selected and it's on. But toggle isn't on so just leave it
|
74
|
+
return false
|
75
|
+
}
|
76
|
+
|
77
|
+
// Wasn't selected and it's active but only one can be active
|
78
|
+
// Was selected and it's active but toggling behavior means it should deactivate
|
79
|
+
// Was selected and it's not on, so it just needs to be turned on
|
80
|
+
return true
|
81
|
+
}
|
82
|
+
|
83
|
+
#activateIndex(key: TOptionKey) {
|
84
|
+
this.activeOptionsValue = {...this.activeOptionsValue, [key]: true}
|
85
|
+
}
|
86
|
+
|
87
|
+
#deactivateIndex(key: TOptionKey) {
|
88
|
+
const copy = {...this.activeOptionsValue}
|
89
|
+
delete copy[key]
|
90
|
+
this.activeOptionsValue = copy
|
91
|
+
}
|
92
|
+
|
93
|
+
#getElementKey(element: Element) {
|
94
|
+
const elementValue = element.getAttribute('data-option-value')
|
95
|
+
if (elementValue) {
|
96
|
+
return elementValue
|
97
|
+
}
|
98
|
+
|
99
|
+
return (element as HTMLElement).innerText.trim()
|
100
|
+
}
|
101
|
+
|
102
|
+
#buildActiveTargets() {
|
103
|
+
this.optionTargetLookup = new Map()
|
104
|
+
for (let index in this.optionTargets) {
|
105
|
+
const target = this.optionTargets[index]
|
106
|
+
const optionKey = this.#getElementKey(target)
|
107
|
+
this.optionTargetLookup.set(target, optionKey)
|
108
|
+
const onSyncAttr = !!this.syncedAttrsValue.find(attr => target.getAttribute(attr) === 'true')
|
109
|
+
if (onSyncAttr) {
|
110
|
+
this.#activateIndex(optionKey)
|
111
|
+
} else {
|
112
|
+
const hasOffAttr = !!this.antiAttrsValue.find(attr => target.getAttribute(attr) === 'false')
|
113
|
+
hasOffAttr && this.#activateIndex(optionKey)
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
getValueForElement(element: Element) {
|
119
|
+
if (!this.optionTargetLookup.has(element)) {
|
120
|
+
return null
|
121
|
+
}
|
122
|
+
|
123
|
+
const optionKey = this.#getElementKey(element)
|
124
|
+
return this.activeOptionsValue[optionKey] ?? false
|
125
|
+
}
|
126
|
+
|
127
|
+
getState() {
|
128
|
+
return this.activeOptionsValue
|
129
|
+
}
|
130
|
+
|
131
|
+
outletUpdate = this.select
|
132
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
2
|
+
type TOutletEventLookup = boolean | {
|
3
|
+
[k: string]: TOutletEventLookup;
|
4
|
+
};
|
5
|
+
export type TOutletChangeData<T> = {
|
6
|
+
eventKey?: string;
|
7
|
+
data?: T;
|
8
|
+
} | undefined;
|
9
|
+
export default class OutletManagerController<T> extends Controller {
|
10
|
+
#private;
|
11
|
+
static values: {
|
12
|
+
outletEvents: ArrayConstructor;
|
13
|
+
};
|
14
|
+
readonly outletEventsValue: Array<string>;
|
15
|
+
readonly hasOutletEventsValue: boolean;
|
16
|
+
static outlets: string[];
|
17
|
+
readonly toggleableOutlets: Array<OutletManagerController<T>>;
|
18
|
+
readonly hasToggleableOutlet: boolean;
|
19
|
+
readonly optionsOutlets: Array<OutletManagerController<T>>;
|
20
|
+
readonly hasOptionsOutlet: boolean;
|
21
|
+
readonly stringMatchOutlets: Array<OutletManagerController<T>>;
|
22
|
+
readonly hasStringMatchOutlet: boolean;
|
23
|
+
outletEventsLookup: TOutletEventLookup | null;
|
24
|
+
static domEvents: {
|
25
|
+
[k: string]: boolean;
|
26
|
+
};
|
27
|
+
eventRecords: Map<Event, boolean>;
|
28
|
+
getOutlets(): Array<OutletManagerController<T>> | null | void;
|
29
|
+
outletUpdate(event: Event, data: TOutletChangeData<T>): void;
|
30
|
+
getState(): T;
|
31
|
+
connect(): void;
|
32
|
+
syncOutlets(): void;
|
33
|
+
sendToOutlets(event: Event, updateTo?: TOutletChangeData<T>): void;
|
34
|
+
isListeningForOutletEvent(eventTypes: string): boolean;
|
35
|
+
isDOMEventName(eventName: string): boolean;
|
36
|
+
getEventKey(event: Event): string;
|
37
|
+
hasHeardEvent(event: Event): boolean;
|
38
|
+
get event_key_prefix(): string;
|
39
|
+
get event_key_postfix(): string;
|
40
|
+
get outletEvents(): TOutletEventLookup;
|
41
|
+
}
|
42
|
+
export {};
|