ariadne_view_components 0.0.47-x86_64-linux → 0.0.49-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
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 {};