ariadne_view_components 0.0.48-x64-mingw-ucrt → 0.0.49-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  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/events_controller/events_controller.d.ts +4 -0
  6. data/app/assets/javascripts/components/ariadne/options_controller/options_controller.d.ts +30 -11
  7. data/app/assets/javascripts/components/ariadne/outlet_manager_controller/outlet_manager_controller.d.ts +42 -0
  8. data/app/assets/javascripts/components/ariadne/string_match_controller/string_match_controller.d.ts +27 -0
  9. data/app/assets/javascripts/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.d.ts +44 -0
  10. data/app/assets/javascripts/components/ariadne/toggleable_controller/toggleable_controller.d.ts +17 -17
  11. data/app/components/ariadne/ariadne.js +4 -0
  12. data/app/components/ariadne/ariadne.ts +4 -0
  13. data/app/components/ariadne/close_button_component.html.erb +1 -1
  14. data/app/components/ariadne/close_button_component.rb +2 -1
  15. data/app/components/ariadne/combobox_component.html.erb +14 -0
  16. data/app/components/ariadne/combobox_component.rb +76 -0
  17. data/app/components/ariadne/events_controller/events_controller.d.ts +4 -0
  18. data/app/components/ariadne/events_controller/events_controller.js +6 -0
  19. data/app/components/ariadne/events_controller/events_controller.ts +7 -0
  20. data/app/components/ariadne/layout_component.html.erb +21 -0
  21. data/app/components/ariadne/layout_component.rb +69 -0
  22. data/app/components/ariadne/modal_component.html.erb +11 -0
  23. data/app/components/ariadne/modal_component.rb +88 -0
  24. data/app/components/ariadne/options_controller/options_controller.d.ts +30 -11
  25. data/app/components/ariadne/options_controller/options_controller.js +75 -27
  26. data/app/components/ariadne/options_controller/options_controller.ts +104 -29
  27. data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.d.ts +42 -0
  28. data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.js +237 -0
  29. data/app/components/ariadne/outlet_manager_controller/outlet_manager_controller.ts +278 -0
  30. data/app/components/ariadne/popover_component.html.erb +0 -1
  31. data/app/components/ariadne/show_more_button_component.html.erb +1 -1
  32. data/app/components/ariadne/string_match_controller/string_match_controller.d.ts +27 -0
  33. data/app/components/ariadne/string_match_controller/string_match_controller.js +51 -0
  34. data/app/components/ariadne/string_match_controller/string_match_controller.ts +64 -0
  35. data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.d.ts +44 -0
  36. data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.js +153 -0
  37. data/app/components/ariadne/synced_boolean_attributes_controller/synced_boolean_attributes_controller.ts +192 -0
  38. data/app/components/ariadne/toggleable_controller/toggleable_controller.d.ts +17 -17
  39. data/app/components/ariadne/toggleable_controller/toggleable_controller.js +35 -55
  40. data/app/components/ariadne/toggleable_controller/toggleable_controller.ts +41 -51
  41. data/lib/ariadne/view_components/version.rb +1 -1
  42. data/static/audited_at.json +3 -0
  43. data/static/constants.json +74 -0
  44. data/static/statuses.json +3 -0
  45. metadata +24 -2
