ariadne_view_components 0.0.47-x86_64-darwin → 0.0.49-x86_64-darwin

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -25
  3. data/app/assets/javascripts/ariadne_view_components.js +2 -2
  4. data/app/assets/javascripts/ariadne_view_components.js.map +1 -1
  5. data/app/assets/javascripts/components/ariadne/accumulator_controller/accumulator_controller.d.ts +22 -0
  6. data/app/assets/javascripts/components/ariadne/dropdown/menu_component.d.ts +1 -0
  7. data/app/assets/javascripts/components/ariadne/events_controller/events_controller.d.ts +4 -0
  8. data/app/assets/javascripts/components/ariadne/options_controller/options_controller.d.ts +40 -0
  9. data/app/assets/javascripts/components/ariadne/outlet_manager_controller/outlet_manager_controller.d.ts +42 -0
  10. data/app/assets/javascripts/components/ariadne/string_match_controller/string_match_controller.d.ts +27 -0
  11. data/app/assets/javascripts/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.d.ts +44 -0
  12. data/app/assets/javascripts/components/ariadne/toggleable_controller/toggleable_controller.d.ts +34 -0
  13. data/app/assets/stylesheets/ariadne_view_components.css +3 -3
  14. data/app/components/ariadne/accumulator_controller/accumulator_controller.d.ts +22 -0
  15. data/app/components/ariadne/accumulator_controller/accumulator_controller.js +39 -0
  16. data/app/components/ariadne/accumulator_controller/accumulator_controller.ts +48 -0
  17. data/app/components/ariadne/action_card_component.html.erb +11 -0
  18. data/app/components/ariadne/action_card_component.rb +45 -0
  19. data/app/components/ariadne/ariadne.js +10 -0
  20. data/app/components/ariadne/ariadne.ts +10 -0
  21. data/app/components/ariadne/bottom_tab_component.html.erb +4 -0
  22. data/app/components/ariadne/bottom_tab_component.rb +44 -0
  23. data/app/components/ariadne/bottom_tab_nav_component.html.erb +5 -0
  24. data/app/components/ariadne/bottom_tab_nav_component.rb +33 -0
  25. data/app/components/ariadne/breadcrumbs_component.html.erb +13 -0
  26. data/app/components/ariadne/breadcrumbs_component.rb +31 -0
  27. data/app/components/ariadne/checkbox_component.html.erb +5 -0
  28. data/app/components/ariadne/checkbox_component.rb +43 -0
  29. data/app/components/ariadne/close_button_component.html.erb +4 -0
  30. data/app/components/ariadne/close_button_component.rb +33 -0
  31. data/app/components/ariadne/combobox_component.html.erb +14 -0
  32. data/app/components/ariadne/combobox_component.rb +76 -0
  33. data/app/components/ariadne/dropdown/menu_component.d.ts +1 -0
  34. data/app/components/ariadne/dropdown/menu_component.js +1 -0
  35. data/app/components/ariadne/events_controller/events_controller.d.ts +4 -0
  36. data/app/components/ariadne/events_controller/events_controller.js +6 -0
  37. data/app/components/ariadne/events_controller/events_controller.ts +7 -0
  38. data/app/components/ariadne/layout_component.html.erb +21 -0
  39. data/app/components/ariadne/layout_component.rb +69 -0
  40. data/app/components/ariadne/modal_component.html.erb +11 -0
  41. data/app/components/ariadne/modal_component.rb +88 -0
  42. data/app/components/ariadne/options_controller/options_controller.d.ts +40 -0
  43. data/app/components/ariadne/options_controller/options_controller.js +98 -0
  44. data/app/components/ariadne/options_controller/options_controller.ts +132 -0
  45. data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.d.ts +42 -0
  46. data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.js +237 -0
  47. data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.ts +278 -0
  48. data/app/components/ariadne/popover_component.html.erb +10 -0
  49. data/app/components/ariadne/popover_component.rb +81 -0
  50. data/app/components/ariadne/progress_bar_component.html.erb +5 -0
  51. data/app/components/ariadne/progress_bar_component.rb +63 -0
  52. data/app/components/ariadne/relative_time_component.html.erb +3 -0
  53. data/app/components/ariadne/relative_time_component.rb +61 -0
  54. data/app/components/ariadne/show_more_button_component.html.erb +11 -0
  55. data/app/components/ariadne/show_more_button_component.rb +47 -0
  56. data/app/components/ariadne/spinner_component.html.erb +16 -0
  57. data/app/components/ariadne/spinner_component.rb +45 -0
  58. data/app/components/ariadne/string_match_controller/string_match_controller.d.ts +27 -0
  59. data/app/components/ariadne/string_match_controller/string_match_controller.js +51 -0
  60. data/app/components/ariadne/string_match_controller/string_match_controller.ts +64 -0
  61. data/app/components/ariadne/subheader_component.html.erb +11 -0
  62. data/app/components/ariadne/subheader_component.rb +65 -0
  63. data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.d.ts +44 -0
  64. data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.js +153 -0
  65. data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.ts +192 -0
  66. data/app/components/ariadne/toggle_component/toggle_component.html.erb +15 -0
  67. data/app/components/ariadne/toggle_component.rb +95 -0
  68. data/app/components/ariadne/toggleable_controller/toggleable_controller.d.ts +34 -0
  69. data/app/components/ariadne/toggleable_controller/toggleable_controller.js +54 -0
  70. data/app/components/ariadne/toggleable_controller/toggleable_controller.ts +77 -0
  71. data/lib/ariadne/view_components/version.rb +1 -1
  72. data/static/arguments.yml +50 -0
  73. data/static/audited_at.json +17 -0
  74. data/static/classes.yml +209 -173
  75. data/static/constants.json +356 -0
  76. data/static/statuses.json +17 -0
  77. data/tailwind.config.js +7 -7
  78. metadata +75 -12
  79. /data/app/assets/javascripts/{ariadne-form.d.ts → components/ariadne/ariadne-form.d.ts} +0 -0
  80. /data/app/assets/javascripts/{ariadne.d.ts → components/ariadne/ariadne.d.ts} +0 -0
  81. /data/app/assets/javascripts/{clipboard_copy_component → components/ariadne/clipboard_copy_component}/clipboard-copy-component.d.ts +0 -0
  82. /data/app/assets/javascripts/{rich_text_area_component → components/ariadne/rich_text_area_component}/rich-text-area-component.d.ts +0 -0
  83. /data/app/assets/javascripts/{slideover_component → components/ariadne/slideover_component}/slideover-component.d.ts +0 -0
  84. /data/app/assets/javascripts/{tab_container_component → components/ariadne/tab_container_component}/tab-container-component.d.ts +0 -0
  85. /data/app/assets/javascripts/{tab_nav_component → components/ariadne/tab_nav_component}/tab-nav-component.d.ts +0 -0
  86. /data/app/assets/javascripts/{time_ago_component → components/ariadne/time_ago_component}/time-ago-component.d.ts +0 -0
  87. /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,4 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class EventsController extends Controller {
3
+ stopPropagation(e: Event): void;
4
+ }
@@ -0,0 +1,6 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+ export default class EventsController extends Controller {
3
+ stopPropagation(e) {
4
+ e.stopPropagation();
5
+ }
6
+ }
@@ -0,0 +1,7 @@
1
+ import {Controller} from '@hotwired/stimulus'
2
+
3
+ export default class EventsController extends Controller {
4
+ stopPropagation(e: Event) {
5
+ e.stopPropagation()
6
+ }
7
+ }
@@ -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 {};