@@ -0,0 +1,278 @@
1
+ import {Controller} from '@hotwired/stimulus'
2
+
3
+ type TOutletEventLookup = boolean | {[k: string]: TOutletEventLookup}
4
+
5
+ export type TOutletChangeData<T> =
6
+ | {
7
+ eventKey?: string
8
+ data?: T
9
+ }
10
+ | undefined
11
+
12
+ export default class OutletManagerController<T> extends Controller {
13
+ static values = {
14
+ outletEvents: Array,
15
+ }
16
+
17
+ declare readonly outletEventsValue: Array<string>
18
+ declare readonly hasOutletEventsValue: boolean
19
+
20
+ static outlets = ['toggleable', 'options', 'string-match']
21
+ declare readonly toggleableOutlets: Array<OutletManagerController<T>>
22
+ declare readonly hasToggleableOutlet: boolean
23
+ declare readonly optionsOutlets: Array<OutletManagerController<T>>
24
+ declare readonly hasOptionsOutlet: boolean
25
+ declare readonly stringMatchOutlets: Array<OutletManagerController<T>>
26
+ declare readonly hasStringMatchOutlet: boolean
27
+
28
+ outletEventsLookup: TOutletEventLookup | null = null
29
+
30
+ static domEvents: {[k: string]: boolean} = {
31
+ abort: true,
32
+ afterprint: true,
33
+ animationend: true,
34
+ animationiteration: true,
35
+ animationstart: true,
36
+ beforeprint: true,
37
+ beforeunload: true,
38
+ blur: true,
39
+ canplay: true,
40
+ canplaythrough: true,
41
+ change: true,
42
+ click: true,
43
+ contextmenu: true,
44
+ copy: true,
45
+ cut: true,
46
+ dblclick: true,
47
+ drag: true,
48
+ dragend: true,
49
+ dragenter: true,
50
+ dragleave: true,
51
+ dragover: true,
52
+ dragstart: true,
53
+ drop: true,
54
+ durationchange: true,
55
+ ended: true,
56
+ error: true,
57
+ focus: true,
58
+ focusin: true,
59
+ focusout: true,
60
+ fullscreenchange: true,
61
+ fullscreenerror: true,
62
+ hashchange: true,
63
+ input: true,
64
+ invalid: true,
65
+ keydown: true,
66
+ keypress: true,
67
+ keyup: true,
68
+ load: true,
69
+ loadeddata: true,
70
+ loadedmetadata: true,
71
+ loadstart: true,
72
+ message: true,
73
+ mousedown: true,
74
+ mouseenter: true,
75
+ mouseleave: true,
76
+ mousemove: true,
77
+ mouseover: true,
78
+ mouseout: true,
79
+ mouseup: true,
80
+ mousewheel: true,
81
+ offline: true,
82
+ online: true,
83
+ open: true,
84
+ pagehide: true,
85
+ pageshow: true,
86
+ paste: true,
87
+ pause: true,
88
+ play: true,
89
+ playing: true,
90
+ popstate: true,
91
+ progress: true,
92
+ ratechange: true,
93
+ resize: true,
94
+ reset: true,
95
+ scroll: true,
96
+ search: true,
97
+ seeked: true,
98
+ seeking: true,
99
+ select: true,
100
+ show: true,
101
+ stalled: true,
102
+ storage: true,
103
+ submit: true,
104
+ suspend: true,
105
+ timeupdate: true,
106
+ toggle: true,
107
+ touchcancel: true,
108
+ touchend: true,
109
+ touchmove: true,
110
+ touchstart: true,
111
+ transitionend: true,
112
+ unload: true,
113
+ volumechange: true,
114
+ waiting: true,
115
+ wheel: true,
116
+ }
117
+
118
+ eventRecords: Map<Event, boolean> = new Map()
119
+
120
+ getOutlets(): Array<OutletManagerController<T>> | null | void {
121
+ return null
122
+ }
123
+
124
+ outletUpdate(event: Event, data: TOutletChangeData<T>): void {}
125
+
126
+ getState(): T {
127
+ return null as T
128
+ }
129
+
130
+ connect() {
131
+ this.syncOutlets()
132
+ }
133
+
134
+ syncOutlets() {
135
+ const event = new Event('init')
136
+ this.sendToOutlets(event, {
137
+ data: this.getState(),
138
+ eventKey: this.getEventKey(event),
139
+ })
140
+ }
141
+
142
+ sendToOutlets(event: Event, updateTo: TOutletChangeData<T> = {}): void {
143
+ const eventKey = updateTo.eventKey ?? this.getEventKey(event)
144
+ const outlets = this.#outlets
145
+ if (outlets?.length) {
146
+ for (const index in outlets) {
147
+ const outlet = outlets[index]
148
+ if (outlet.isListeningForOutletEvent(eventKey) && !this.hasHeardEvent(event)) {
149
+ const isSameControllerType = this.identifier === outlet.identifier
150
+ outlet.outletUpdate(event, {eventKey, data: isSameControllerType ? updateTo.data : undefined})
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ isListeningForOutletEvent(eventTypes: string) {
157
+ const eventTypeNames = eventTypes.split('-')
158
+
159
+ if (!eventTypeNames.length) {
160
+ return false
161
+ }
162
+
163
+ let lookup = this.outletEvents
164
+ for (let i = 0; i < eventTypeNames.length; i++) {
165
+ const name = eventTypeNames[i]
166
+ if (typeof lookup === 'boolean') {
167
+ return lookup
168
+ }
169
+
170
+ const hasWildCard = lookup['*'] !== undefined
171
+ let nextKey = name
172
+ if (hasWildCard) {
173
+ nextKey = '*'
174
+ } else {
175
+ const isListeningForDOMEvent = lookup.domEvent !== undefined
176
+ if (isListeningForDOMEvent && !this.isDOMEventName(name)) {
177
+ return false
178
+ } else if (isListeningForDOMEvent) {
179
+ nextKey = 'domEvent'
180
+ }
181
+ }
182
+
183
+ if (!lookup[nextKey]) {
184
+ return false
185
+ }
186
+
187
+ lookup = lookup[nextKey]
188
+ }
189
+
190
+ return true
191
+ }
192
+
193
+ isDOMEventName(eventName: string) {
194
+ return OutletManagerController.domEvents[eventName]
195
+ }
196
+
197
+ getEventKey(event: Event) {
198
+ const pre = this.event_key_prefix
199
+ const post = this.event_key_postfix
200
+
201
+ const maybePreHyphen = pre ? '-' : ''
202
+ const maybePrefix = pre ?? ''
203
+ const maybePostHyphen = post ? '-' : ''
204
+ const maybePostfix = post ?? ''
205
+
206
+ return `${this.identifier}-${maybePrefix}${maybePreHyphen}${event.type}${maybePostHyphen}${maybePostfix}`
207
+ }
208
+
209
+ hasHeardEvent(event: Event) {
210
+ if (this.eventRecords.has(event)) {
211
+ return true
212
+ }
213
+
214
+ this.eventRecords.set(event, true)
215
+ setTimeout(() => this.eventRecords.delete(event))
216
+
217
+ return false
218
+ }
219
+
220
+ get event_key_prefix() {
221
+ return ''
222
+ }
223
+
224
+ get event_key_postfix() {
225
+ return ''
226
+ }
227
+
228
+ get outletEvents() {
229
+ if (!this.outletEventsLookup && this.hasOutletEventsValue) {
230
+ this.outletEventsLookup = this.outletEventsValue.reduce((acc, eventType) => {
231
+ let step = acc
232
+ eventType.split('-').forEach((eventTypeName, i, splitArr) => {
233
+ if (typeof step === 'boolean') {
234
+ return
235
+ }
236
+
237
+ if (i === splitArr.length - 1) {
238
+ step[eventTypeName] = true
239
+ } else if (step[eventTypeName] === undefined) {
240
+ step[eventTypeName] = {}
241
+ }
242
+
243
+ step = step[eventTypeName]
244
+ })
245
+
246
+ return acc
247
+ }, {} as TOutletEventLookup)
248
+ } else if (!this.outletEventsLookup) {
249
+ this.outletEventsLookup = {'*': true}
250
+ }
251
+
252
+ return this.outletEventsLookup
253
+ }
254
+
255
+ get #outlets(): Array<OutletManagerController<T>> | null | void {
256
+ const outlets = this.getOutlets()
257
+
258
+ if (outlets) {
259
+ return outlets
260
+ }
261
+
262
+ const defaultOutlets: Array<OutletManagerController<T>> = []
263
+
264
+ if (this.hasToggleableOutlet) {
265
+ defaultOutlets.push(...this.toggleableOutlets)
266
+ }
267
+
268
+ if (this.hasOptionsOutlet) {
269
+ defaultOutlets.push(...this.optionsOutlets)
270
+ }
271
+
272
+ if (this.hasStringMatchOutlet) {
273
+ defaultOutlets.push(...this.stringMatchOutlets)
274
+ }
275
+
276
+ return defaultOutlets
277
+ }
278
+ }
@@ -2,7 +2,6 @@
2
2
  <div class="<%= @base_classes %>">
3
3
  <%= base %>
4
4
  </div>
5
- <div class="hidden ariadne-my-20 ariadne-w-20"></div>
6
5
  <div class="<%= @items_wrapper_classes %>" data-action="click->toggleable#toggle">
7
6
  <% items.each do |item| %>
8
7
  <%= item %>
@@ -3,7 +3,7 @@
3
3
  <%= @button_text %>
4
4
  <% end %>
5
5
  <div
6
- class="ariadne-max-h-full aria-hidden:ariadne-max-h-0 ariadne-overflow-hidden ariadne-my-2" data-controller="toggleable" aria-hidden="true" data-toggleable-anti-attrs-value='["aria-hidden"]' data-toggleable-outlet="<%= @outlet %>">
6
+ class="ariadne-max-h-full aria-hidden:ariadne-max-h-0 ariadne-overflow-hidden ariadne-my-2 ariadne-border-solid ariadne-border-2 aria-selected:ariadne-hidden aria-selected:ariadne-border-green-300 aria-selected:ariadne-border-solid aria-selected:ariadne-border-2 ariadne-border-transparent ariadne-min-w-[21rem] ariadne-whitespace-nowrap ariadne-text-ellipsis" data-controller="toggleable" aria-hidden="true" data-toggleable-anti-attrs-value='["aria-hidden"]' data-toggleable-outlet="<%= @outlet %>">
7
7
  <% hiddens.each do |hidden| %>
8
8
  <%= hidden %>
9
9
  <% end %>
@@ -0,0 +1,27 @@
1
+ import { TOutletChangeData } from '../outlet_manager_controller/outlet_manager_controller';
2
+ import SyncedBooleanAttributesController from '../synced_boolean_attributes_controller/synced_boolean_attributes_controller';
3
+ export interface StringMatchOutlet extends SyncedBooleanAttributesController<string> {
4
+ change: (event: Event, updateTo?: TOutletChangeData<string>) => void;
5
+ }
6
+ export default class StringMatchController extends SyncedBooleanAttributesController<string> implements StringMatchOutlet {
7
+ #private;
8
+ static outlets: string[];
9
+ static targets: string[];
10
+ static values: {
11
+ keyword: StringConstructor;
12
+ syncedAttrs: ArrayConstructor;
13
+ antiAttrs: ArrayConstructor;
14
+ protectAttrs: BooleanConstructor;
15
+ outletEvents: ArrayConstructor;
16
+ };
17
+ readonly matchTargets: Array<HTMLElement>;
18
+ readonly hasMatchTarget: boolean;
19
+ readonly emptyTarget: Element;
20
+ readonly hasEmptyTarget: boolean;
21
+ keywordValue: string;
22
+ change(event: Event, updateTo?: TOutletChangeData<string>): void;
23
+ getElementsToSync(): Element[] | null | undefined;
24
+ getValueForElement(element: Element): boolean;
25
+ getState(): string;
26
+ outletUpdate: (event: Event, updateTo?: TOutletChangeData<string>) => void;
27
+ }
@@ -0,0 +1,51 @@
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 _StringMatchController_instances, _StringMatchController_compareKeywordToTargets;
7
+ import SyncedBooleanAttributesController from '../synced_boolean_attributes_controller/synced_boolean_attributes_controller';
8
+ class StringMatchController extends SyncedBooleanAttributesController {
9
+ constructor() {
10
+ super(...arguments);
11
+ _StringMatchController_instances.add(this);
12
+ this.outletUpdate = this.change;
13
+ }
14
+ change(event, updateTo = {}) {
15
+ var _a;
16
+ const value = (_a = updateTo.data) !== null && _a !== void 0 ? _a : event.currentTarget.value;
17
+ this.keywordValue = value;
18
+ __classPrivateFieldGet(this, _StringMatchController_instances, "m", _StringMatchController_compareKeywordToTargets).call(this);
19
+ this.sendToOutlets(event, Object.assign(Object.assign({}, updateTo), { data: this.keywordValue }));
20
+ }
21
+ getElementsToSync() {
22
+ return this.matchTargets;
23
+ }
24
+ getValueForElement(element) {
25
+ var _a, _b;
26
+ return (_b = (_a = element.innerText) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(this.keywordValue.toLowerCase())) !== null && _b !== void 0 ? _b : false;
27
+ }
28
+ getState() {
29
+ return this.keywordValue;
30
+ }
31
+ }
32
+ _StringMatchController_instances = new WeakSet(), _StringMatchController_compareKeywordToTargets = function _StringMatchController_compareKeywordToTargets() {
33
+ if (this.hasMatchTarget) {
34
+ let foundMatch = false;
35
+ for (let index in this.matchTargets) {
36
+ const target = this.matchTargets[index];
37
+ const value = this.getValueForElement(target);
38
+ this.updateAttributesForElement(target, value);
39
+ if (value) {
40
+ foundMatch = true;
41
+ }
42
+ }
43
+ if (this.hasEmptyTarget) {
44
+ this.emptyTarget.setAttribute('aria-hidden', String(foundMatch));
45
+ }
46
+ }
47
+ };
48
+ StringMatchController.outlets = SyncedBooleanAttributesController.outlets;
49
+ StringMatchController.targets = ['match', 'empty'];
50
+ StringMatchController.values = Object.assign(Object.assign({}, SyncedBooleanAttributesController.values), { keyword: String });
51
+ export default StringMatchController;
@@ -0,0 +1,64 @@
1
+ import {TOutletChangeData} from '../outlet_manager_controller/outlet_manager_controller'
2
+ import SyncedBooleanAttributesController from '../synced_boolean_attributes_controller/synced_boolean_attributes_controller'
3
+
4
+ export interface StringMatchOutlet extends SyncedBooleanAttributesController<string> {
5
+ change: (event: Event, updateTo?: TOutletChangeData<string>) => void
6
+ }
7
+
8
+ export default class StringMatchController
9
+ extends SyncedBooleanAttributesController<string>
10
+ implements StringMatchOutlet
11
+ {
12
+ static outlets = SyncedBooleanAttributesController.outlets
13
+ static targets = ['match', 'empty']
14
+ static values = {
15
+ ...SyncedBooleanAttributesController.values,
16
+ keyword: String,
17
+ }
18
+
19
+ declare readonly matchTargets: Array<HTMLElement>
20
+ declare readonly hasMatchTarget: boolean
21
+
22
+ declare readonly emptyTarget: Element
23
+ declare readonly hasEmptyTarget: boolean
24
+
25
+ declare keywordValue: string
26
+
27
+ change(event: Event, updateTo: TOutletChangeData<string> = {}) {
28
+ const value = updateTo.data ?? (event.currentTarget as HTMLInputElement).value
29
+ this.keywordValue = value
30
+ this.#compareKeywordToTargets()
31
+ this.sendToOutlets(event, {...updateTo, data: this.keywordValue})
32
+ }
33
+ #compareKeywordToTargets() {
34
+ if (this.hasMatchTarget) {
35
+ let foundMatch = false
36
+ for (let index in this.matchTargets) {
37
+ const target = this.matchTargets[index]
38
+ const value = this.getValueForElement(target)
39
+ this.updateAttributesForElement(target, value)
40
+ if (value) {
41
+ foundMatch = true
42
+ }
43
+ }
44
+
45
+ if (this.hasEmptyTarget) {
46
+ this.emptyTarget.setAttribute('aria-hidden', String(foundMatch))
47
+ }
48
+ }
49
+ }
50
+
51
+ getElementsToSync(): Element[] | null | undefined {
52
+ return this.matchTargets
53
+ }
54
+
55
+ getValueForElement(element: Element) {
56
+ return (element as HTMLElement).innerText?.toLowerCase().includes(this.keywordValue.toLowerCase()) ?? false
57
+ }
58
+
59
+ getState() {
60
+ return this.keywordValue
61
+ }
62
+
63
+ outletUpdate = this.change
64
+ }
@@ -0,0 +1,44 @@
1
+ import OutletManagerController from '../outlet_manager_controller/outlet_manager_controller';
2
+ export type TStimulusDispatchEvent<TDetails> = {
3
+ target?: Element | undefined;
4
+ detail?: TDetails | undefined;
5
+ prefix?: string | undefined;
6
+ bubbles?: boolean | undefined;
7
+ cancelable?: boolean | undefined;
8
+ preventDefault: () => void;
9
+ };
10
+ export type TBooleanValueDetail = {
11
+ value: boolean;
12
+ };
13
+ export interface TSyncAttrDetail extends TBooleanValueDetail {
14
+ attr: string;
15
+ }
16
+ export default class SyncedBooleanAttributesController<T> extends OutletManagerController<T> {
17
+ #private;
18
+ static values: {
19
+ syncedAttrs: ArrayConstructor;
20
+ antiAttrs: ArrayConstructor;
21
+ protectAttrs: BooleanConstructor;
22
+ outletEvents: ArrayConstructor;
23
+ };
24
+ readonly syncedAttrsValue: string[];
25
+ readonly hasSyncedAttrsValue: boolean;
26
+ readonly antiAttrsValue: string[];
27
+ readonly hasAntiAttrsValue: boolean;
28
+ readonly protectAttrsValue: boolean;
29
+ static removeOnFalseAttrs: {
30
+ [k: string]: boolean;
31
+ };
32
+ syncedAttrsLookup: {
33
+ [k: string]: boolean;
34
+ } | null;
35
+ antiAttrsLookup: {
36
+ [k: string]: boolean;
37
+ } | null;
38
+ getValueForElement(element: Element): boolean | null;
39
+ getElementsToSync(): Array<Element> | null | undefined;
40
+ connect(): void;
41
+ updateAttributesForElement(element: Element, value: boolean): void;
42
+ syncElementAttributes(): void;
43
+ validateAttrChange(dispatchEvent: TStimulusDispatchEvent<TSyncAttrDetail>): void;
44
+ }
@@ -0,0 +1,153 @@
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 _SyncedBooleanAttributesController_instances, _SyncedBooleanAttributesController_isSyncedAttr, _SyncedBooleanAttributesController_isAntiAttr, _SyncedBooleanAttributesController_isAttr, _SyncedBooleanAttributesController_setAttrs, _SyncedBooleanAttributesController_getLookupForStringArray;
7
+ import OutletManagerController from '../outlet_manager_controller/outlet_manager_controller';
8
+ /*
9
+ This class isn't used directly by itself because it has no functionality. What it does is establishes
10
+ "synced attrs" and "anti-attrs" so that other controllers can extend this one and not worry about
11
+ implementing the logic themselves (and thus have duplicate logic all over our controllers)
12
+
13
+ To implement this, extend your controller with "SyncedBooleanAttributesController" then spread its
14
+ values into your controllers then implement:
15
+
16
+ export default class MyNewController extends SyncedBooleanAttributesController {
17
+ static values = {
18
+ ...SyncedBooleanAttributesController.values,
19
+ myString: String,
20
+ }
21
+ }
22
+
23
+ Also, consider the functions defined here ABOVE the connection() function. Those functions will
24
+ give you control over how your controller can interact with other SyncedBooleanAttributeControllers.
25
+ Not every controller needs them but you should consider them (description can be found in the functions)
26
+
27
+ And don't forget that when you want to change an attr on an element, you should not do it manually.
28
+ Instead run:
29
+ this.updateAttributesForElement(element, value)
30
+ This will let you take advantage of the ecosystem created by this base controller without any additional work
31
+ */
32
+ class SyncedBooleanAttributesController extends OutletManagerController {
33
+ constructor() {
34
+ super(...arguments);
35
+ _SyncedBooleanAttributesController_instances.add(this);
36
+ this.syncedAttrsLookup = null;
37
+ this.antiAttrsLookup = null;
38
+ }
39
+ getValueForElement(element) {
40
+ // This function allows the base controller to access a given element's
41
+ // current status so the attributes can be set or compared. For example,
42
+ // you will want to make sure the attributes are added initially and are
43
+ // in sync with the controller's state. To ensure this, you can use the
44
+ // default connect() function or add "this.syncElementAttributes()" to your
45
+ // custom connect function. It'll look through your targets and get values for
46
+ // each then set the appropriate attrs and anti-attrs
47
+ return null;
48
+ }
49
+ getElementsToSync() {
50
+ // These are the elements your controller wants to keep in sync with the attrs
51
+ // Sometimes these are this.element, sometimes they're specific targets.
52
+ // Return them here so the base controller can automate some behaviors for you
53
+ return [];
54
+ }
55
+ connect() {
56
+ // This function will sync attrs and anti-attrs when the controller connects.
57
+ // The logic is abstracted to a function so you can override this connect
58
+ // function in favor of your own without having to duplicate the sync logic
59
+ this.syncElementAttributes();
60
+ }
61
+ updateAttributesForElement(element, value) {
62
+ // This is how you should update any synced or anti-synced attrs on your elements
63
+ // Do not do it manually unless you are very sure of what you're doing
64
+ if (this.hasSyncedAttrsValue) {
65
+ __classPrivateFieldGet(this, _SyncedBooleanAttributesController_instances, "m", _SyncedBooleanAttributesController_setAttrs).call(this, element, this.syncedAttrsValue, value);
66
+ }
67
+ if (this.hasAntiAttrsValue) {
68
+ __classPrivateFieldGet(this, _SyncedBooleanAttributesController_instances, "m", _SyncedBooleanAttributesController_setAttrs).call(this, element, this.antiAttrsValue, !value);
69
+ }
70
+ }
71
+ syncElementAttributes() {
72
+ var _a;
73
+ this.syncOutlets();
74
+ // Essentially just a "sync attrs and anti-attrs on mount" function
75
+ const elements = this.getElementsToSync();
76
+ if (elements === null || elements === void 0 ? void 0 : elements.length) {
77
+ for (let index in elements) {
78
+ const element = elements[index];
79
+ const value = (_a = this.getValueForElement(element)) !== null && _a !== void 0 ? _a : false;
80
+ this.updateAttributesForElement(element, value);
81
+ }
82
+ }
83
+ }
84
+ validateAttrChange(dispatchEvent) {
85
+ // If you protect your attrs, then this function will deny other controllers you specify from making changes to them.
86
+ // For example, if you want an item to disappear when it's selected, then your Options controller likely has an "aria-hidden"
87
+ // synced attr. If you use another attr to filter the list and then remove that filter, normally that would unhide your selected
88
+ // element. But if you have Options protect its attrs, the filter behavior won't be allowed to change it at any time and thus
89
+ // the element will remain hidden
90
+ const { target, detail } = dispatchEvent;
91
+ if (target && detail) {
92
+ const currentValue = this.getValueForElement(target);
93
+ if (currentValue !== null && this.protectAttrsValue && __classPrivateFieldGet(this, _SyncedBooleanAttributesController_instances, "m", _SyncedBooleanAttributesController_isAttr).call(this, detail.attr)) {
94
+ dispatchEvent.preventDefault();
95
+ }
96
+ }
97
+ }
98
+ }
99
+ _SyncedBooleanAttributesController_instances = new WeakSet(), _SyncedBooleanAttributesController_isSyncedAttr = function _SyncedBooleanAttributesController_isSyncedAttr(attr) {
100
+ var _a;
101
+ // Helper function to determine if the attr is synced
102
+ if (this.syncedAttrsLookup === null) {
103
+ this.syncedAttrsLookup = __classPrivateFieldGet(this, _SyncedBooleanAttributesController_instances, "m", _SyncedBooleanAttributesController_getLookupForStringArray).call(this, this.syncedAttrsValue);
104
+ }
105
+ return (_a = this.syncedAttrsLookup[attr]) !== null && _a !== void 0 ? _a : false;
106
+ }, _SyncedBooleanAttributesController_isAntiAttr = function _SyncedBooleanAttributesController_isAntiAttr(attr) {
107
+ var _a;
108
+ // Helper function to determine if the attr is anti-synced
109
+ if (this.antiAttrsLookup === null) {
110
+ this.antiAttrsLookup = __classPrivateFieldGet(this, _SyncedBooleanAttributesController_instances, "m", _SyncedBooleanAttributesController_getLookupForStringArray).call(this, this.antiAttrsValue);
111
+ }
112
+ return (_a = this.antiAttrsLookup[attr]) !== null && _a !== void 0 ? _a : false;
113
+ }, _SyncedBooleanAttributesController_isAttr = function _SyncedBooleanAttributesController_isAttr(attr) {
114
+ // Helper function to determine if an attr is known to a controller
115
+ return __classPrivateFieldGet(this, _SyncedBooleanAttributesController_instances, "m", _SyncedBooleanAttributesController_isAntiAttr).call(this, attr) || __classPrivateFieldGet(this, _SyncedBooleanAttributesController_instances, "m", _SyncedBooleanAttributesController_isSyncedAttr).call(this, attr);
116
+ }, _SyncedBooleanAttributesController_setAttrs = function _SyncedBooleanAttributesController_setAttrs(element, attrs, value) {
117
+ // Attempts to change the attr for an element. However, it'll dispatch an event
118
+ // first so other controllers get the opportunity to deny it
119
+ const attrState = JSON.stringify(value);
120
+ for (let index in attrs) {
121
+ const attr = attrs[index];
122
+ const dispatchEvent = this.dispatch('attrChange', {
123
+ target: element,
124
+ detail: { attr, value },
125
+ });
126
+ if (!dispatchEvent.defaultPrevented) {
127
+ if (attrState === 'false' && SyncedBooleanAttributesController.removeOnFalseAttrs[attr]) {
128
+ element.removeAttribute(attr);
129
+ }
130
+ else {
131
+ element.setAttribute(attr, attrState);
132
+ }
133
+ }
134
+ }
135
+ }, _SyncedBooleanAttributesController_getLookupForStringArray = function _SyncedBooleanAttributesController_getLookupForStringArray(arr) {
136
+ // Helper function to return an array of strings into an object for easy lookup
137
+ // While the arrays contained here are small, looking up attrs will happen often
138
+ // so I think it's worth the small sacrifice to memory
139
+ if (!(arr === null || arr === void 0 ? void 0 : arr.length)) {
140
+ return {};
141
+ }
142
+ return arr.reduce((acc, cur) => {
143
+ acc[cur] = true;
144
+ return acc;
145
+ }, {});
146
+ };
147
+ SyncedBooleanAttributesController.values = Object.assign(Object.assign({}, OutletManagerController.values), { syncedAttrs: Array, antiAttrs: Array, protectAttrs: Boolean });
148
+ // Some attributes are only false in HTML if they don't exist
149
+ // If included here, the property will be deleted on "false"
150
+ SyncedBooleanAttributesController.removeOnFalseAttrs = {
151
+ checked: true,
152
+ };
153
+ export default SyncedBooleanAttributesController;