primer_view_components 0.1.8 → 0.1.9

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/app/assets/javascripts/primer_view_components.js +1 -1
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  5. data/app/assets/styles/primer_view_components.css +1 -1
  6. data/app/assets/styles/primer_view_components.css.map +1 -1
  7. data/app/components/primer/alpha/action_list/item.rb +1 -3
  8. data/app/components/primer/alpha/action_menu.rb +1 -1
  9. data/app/components/primer/alpha/modal_dialog.js +6 -0
  10. data/app/components/primer/alpha/modal_dialog.ts +6 -0
  11. data/app/components/primer/alpha/overlay/header.html.erb +5 -3
  12. data/app/components/primer/alpha/overlay/header.rb +4 -1
  13. data/app/components/primer/alpha/overlay.css +1 -1
  14. data/app/components/primer/alpha/overlay.css.json +1 -1
  15. data/app/components/primer/alpha/overlay.css.map +1 -1
  16. data/app/components/primer/alpha/overlay.pcss +1 -1
  17. data/app/components/primer/alpha/overlay.rb +1 -0
  18. data/app/components/primer/alpha/toggle_switch.css +1 -1
  19. data/app/components/primer/alpha/toggle_switch.css.json +11 -11
  20. data/app/components/primer/alpha/toggle_switch.css.map +1 -1
  21. data/app/components/primer/alpha/toggle_switch.d.ts +1 -1
  22. data/app/components/primer/alpha/toggle_switch.html.erb +2 -2
  23. data/app/components/primer/alpha/toggle_switch.js +44 -42
  24. data/app/components/primer/alpha/toggle_switch.pcss +4 -4
  25. data/app/components/primer/alpha/toggle_switch.rb +7 -0
  26. data/app/components/primer/alpha/toggle_switch.ts +50 -41
  27. data/app/components/primer/beta/auto_complete.rb +1 -1
  28. data/app/components/primer/focus_group.js +10 -6
  29. data/app/components/primer/focus_group.ts +10 -5
  30. data/lib/primer/forms/dsl/input.rb +4 -8
  31. data/lib/primer/forms/dsl/text_field_input.rb +0 -4
  32. data/lib/primer/forms/dsl/toggle_switch_input.rb +4 -0
  33. data/lib/primer/forms/form_control.html.erb +3 -5
  34. data/lib/primer/forms/primer_base_component_wrapper.html.erb +3 -0
  35. data/lib/primer/forms/primer_base_component_wrapper.rb +24 -0
  36. data/lib/primer/forms/toggle_switch.html.erb +3 -3
  37. data/lib/primer/forms/toggle_switch.rb +6 -2
  38. data/lib/primer/forms/toggle_switch_input.js +7 -2
  39. data/lib/primer/forms/toggle_switch_input.ts +9 -2
  40. data/lib/primer/static/generate_info_arch.rb +3 -0
  41. data/lib/primer/view_components/version.rb +1 -1
  42. data/lib/primer/yard/component_manifest.rb +1 -1
  43. data/lib/primer/yard/lookbook_pages_backend.rb +7 -1
  44. data/lib/primer/yard/registry.rb +4 -0
  45. data/previews/primer/alpha/overlay_preview/middle_of_page_with_relative_container.html.erb +19 -0
  46. data/previews/primer/alpha/overlay_preview.rb +31 -0
  47. data/static/arguments.json +7 -1
  48. data/static/info_arch.json +312 -1
  49. data/static/previews.json +5 -0
  50. metadata +5 -9
  51. data/lib/tasks/docs.rake +0 -185
  52. data/lib/tasks/helpers/ast_processor.rb +0 -44
  53. data/lib/tasks/helpers/ast_traverser.rb +0 -77
  54. data/lib/tasks/primer_view_components.rake +0 -47
  55. data/lib/tasks/static.rake +0 -29
  56. data/lib/tasks/test.rake +0 -83
  57. data/lib/tasks/utilities.rake +0 -109
@@ -42,9 +42,7 @@ module Primer
42
42
  #
43
43
  # To render custom content, call the `with_leading_visual_content` method and pass a block that returns a string.
44
44
  renders_one :leading_visual, types: {
45
- icon: lambda { |**system_arguments|
46
- Primer::Beta::Octicon.new(classes: "ActionListItem-visual ActionListItem-visual--leading", **system_arguments)
47
- },
45
+ icon: Primer::Beta::Octicon,
48
46
  avatar: ->(**kwargs) { Primer::Beta::Avatar.new(**{ **kwargs, size: 16 }) },
49
47
  svg: lambda { |**system_arguments|
50
48
  Primer::BaseComponent.new(tag: :svg, width: "16", height: "16", **system_arguments)
@@ -105,7 +105,7 @@ module Primer
105
105
  # <% c.with_item(tag: :"clipboard-copy", value: "Text to copy") do %>
106
106
  # Copy Text
107
107
  # <% end %>
108
- # <% c.with_item(tag: :button, type: "button", is_dangerous: true) do %>
108
+ # <% c.with_item(tag: :button, type: "button", scheme: :danger) do %>
109
109
  # Delete
110
110
  # <% end %>
111
111
  # <% end %>
@@ -31,6 +31,10 @@ function clickHandler(event) {
31
31
  if (dialog instanceof ModalDialogElement) {
32
32
  dialog.openButton = button;
33
33
  dialog.show();
34
+ // A buttons default behaviour in some browsers it to send a pointer event
35
+ // If the behaviour is allowed through the dialog will be shown but then
36
+ // quickly hidden- as if it were never shown. This prevents that.
37
+ event.preventDefault();
34
38
  return;
35
39
  }
36
40
  }
@@ -96,6 +100,7 @@ export class ModalDialogElement extends HTMLElement {
96
100
  if (this.open)
97
101
  return;
98
102
  this.setAttribute('open', '');
103
+ this.setAttribute('aria-disabled', 'false');
99
104
  (_a = __classPrivateFieldGet(this, _ModalDialogElement_instances, "a", _ModalDialogElement_overlayBackdrop_get)) === null || _a === void 0 ? void 0 : _a.classList.remove('Overlay--hidden');
100
105
  document.body.style.paddingRight = `${window.innerWidth - document.body.clientWidth}px`;
101
106
  document.body.style.overflow = 'hidden';
@@ -109,6 +114,7 @@ export class ModalDialogElement extends HTMLElement {
109
114
  if (!this.open)
110
115
  return;
111
116
  this.removeAttribute('open');
117
+ this.setAttribute('aria-disabled', 'true');
112
118
  (_b = __classPrivateFieldGet(this, _ModalDialogElement_instances, "a", _ModalDialogElement_overlayBackdrop_get)) === null || _b === void 0 ? void 0 : _b.classList.add('Overlay--hidden');
113
119
  document.body.style.paddingRight = '0';
114
120
  document.body.style.overflow = 'initial';
@@ -23,6 +23,10 @@ function clickHandler(event: Event) {
23
23
  if (dialog instanceof ModalDialogElement) {
24
24
  dialog.openButton = button
25
25
  dialog.show()
26
+ // A buttons default behaviour in some browsers it to send a pointer event
27
+ // If the behaviour is allowed through the dialog will be shown but then
28
+ // quickly hidden- as if it were never shown. This prevents that.
29
+ event.preventDefault()
26
30
  return
27
31
  }
28
32
  }
@@ -96,6 +100,7 @@ export class ModalDialogElement extends HTMLElement {
96
100
  if (value) {
97
101
  if (this.open) return
98
102
  this.setAttribute('open', '')
103
+ this.setAttribute('aria-disabled', 'false')
99
104
  this.#overlayBackdrop?.classList.remove('Overlay--hidden')
100
105
  document.body.style.paddingRight = `${window.innerWidth - document.body.clientWidth}px`
101
106
  document.body.style.overflow = 'hidden'
@@ -107,6 +112,7 @@ export class ModalDialogElement extends HTMLElement {
107
112
  } else {
108
113
  if (!this.open) return
109
114
  this.removeAttribute('open')
115
+ this.setAttribute('aria-disabled', 'true')
110
116
  this.#overlayBackdrop?.classList.add('Overlay--hidden')
111
117
  document.body.style.paddingRight = '0'
112
118
  document.body.style.overflow = 'initial'
@@ -8,8 +8,10 @@
8
8
  <h2 class="Overlay-description"><%= @subtitle %></h2>
9
9
  <% end %>
10
10
  </div>
11
- <div class="Overlay-actionWrap">
12
- <%= render Primer::Beta::CloseButton.new(classes: "Overlay-closeButton", "popoverhidetarget": @id) %>
13
- </div>
11
+ <% if @overlay_id %>
12
+ <div class="Overlay-actionWrap">
13
+ <%= render Primer::Beta::CloseButton.new(classes: "Overlay-closeButton", "popovertarget": @overlay_id, "popovertargetaction": "hide") %>
14
+ </div>
15
+ <% end %>
14
16
  </div>
15
17
  <% end %>
@@ -14,7 +14,8 @@ module Primer
14
14
  SIZE_OPTIONS = SIZE_MAPPINGS.keys
15
15
 
16
16
  # @param title [String] Describes the content of the Overlay.
17
- # @param subtitle [String] Provides dditional context for the Overlay, also setting the `aria-describedby` attribute.
17
+ # @param subtitle [String] Provides additional context for the Overlay, also setting the `aria-describedby` attribute.
18
+ # @param overlay_id [String] Provides the id of the overlay element so the close button can close it
18
19
  # @param size [Symbol] The size of the Header. <%= one_of(Primer::Alpha::Overlay::Header::SIZE_OPTIONS) %>
19
20
  # @param divider [Boolean] Show a divider between the header and body.
20
21
  # @param visually_hide_title [Boolean] Visually hide the `title` while maintaining a label for assistive technologies.
@@ -22,12 +23,14 @@ module Primer
22
23
  def initialize(
23
24
  id:,
24
25
  title:,
26
+ overlay_id: nil,
25
27
  subtitle: nil,
26
28
  size: DEFAULT_SIZE,
27
29
  divider: false,
28
30
  visually_hide_title: false,
29
31
  **system_arguments
30
32
  )
33
+ @overlay_id = overlay_id
31
34
  @id = id
32
35
  @title = title
33
36
  @subtitle = subtitle
@@ -1 +1 @@
1
- .Overlay[popover]{border-width:0;min-width:192px;padding:0;position:absolute}.Overlay[popover]:not(:open){display:none}anchored-position{display:block}
1
+ .Overlay[popover]{border-width:0;min-width:192px;padding:0;position:absolute}.Overlay[popover]:not(:popover-open){display:none}anchored-position{display:block}
@@ -2,7 +2,7 @@
2
2
  "name": "alpha/overlay",
3
3
  "selectors": [
4
4
  ".Overlay[popover]",
5
- ".Overlay[popover]:not(:open)",
5
+ ".Overlay[popover]:not(:popover-open)",
6
6
  "anchored-position"
7
7
  ]
8
8
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["overlay.pcss"],"names":[],"mappings":"AAAA,kBACE,cAAe,CAGf,eAAgB,CAFhB,SAAU,CACV,iBAEF,CAGA,6BACE,YACF,CAEA,kBACE,aACF","file":"overlay.css","sourcesContent":[".Overlay[popover] {\n border-width: 0;\n padding: 0;\n position: absolute;\n min-width: 192px;\n}\n\n/* stylelint-disable-next-line selector-pseudo-class-no-unknown */\n.Overlay[popover]:not(:open) {\n display: none;\n}\n\nanchored-position {\n display: block;\n}\n"]}
1
+ {"version":3,"sources":["overlay.pcss"],"names":[],"mappings":"AAAA,kBACE,cAAe,CAGf,eAAgB,CAFhB,SAAU,CACV,iBAEF,CAGA,qCACE,YACF,CAEA,kBACE,aACF","file":"overlay.css","sourcesContent":[".Overlay[popover] {\n border-width: 0;\n padding: 0;\n position: absolute;\n min-width: 192px;\n}\n\n/* stylelint-disable-next-line selector-pseudo-class-no-unknown */\n.Overlay[popover]:not(:popover-open) {\n display: none;\n}\n\nanchored-position {\n display: block;\n}\n"]}
@@ -6,7 +6,7 @@
6
6
  }
7
7
 
8
8
  /* stylelint-disable-next-line selector-pseudo-class-no-unknown */
9
- .Overlay[popover]:not(:open) {
9
+ .Overlay[popover]:not(:popover-open) {
10
10
  display: none;
11
11
  }
12
12
 
@@ -94,6 +94,7 @@ module Primer
94
94
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
95
95
  renders_one :header, lambda { |divider: false, size: :medium, visually_hide_title: @visually_hide_title, **system_arguments|
96
96
  Primer::Alpha::Overlay::Header.new(
97
+ overlay_id: @id,
97
98
  id: title_id,
98
99
  title: @title,
99
100
  subtitle: @subtitle,
@@ -1 +1 @@
1
- .ToggleSwitch,.ToggleSwitch.ToggleSwitch{display:inline-flex}.ToggleSwitch{align-items:center;gap:var(--controlStack-medium-gap-condensed,8px)}.ToggleSwitch--checked .ToggleSwitch-statusOn{height:auto;visibility:visible}.ToggleSwitch--checked .ToggleSwitch-statusOff{height:0;visibility:hidden}.ToggleSwitch-track{-webkit-appearance:none;appearance:none;background-color:var(--color-switch-track-bg);border:var(--borderWidth-thin,1px) solid var(--color-switch-track-border);border-radius:var(--borderRadius-medium,6px);cursor:pointer;display:block;height:var(--control-medium-size,32px);overflow:hidden;padding:0;position:relative;text-decoration:none;transition-duration:80ms;transition-property:background-color,border-color;transition-timing-function:cubic-bezier(.5,1,.89,1);-webkit-user-select:none;user-select:none;width:var(--base-size-64,64px)}.ToggleSwitch-track:focus,.ToggleSwitch-track:focus-visible{outline-offset:1px}.ToggleSwitch-track:hover{background-color:var(--color-switch-track-hover-bg)}.ToggleSwitch-track:active{background-color:var(--color-switch-track-active-bg)}@media (pointer:coarse){.ToggleSwitch-track:before{content:"";height:100%;left:50%;min-height:44px;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:100%}}@media (prefers-reduced-motion){.ToggleSwitch-track,.ToggleSwitch-track *{transition:none}}.ToggleSwitch-track[aria-checked=true][aria-disabled=true]{background-color:var(--color-switch-track-disabled-bg);border-color:#0000;color:var(--color-switch-track-checked-disabled-fg)}.ToggleSwitch-track[aria-checked=true]{background-color:var(--color-switch-track-checked-bg);border-color:var(--color-switch-track-checked-border)}.ToggleSwitch-track[aria-checked=true]:not([aria-disabled=true]):hover{background-color:var(--color-switch-track-checked-hover-bg)}.ToggleSwitch-track[aria-checked=true]:not([aria-disabled=true]):active{background-color:var(--color-switch-track-checked-active-bg)}.ToggleSwitch-track[aria-checked=true] .ToggleSwitch-knob{background-color:var(--color-switch-knob-checked-bg);border-color:var(--color-switch-knob-checked-border);transform:translateX(100%)}.ToggleSwitch-track[aria-checked=true] .ToggleSwitch-lineIcon{transform:translateX(0)}.ToggleSwitch-track[aria-checked=true] .ToggleSwitch-circleIcon{transform:translateX(100%)}.ToggleSwitch-track[aria-disabled=true]{background-color:var(--color-switch-track-disabled-bg);border-color:#0000;cursor:not-allowed;transition-property:none}.ToggleSwitch-track[aria-disabled=true] .ToggleSwitch-knob{border-color:var(--color-border-default);box-shadow:none}.ToggleSwitch-track[aria-disabled=true] .ToggleSwitch-circleIcon,.ToggleSwitch-track[aria-disabled=true] .ToggleSwitch-lineIcon{color:var(--color-switch-track-disabled-fg)}.ToggleSwitch-icons{align-items:center;display:flex;height:100%;overflow:hidden;width:100%}.ToggleSwitch-lineIcon{color:var(--color-switch-track-checked-fg);transform:translateX(-100%)}.ToggleSwitch-circleIcon,.ToggleSwitch-lineIcon{flex:1 0 50%;line-height:0;transition-duration:80ms;transition-property:transform}.ToggleSwitch-circleIcon{color:var(--color-switch-track-fg);transform:translateX(0)}.ToggleSwitch-knob{background-color:var(--color-switch-knob-bg);border:var(--borderWidth-thin,1px) solid var(--color-switch-knob-border);border-radius:var(--borderRadius-medium,6px);bottom:0;box-shadow:var(--color-shadow-medium),var(--color-btn-inset-shadow);position:absolute;top:0;transition-duration:80ms;transition-property:transform;transition-timing-function:cubic-bezier(.5,1,.89,1);width:50%;z-index:1}@media (prefers-reduced-motion){.ToggleSwitch-knob{transition:none}}.ToggleSwitch-status{color:var(--color-fg-default);font-size:var(--text-body-size-medium,14px);line-height:1.5;position:relative;text-align:right}.ToggleSwitch-statusIcon{display:flex;margin-top:.063rem;width:var(--base-size-16,16px)}.ToggleSwitch--small .ToggleSwitch-status{font-size:var(--text-body-size-small,12px)}.ToggleSwitch--small .ToggleSwitch-track{height:var(--control-xsmall-size,24px);width:var(--base-size-48,48px)}.ToggleSwitch--disabled .ToggleSwitch-status{color:var(--color-fg-muted)}.ToggleSwitch-statusOn{height:0;visibility:hidden}.ToggleSwitch-statusOff{height:auto;visibility:visible}.ToggleSwitch--statusAtEnd{flex-direction:row-reverse}.ToggleSwitch--statusAtEnd .ToggleSwitch-status{text-align:left}
1
+ .ToggleSwitch,.ToggleSwitch.ToggleSwitch{display:inline-flex}.ToggleSwitch{align-items:center;gap:var(--controlStack-medium-gap-condensed,8px)}.ToggleSwitch--checked .ToggleSwitch-statusOn{height:auto;visibility:visible}.ToggleSwitch--checked .ToggleSwitch-statusOff{height:0;visibility:hidden}.ToggleSwitch-track{-webkit-appearance:none;appearance:none;background-color:var(--color-switch-track-bg);border:var(--borderWidth-thin,1px) solid var(--color-switch-track-border);border-radius:var(--borderRadius-medium,6px);cursor:pointer;display:block;height:var(--control-medium-size,32px);overflow:hidden;padding:0;position:relative;text-decoration:none;transition-duration:80ms;transition-property:background-color,border-color;transition-timing-function:cubic-bezier(.5,1,.89,1);-webkit-user-select:none;user-select:none;width:var(--base-size-64,64px)}.ToggleSwitch-track:focus,.ToggleSwitch-track:focus-visible{outline-offset:1px}.ToggleSwitch-track:hover{background-color:var(--color-switch-track-hover-bg)}.ToggleSwitch-track:active{background-color:var(--color-switch-track-active-bg)}@media (pointer:coarse){.ToggleSwitch-track:before{content:"";height:100%;left:50%;min-height:44px;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:100%}}@media (prefers-reduced-motion){.ToggleSwitch-track,.ToggleSwitch-track *{transition:none}}.ToggleSwitch-track[aria-pressed=true][disabled]{background-color:var(--color-switch-track-disabled-bg);border-color:#0000;color:var(--color-switch-track-checked-disabled-fg)}.ToggleSwitch-track[aria-pressed=true]{background-color:var(--color-switch-track-checked-bg);border-color:var(--color-switch-track-checked-border)}.ToggleSwitch-track[aria-pressed=true]:not([disabled]):hover{background-color:var(--color-switch-track-checked-hover-bg)}.ToggleSwitch-track[aria-pressed=true]:not([disabled]):active{background-color:var(--color-switch-track-checked-active-bg)}.ToggleSwitch-track[aria-pressed=true] .ToggleSwitch-knob{background-color:var(--color-switch-knob-checked-bg);border-color:var(--color-switch-knob-checked-border);transform:translateX(100%)}.ToggleSwitch-track[aria-pressed=true] .ToggleSwitch-lineIcon{transform:translateX(0)}.ToggleSwitch-track[aria-pressed=true] .ToggleSwitch-circleIcon{transform:translateX(100%)}.ToggleSwitch-track[disabled]{background-color:var(--color-switch-track-disabled-bg);border-color:#0000;cursor:not-allowed;transition-property:none}.ToggleSwitch-track[disabled] .ToggleSwitch-knob{border-color:var(--color-border-default);box-shadow:none}.ToggleSwitch-track[disabled] .ToggleSwitch-circleIcon,.ToggleSwitch-track[disabled] .ToggleSwitch-lineIcon{color:var(--color-switch-track-disabled-fg)}.ToggleSwitch-icons{align-items:center;display:flex;height:100%;overflow:hidden;width:100%}.ToggleSwitch-lineIcon{color:var(--color-switch-track-checked-fg);transform:translateX(-100%)}.ToggleSwitch-circleIcon,.ToggleSwitch-lineIcon{flex:1 0 50%;line-height:0;transition-duration:80ms;transition-property:transform}.ToggleSwitch-circleIcon{color:var(--color-switch-track-fg);transform:translateX(0)}.ToggleSwitch-knob{background-color:var(--color-switch-knob-bg);border:var(--borderWidth-thin,1px) solid var(--color-switch-knob-border);border-radius:var(--borderRadius-medium,6px);bottom:0;box-shadow:var(--color-shadow-medium),var(--color-btn-inset-shadow);position:absolute;top:0;transition-duration:80ms;transition-property:transform;transition-timing-function:cubic-bezier(.5,1,.89,1);width:50%;z-index:1}@media (prefers-reduced-motion){.ToggleSwitch-knob{transition:none}}.ToggleSwitch-status{color:var(--color-fg-default);font-size:var(--text-body-size-medium,14px);line-height:1.5;position:relative;text-align:right}.ToggleSwitch-statusIcon{display:flex;margin-top:.063rem;width:var(--base-size-16,16px)}.ToggleSwitch--small .ToggleSwitch-status{font-size:var(--text-body-size-small,12px)}.ToggleSwitch--small .ToggleSwitch-track{height:var(--control-xsmall-size,24px);width:var(--base-size-48,48px)}.ToggleSwitch--disabled .ToggleSwitch-status{color:var(--color-fg-muted)}.ToggleSwitch-statusOn{height:0;visibility:hidden}.ToggleSwitch-statusOff{height:auto;visibility:visible}.ToggleSwitch--statusAtEnd{flex-direction:row-reverse}.ToggleSwitch--statusAtEnd .ToggleSwitch-status{text-align:left}
@@ -12,17 +12,17 @@
12
12
  ".ToggleSwitch-track:active",
13
13
  ".ToggleSwitch-track:before",
14
14
  ".ToggleSwitch-track *",
15
- ".ToggleSwitch-track[aria-checked=true][aria-disabled=true]",
16
- ".ToggleSwitch-track[aria-checked=true]",
17
- ".ToggleSwitch-track[aria-checked=true]:not([aria-disabled=true]):hover",
18
- ".ToggleSwitch-track[aria-checked=true]:not([aria-disabled=true]):active",
19
- ".ToggleSwitch-track[aria-checked=true] .ToggleSwitch-knob",
20
- ".ToggleSwitch-track[aria-checked=true] .ToggleSwitch-lineIcon",
21
- ".ToggleSwitch-track[aria-checked=true] .ToggleSwitch-circleIcon",
22
- ".ToggleSwitch-track[aria-disabled=true]",
23
- ".ToggleSwitch-track[aria-disabled=true] .ToggleSwitch-knob",
24
- ".ToggleSwitch-track[aria-disabled=true] .ToggleSwitch-circleIcon",
25
- ".ToggleSwitch-track[aria-disabled=true] .ToggleSwitch-lineIcon",
15
+ ".ToggleSwitch-track[aria-pressed=true][disabled]",
16
+ ".ToggleSwitch-track[aria-pressed=true]",
17
+ ".ToggleSwitch-track[aria-pressed=true]:not([disabled]):hover",
18
+ ".ToggleSwitch-track[aria-pressed=true]:not([disabled]):active",
19
+ ".ToggleSwitch-track[aria-pressed=true] .ToggleSwitch-knob",
20
+ ".ToggleSwitch-track[aria-pressed=true] .ToggleSwitch-lineIcon",
21
+ ".ToggleSwitch-track[aria-pressed=true] .ToggleSwitch-circleIcon",
22
+ ".ToggleSwitch-track[disabled]",
23
+ ".ToggleSwitch-track[disabled] .ToggleSwitch-knob",
24
+ ".ToggleSwitch-track[disabled] .ToggleSwitch-circleIcon",
25
+ ".ToggleSwitch-track[disabled] .ToggleSwitch-lineIcon",
26
26
  ".ToggleSwitch-icons",
27
27
  ".ToggleSwitch-lineIcon",
28
28
  ".ToggleSwitch-circleIcon",
@@ -1 +1 @@
1
- {"version":3,"sources":["toggle_switch.pcss","<no source>"],"names":[],"mappings":"AAQA,yCAHE,mBAOF,CAJA,cACE,kBAAmB,CAEnB,gDACF,CAGE,8CACE,WAAY,CACZ,kBACF,CAEA,+CACE,QAAS,CACT,iBACF,CAGF,oBAgBE,uBAAgB,CAAhB,eAAgB,CANhB,6CAA8C,CAC9C,yEAA2E,CAC3E,4CAA8C,CAJ9C,cAAe,CANf,aAAc,CAEd,sCAAwC,CAExC,eAAgB,CADhB,SAAU,CAJV,iBAAkB,CAMlB,oBAAqB,CAOrB,wBAAyB,CACzB,iDAAmD,CAFnD,mDAAyD,CAJzD,wBAAiB,CAAjB,gBAAiB,CANjB,8BAyCF,CA1BE,4DAEE,kBACF,CAEA,0BACE,mDACF,CAEA,2BACE,oDACF,CAEA,wBAEI,2BC3DN,WAAA,YAAA,SAAA,gBAAA,kBAAA,QAAA,4CAAA,UD2DgC,CAE9B,CAEA,gCAGE,0CACE,eACF,CACF,CAGF,2DACE,sDAAuD,CAEvD,kBAAyB,CADzB,mDAEF,CAEA,uCACE,qDAAsD,CACtD,qDAyBF,CAtBI,uEACE,2DACF,CAEA,wEACE,4DACF,CAGF,0DACE,oDAAqD,CACrD,oDAAqD,CACrD,0BACF,CAEA,8DACE,uBACF,CAEA,gEACE,0BACF,CAGF,wCAEE,sDAAuD,CACvD,kBAAyB,CAFzB,kBAAmB,CAGnB,wBAcF,CAZE,2DACE,wCAAyC,CACzC,eACF,CAMA,gIACE,2CACF,CAGF,oBAEE,kBAAmB,CADnB,YAAa,CAGb,WAAY,CACZ,eAAgB,CAFhB,UAGF,CAEA,uBAEE,0CAA2C,CAG3C,2BAEF,CAEA,gDAHE,YAAa,CALb,aAAc,CAEd,wBAAyB,CACzB,6BAYF,CAPA,yBAEE,kCAAmC,CAGnC,uBAEF,CAEA,mBAME,4CAA6C,CAC7C,wEAA0E,CAC1E,4CAA8C,CAL9C,QAAS,CAMT,mEAAqE,CARrE,iBAAkB,CAClB,KAAM,CASN,wBAAyB,CACzB,6BAA8B,CAF9B,mDAAyD,CALzD,SAAU,CADV,SAaF,CAHE,gCAdF,mBAeI,eAEJ,CADE,CAGF,qBAIE,6BAA8B,CAF9B,2CAA6C,CAC7C,eAAgB,CAFhB,iBAAkB,CAIlB,gBACF,CAEA,yBAEE,YAAa,CACb,kBAAoB,CAFpB,8BAGF,CAGE,0CACE,0CACF,CAEA,yCAEE,sCAAwC,CADxC,8BAEF,CAIA,6CACE,2BACF,CAGF,uBACE,QAAS,CACT,iBACF,CAEA,wBACE,WAAY,CACZ,kBACF,CAEA,2BACE,0BAKF,CAHE,gDACE,eACF","file":"toggle_switch.css","sourcesContent":["/* ToggleSwitch */\n\n/* Catalyst in dotcom applies a display: block to all web component elements. This\n** rule overrides it so the status label and toggle switch are laid out correctly. */\n.ToggleSwitch.ToggleSwitch {\n display: inline-flex;\n}\n\n.ToggleSwitch {\n align-items: center;\n display: inline-flex;\n gap: var(--controlStack-medium-gap-condensed, 8px);\n}\n\n.ToggleSwitch--checked {\n & .ToggleSwitch-statusOn {\n height: auto;\n visibility: visible;\n }\n\n & .ToggleSwitch-statusOff {\n height: 0;\n visibility: hidden;\n }\n}\n\n.ToggleSwitch-track {\n position: relative;\n display: block;\n width: var(--base-size-64, 64px);\n height: var(--control-medium-size, 32px);\n padding: 0;\n overflow: hidden;\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n background-color: var(--color-switch-track-bg);\n border: var(--borderWidth-thin, 1px) solid var(--color-switch-track-border);\n border-radius: var(--borderRadius-medium, 6px);\n transition-timing-function: cubic-bezier(0.5, 1, 0.89, 1);\n transition-duration: 80ms;\n transition-property: background-color, border-color;\n appearance: none;\n\n &:focus,\n &:focus-visible {\n outline-offset: 1px;\n }\n\n &:hover {\n background-color: var(--color-switch-track-hover-bg);\n }\n\n &:active {\n background-color: var(--color-switch-track-active-bg);\n }\n\n @media (pointer: coarse) {\n &::before {\n @mixin minTouchTarget 44px;\n }\n }\n\n @media (prefers-reduced-motion) {\n transition: none;\n\n & * {\n transition: none;\n }\n }\n}\n\n.ToggleSwitch-track[aria-checked='true'][aria-disabled='true'] {\n background-color: var(--color-switch-track-disabled-bg);\n color: var(--color-switch-track-checked-disabled-fg);\n border-color: transparent;\n}\n\n.ToggleSwitch-track[aria-checked='true'] {\n background-color: var(--color-switch-track-checked-bg);\n border-color: var(--color-switch-track-checked-border);\n\n &:not([aria-disabled='true']) {\n &:hover {\n background-color: var(--color-switch-track-checked-hover-bg);\n }\n\n &:active {\n background-color: var(--color-switch-track-checked-active-bg);\n }\n }\n\n & .ToggleSwitch-knob {\n background-color: var(--color-switch-knob-checked-bg);\n border-color: var(--color-switch-knob-checked-border);\n transform: translateX(100%);\n }\n\n & .ToggleSwitch-lineIcon {\n transform: translateX(0%);\n }\n\n & .ToggleSwitch-circleIcon {\n transform: translateX(100%);\n }\n}\n\n.ToggleSwitch-track[aria-disabled='true'] {\n cursor: not-allowed;\n background-color: var(--color-switch-track-disabled-bg);\n border-color: transparent;\n transition-property: none;\n\n & .ToggleSwitch-knob {\n border-color: var(--color-border-default);\n box-shadow: none;\n }\n\n & .ToggleSwitch-lineIcon {\n color: var(--color-switch-track-disabled-fg);\n }\n\n & .ToggleSwitch-circleIcon {\n color: var(--color-switch-track-disabled-fg);\n }\n}\n\n.ToggleSwitch-icons {\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.ToggleSwitch-lineIcon {\n line-height: 0;\n color: var(--color-switch-track-checked-fg);\n transition-duration: 80ms;\n transition-property: transform;\n transform: translateX(-100%);\n flex: 1 0 50%;\n}\n\n.ToggleSwitch-circleIcon {\n line-height: 0;\n color: var(--color-switch-track-fg);\n transition-duration: 80ms;\n transition-property: transform;\n transform: translateX(0);\n flex: 1 0 50%;\n}\n\n.ToggleSwitch-knob {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 1;\n width: 50%;\n background-color: var(--color-switch-knob-bg);\n border: var(--borderWidth-thin, 1px) solid var(--color-switch-knob-border);\n border-radius: var(--borderRadius-medium, 6px);\n box-shadow: var(--color-shadow-medium), var(--color-btn-inset-shadow);\n transition-timing-function: cubic-bezier(0.5, 1, 0.89, 1);\n transition-duration: 80ms;\n transition-property: transform;\n\n @media (prefers-reduced-motion) {\n transition: none;\n }\n}\n\n.ToggleSwitch-status {\n position: relative;\n font-size: var(--text-body-size-medium, 14px);\n line-height: 1.5;\n color: var(--color-fg-default);\n text-align: right;\n}\n\n.ToggleSwitch-statusIcon {\n width: var(--base-size-16, 16px);\n display: flex;\n margin-top: 0.063rem;\n}\n\n.ToggleSwitch--small {\n & .ToggleSwitch-status {\n font-size: var(--text-body-size-small, 12px);\n }\n\n & .ToggleSwitch-track {\n width: var(--base-size-48, 48px);\n height: var(--control-xsmall-size, 24px);\n }\n}\n\n.ToggleSwitch--disabled {\n & .ToggleSwitch-status {\n color: var(--color-fg-muted);\n }\n}\n\n.ToggleSwitch-statusOn {\n height: 0;\n visibility: hidden;\n}\n\n.ToggleSwitch-statusOff {\n height: auto;\n visibility: visible;\n}\n\n.ToggleSwitch--statusAtEnd {\n flex-direction: row-reverse;\n\n & .ToggleSwitch-status {\n text-align: left;\n }\n}\n",null]}
1
+ {"version":3,"sources":["toggle_switch.pcss","<no source>"],"names":[],"mappings":"AAQA,yCAHE,mBAOF,CAJA,cACE,kBAAmB,CAEnB,gDACF,CAGE,8CACE,WAAY,CACZ,kBACF,CAEA,+CACE,QAAS,CACT,iBACF,CAGF,oBAgBE,uBAAgB,CAAhB,eAAgB,CANhB,6CAA8C,CAC9C,yEAA2E,CAC3E,4CAA8C,CAJ9C,cAAe,CANf,aAAc,CAEd,sCAAwC,CAExC,eAAgB,CADhB,SAAU,CAJV,iBAAkB,CAMlB,oBAAqB,CAOrB,wBAAyB,CACzB,iDAAmD,CAFnD,mDAAyD,CAJzD,wBAAiB,CAAjB,gBAAiB,CANjB,8BAyCF,CA1BE,4DAEE,kBACF,CAEA,0BACE,mDACF,CAEA,2BACE,oDACF,CAEA,wBAEI,2BC3DN,WAAA,YAAA,SAAA,gBAAA,kBAAA,QAAA,4CAAA,UD2DgC,CAE9B,CAEA,gCAGE,0CACE,eACF,CACF,CAGF,iDACE,sDAAuD,CAEvD,kBAAyB,CADzB,mDAEF,CAEA,uCACE,qDAAsD,CACtD,qDAyBF,CAtBI,6DACE,2DACF,CAEA,8DACE,4DACF,CAGF,0DACE,oDAAqD,CACrD,oDAAqD,CACrD,0BACF,CAEA,8DACE,uBACF,CAEA,gEACE,0BACF,CAGF,8BAEE,sDAAuD,CACvD,kBAAyB,CAFzB,kBAAmB,CAGnB,wBAcF,CAZE,iDACE,wCAAyC,CACzC,eACF,CAMA,4GACE,2CACF,CAGF,oBAEE,kBAAmB,CADnB,YAAa,CAGb,WAAY,CACZ,eAAgB,CAFhB,UAGF,CAEA,uBAEE,0CAA2C,CAG3C,2BAEF,CAEA,gDAHE,YAAa,CALb,aAAc,CAEd,wBAAyB,CACzB,6BAYF,CAPA,yBAEE,kCAAmC,CAGnC,uBAEF,CAEA,mBAME,4CAA6C,CAC7C,wEAA0E,CAC1E,4CAA8C,CAL9C,QAAS,CAMT,mEAAqE,CARrE,iBAAkB,CAClB,KAAM,CASN,wBAAyB,CACzB,6BAA8B,CAF9B,mDAAyD,CALzD,SAAU,CADV,SAaF,CAHE,gCAdF,mBAeI,eAEJ,CADE,CAGF,qBAIE,6BAA8B,CAF9B,2CAA6C,CAC7C,eAAgB,CAFhB,iBAAkB,CAIlB,gBACF,CAEA,yBAEE,YAAa,CACb,kBAAoB,CAFpB,8BAGF,CAGE,0CACE,0CACF,CAEA,yCAEE,sCAAwC,CADxC,8BAEF,CAIA,6CACE,2BACF,CAGF,uBACE,QAAS,CACT,iBACF,CAEA,wBACE,WAAY,CACZ,kBACF,CAEA,2BACE,0BAKF,CAHE,gDACE,eACF","file":"toggle_switch.css","sourcesContent":["/* ToggleSwitch */\n\n/* Catalyst in dotcom applies a display: block to all web component elements. This\n** rule overrides it so the status label and toggle switch are laid out correctly. */\n.ToggleSwitch.ToggleSwitch {\n display: inline-flex;\n}\n\n.ToggleSwitch {\n align-items: center;\n display: inline-flex;\n gap: var(--controlStack-medium-gap-condensed, 8px);\n}\n\n.ToggleSwitch--checked {\n & .ToggleSwitch-statusOn {\n height: auto;\n visibility: visible;\n }\n\n & .ToggleSwitch-statusOff {\n height: 0;\n visibility: hidden;\n }\n}\n\n.ToggleSwitch-track {\n position: relative;\n display: block;\n width: var(--base-size-64, 64px);\n height: var(--control-medium-size, 32px);\n padding: 0;\n overflow: hidden;\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n background-color: var(--color-switch-track-bg);\n border: var(--borderWidth-thin, 1px) solid var(--color-switch-track-border);\n border-radius: var(--borderRadius-medium, 6px);\n transition-timing-function: cubic-bezier(0.5, 1, 0.89, 1);\n transition-duration: 80ms;\n transition-property: background-color, border-color;\n appearance: none;\n\n &:focus,\n &:focus-visible {\n outline-offset: 1px;\n }\n\n &:hover {\n background-color: var(--color-switch-track-hover-bg);\n }\n\n &:active {\n background-color: var(--color-switch-track-active-bg);\n }\n\n @media (pointer: coarse) {\n &::before {\n @mixin minTouchTarget 44px;\n }\n }\n\n @media (prefers-reduced-motion) {\n transition: none;\n\n & * {\n transition: none;\n }\n }\n}\n\n.ToggleSwitch-track[aria-pressed='true'][disabled] {\n background-color: var(--color-switch-track-disabled-bg);\n color: var(--color-switch-track-checked-disabled-fg);\n border-color: transparent;\n}\n\n.ToggleSwitch-track[aria-pressed='true'] {\n background-color: var(--color-switch-track-checked-bg);\n border-color: var(--color-switch-track-checked-border);\n\n &:not([disabled]) {\n &:hover {\n background-color: var(--color-switch-track-checked-hover-bg);\n }\n\n &:active {\n background-color: var(--color-switch-track-checked-active-bg);\n }\n }\n\n & .ToggleSwitch-knob {\n background-color: var(--color-switch-knob-checked-bg);\n border-color: var(--color-switch-knob-checked-border);\n transform: translateX(100%);\n }\n\n & .ToggleSwitch-lineIcon {\n transform: translateX(0%);\n }\n\n & .ToggleSwitch-circleIcon {\n transform: translateX(100%);\n }\n}\n\n.ToggleSwitch-track[disabled] {\n cursor: not-allowed;\n background-color: var(--color-switch-track-disabled-bg);\n border-color: transparent;\n transition-property: none;\n\n & .ToggleSwitch-knob {\n border-color: var(--color-border-default);\n box-shadow: none;\n }\n\n & .ToggleSwitch-lineIcon {\n color: var(--color-switch-track-disabled-fg);\n }\n\n & .ToggleSwitch-circleIcon {\n color: var(--color-switch-track-disabled-fg);\n }\n}\n\n.ToggleSwitch-icons {\n display: flex;\n align-items: center;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.ToggleSwitch-lineIcon {\n line-height: 0;\n color: var(--color-switch-track-checked-fg);\n transition-duration: 80ms;\n transition-property: transform;\n transform: translateX(-100%);\n flex: 1 0 50%;\n}\n\n.ToggleSwitch-circleIcon {\n line-height: 0;\n color: var(--color-switch-track-fg);\n transition-duration: 80ms;\n transition-property: transform;\n transform: translateX(0);\n flex: 1 0 50%;\n}\n\n.ToggleSwitch-knob {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 1;\n width: 50%;\n background-color: var(--color-switch-knob-bg);\n border: var(--borderWidth-thin, 1px) solid var(--color-switch-knob-border);\n border-radius: var(--borderRadius-medium, 6px);\n box-shadow: var(--color-shadow-medium), var(--color-btn-inset-shadow);\n transition-timing-function: cubic-bezier(0.5, 1, 0.89, 1);\n transition-duration: 80ms;\n transition-property: transform;\n\n @media (prefers-reduced-motion) {\n transition: none;\n }\n}\n\n.ToggleSwitch-status {\n position: relative;\n font-size: var(--text-body-size-medium, 14px);\n line-height: 1.5;\n color: var(--color-fg-default);\n text-align: right;\n}\n\n.ToggleSwitch-statusIcon {\n width: var(--base-size-16, 16px);\n display: flex;\n margin-top: 0.063rem;\n}\n\n.ToggleSwitch--small {\n & .ToggleSwitch-status {\n font-size: var(--text-body-size-small, 12px);\n }\n\n & .ToggleSwitch-track {\n width: var(--base-size-48, 48px);\n height: var(--control-xsmall-size, 24px);\n }\n}\n\n.ToggleSwitch--disabled {\n & .ToggleSwitch-status {\n color: var(--color-fg-muted);\n }\n}\n\n.ToggleSwitch-statusOn {\n height: 0;\n visibility: hidden;\n}\n\n.ToggleSwitch-statusOff {\n height: auto;\n visibility: visible;\n}\n\n.ToggleSwitch--statusAtEnd {\n flex-direction: row-reverse;\n\n & .ToggleSwitch-status {\n text-align: left;\n }\n}\n",null]}
@@ -6,7 +6,7 @@ declare class ToggleSwitchElement extends HTMLElement {
6
6
  get csrf(): string | null;
7
7
  get csrfField(): string;
8
8
  isRemote(): boolean;
9
- toggle(): void;
9
+ toggle(): Promise<void>;
10
10
  turnOn(): void;
11
11
  turnOff(): void;
12
12
  isOn(): boolean;
@@ -1,6 +1,6 @@
1
1
  <%= render(Primer::BaseComponent.new(tag: "toggle-switch", **@system_arguments)) do %>
2
2
  <span class="ToggleSwitch-statusIcon">
3
- <%= render(Primer::Beta::Octicon.new(size: :small, color: :danger, icon: :alert, hidden: "true", data: { target: "toggle-switch.errorIcon" })) %>
3
+ <%= render(Primer::Beta::Octicon.new(size: :small, color: :danger, icon: :"alert-fill", hidden: "true", data: { target: "toggle-switch.errorIcon" })) %>
4
4
  <%= render(Primer::Beta::Spinner.new(size: :small, hidden: "true", data: { target: "toggle-switch.loadingSpinner" })) %>
5
5
  </span>
6
6
  <%= render(Primer::Beta::Text.new(aria: { hidden: true }, classes: "ToggleSwitch-status")) do %>
@@ -8,7 +8,7 @@
8
8
  <%= render(Primer::Box.new(classes: "ToggleSwitch-statusOff").with_content("Off")) %>
9
9
  <% end %>
10
10
 
11
- <%= render(Primer::BaseComponent.new(tag: :button, classes: "ToggleSwitch-track", role: "switch", data: { target: "toggle-switch.switch", action: "click:toggle-switch#toggle" }, aria: { checked: on?, disabled: disabled?, label: "Switch" })) do %>
11
+ <%= render(Primer::BaseComponent.new(tag: :button, classes: "ToggleSwitch-track", disabled: disabled?, data: { target: "toggle-switch.switch", action: "click:toggle-switch#toggle" }, **@aria_arguments)) do %>
12
12
  <%= render(Primer::Box.new(classes: "ToggleSwitch-icons", aria: { hidden: true })) do %>
13
13
  <%= render(Primer::Box.new(classes: "ToggleSwitch-lineIcon")) do %>
14
14
  <%= render(Primer::BaseComponent.new(
@@ -26,46 +26,59 @@ let ToggleSwitchElement = class ToggleSwitchElement extends HTMLElement {
26
26
  isRemote() {
27
27
  return this.src != null;
28
28
  }
29
- toggle() {
29
+ async toggle() {
30
30
  if (this.isDisabled()) {
31
31
  return;
32
32
  }
33
- if (this.isRemote()) {
34
- this.setLoadingState();
35
- this.submitForm();
36
- }
37
- else {
33
+ if (!this.isRemote()) {
38
34
  this.performToggle();
35
+ return;
36
+ }
37
+ // toggle immediately to tell screen readers the switch was clicked
38
+ this.performToggle();
39
+ this.setLoadingState();
40
+ try {
41
+ await this.submitForm();
42
+ }
43
+ catch (error) {
44
+ if (error instanceof Error) {
45
+ // because we toggle immediately when the switch is clicked, toggle back to the
46
+ // old state on failure
47
+ this.setErrorState(error.message || 'An error occurred, please try again.');
48
+ this.performToggle();
49
+ }
50
+ return;
39
51
  }
52
+ this.setSuccessState();
40
53
  }
41
54
  turnOn() {
42
55
  if (this.isDisabled()) {
43
56
  return;
44
57
  }
45
- this.switch.setAttribute('aria-checked', 'true');
58
+ this.switch.setAttribute('aria-pressed', 'true');
46
59
  this.classList.add('ToggleSwitch--checked');
47
60
  }
48
61
  turnOff() {
49
62
  if (this.isDisabled()) {
50
63
  return;
51
64
  }
52
- this.switch.setAttribute('aria-checked', 'false');
65
+ this.switch.setAttribute('aria-pressed', 'false');
53
66
  this.classList.remove('ToggleSwitch--checked');
54
67
  }
55
68
  isOn() {
56
- return this.switch.getAttribute('aria-checked') === 'true';
69
+ return this.switch.getAttribute('aria-pressed') === 'true';
57
70
  }
58
71
  isOff() {
59
72
  return !this.isOn();
60
73
  }
61
74
  isDisabled() {
62
- return this.switch.getAttribute('aria-disabled') === 'true';
75
+ return this.switch.getAttribute('disabled') != null;
63
76
  }
64
77
  disable() {
65
- this.switch.setAttribute('aria-disabled', 'true');
78
+ this.switch.setAttribute('disabled', 'disabled');
66
79
  }
67
80
  enable() {
68
- this.switch.setAttribute('aria-disabled', 'false');
81
+ this.switch.removeAttribute('disabled');
69
82
  }
70
83
  performToggle() {
71
84
  if (this.isOn()) {
@@ -76,9 +89,10 @@ let ToggleSwitchElement = class ToggleSwitchElement extends HTMLElement {
76
89
  }
77
90
  }
78
91
  setLoadingState() {
79
- this.disable();
80
92
  this.errorIcon.setAttribute('hidden', 'hidden');
81
93
  this.loadingSpinner.removeAttribute('hidden');
94
+ const event = new CustomEvent('toggleSwitchLoading', { bubbles: true });
95
+ this.dispatchEvent(event);
82
96
  }
83
97
  setSuccessState() {
84
98
  const event = new CustomEvent('toggleSwitchSuccess', { bubbles: true });
@@ -95,43 +109,31 @@ let ToggleSwitchElement = class ToggleSwitchElement extends HTMLElement {
95
109
  this.errorIcon.removeAttribute('hidden');
96
110
  }
97
111
  this.loadingSpinner.setAttribute('hidden', 'hidden');
98
- this.enable();
99
112
  }
100
113
  async submitForm() {
101
114
  const body = new FormData();
102
115
  if (this.csrf) {
103
116
  body.append(this.csrfField, this.csrf);
104
117
  }
105
- body.append('value', this.isOn() ? '0' : '1');
118
+ body.append('value', this.isOn() ? '1' : '0');
119
+ if (!this.src)
120
+ throw new Error('invalid src');
121
+ let response;
106
122
  try {
107
- if (!this.src)
108
- throw new Error('invalid src');
109
- let response;
110
- try {
111
- response = await fetch(this.src, {
112
- credentials: 'same-origin',
113
- method: 'POST',
114
- headers: {
115
- 'Requested-With': 'XMLHttpRequest'
116
- },
117
- body
118
- });
119
- }
120
- catch (error) {
121
- throw new Error('A network error occurred, please try again.');
122
- }
123
- if (response.ok) {
124
- this.setSuccessState();
125
- this.performToggle();
126
- }
127
- else {
128
- throw new Error(await response.text());
129
- }
123
+ response = await fetch(this.src, {
124
+ credentials: 'same-origin',
125
+ method: 'POST',
126
+ headers: {
127
+ 'Requested-With': 'XMLHttpRequest'
128
+ },
129
+ body
130
+ });
130
131
  }
131
132
  catch (error) {
132
- if (error instanceof Error) {
133
- this.setErrorState(error.message || 'An error occurred, please try again.');
134
- }
133
+ throw new Error('A network error occurred, please try again.');
134
+ }
135
+ if (!response.ok) {
136
+ throw new Error(await response.text());
135
137
  }
136
138
  }
137
139
  };
@@ -146,7 +148,7 @@ __decorate([
146
148
  ], ToggleSwitchElement.prototype, "errorIcon", void 0);
147
149
  __decorate([
148
150
  debounce(300)
149
- ], ToggleSwitchElement.prototype, "submitForm", null);
151
+ ], ToggleSwitchElement.prototype, "toggle", null);
150
152
  ToggleSwitchElement = __decorate([
151
153
  controller
152
154
  ], ToggleSwitchElement);
@@ -70,17 +70,17 @@
70
70
  }
71
71
  }
72
72
 
73
- .ToggleSwitch-track[aria-checked='true'][aria-disabled='true'] {
73
+ .ToggleSwitch-track[aria-pressed='true'][disabled] {
74
74
  background-color: var(--color-switch-track-disabled-bg);
75
75
  color: var(--color-switch-track-checked-disabled-fg);
76
76
  border-color: transparent;
77
77
  }
78
78
 
79
- .ToggleSwitch-track[aria-checked='true'] {
79
+ .ToggleSwitch-track[aria-pressed='true'] {
80
80
  background-color: var(--color-switch-track-checked-bg);
81
81
  border-color: var(--color-switch-track-checked-border);
82
82
 
83
- &:not([aria-disabled='true']) {
83
+ &:not([disabled]) {
84
84
  &:hover {
85
85
  background-color: var(--color-switch-track-checked-hover-bg);
86
86
  }
@@ -105,7 +105,7 @@
105
105
  }
106
106
  }
107
107
 
108
- .ToggleSwitch-track[aria-disabled='true'] {
108
+ .ToggleSwitch-track[disabled] {
109
109
  cursor: not-allowed;
110
110
  background-color: var(--color-switch-track-disabled-bg);
111
111
  border-color: transparent;
@@ -66,6 +66,13 @@ module Primer
66
66
  SIZE_MAPPINGS[@size]
67
67
  )
68
68
 
69
+ @aria_arguments = {
70
+ aria: merge_aria(
71
+ @system_arguments,
72
+ aria: { pressed: on? }
73
+ )
74
+ }
75
+
69
76
  @system_arguments[:src] = @src if @src
70
77
 
71
78
  return unless @src && @csrf_token
@@ -31,17 +31,35 @@ class ToggleSwitchElement extends HTMLElement {
31
31
  return this.src != null
32
32
  }
33
33
 
34
- toggle() {
34
+ @debounce(300)
35
+ async toggle() {
35
36
  if (this.isDisabled()) {
36
37
  return
37
38
  }
38
39
 
39
- if (this.isRemote()) {
40
- this.setLoadingState()
41
- this.submitForm()
42
- } else {
40
+ if (!this.isRemote()) {
43
41
  this.performToggle()
42
+ return
44
43
  }
44
+
45
+ // toggle immediately to tell screen readers the switch was clicked
46
+ this.performToggle()
47
+ this.setLoadingState()
48
+
49
+ try {
50
+ await this.submitForm()
51
+ } catch (error) {
52
+ if (error instanceof Error) {
53
+ // because we toggle immediately when the switch is clicked, toggle back to the
54
+ // old state on failure
55
+ this.setErrorState(error.message || 'An error occurred, please try again.')
56
+ this.performToggle()
57
+ }
58
+
59
+ return
60
+ }
61
+
62
+ this.setSuccessState()
45
63
  }
46
64
 
47
65
  turnOn(): void {
@@ -49,7 +67,7 @@ class ToggleSwitchElement extends HTMLElement {
49
67
  return
50
68
  }
51
69
 
52
- this.switch.setAttribute('aria-checked', 'true')
70
+ this.switch.setAttribute('aria-pressed', 'true')
53
71
  this.classList.add('ToggleSwitch--checked')
54
72
  }
55
73
 
@@ -58,12 +76,12 @@ class ToggleSwitchElement extends HTMLElement {
58
76
  return
59
77
  }
60
78
 
61
- this.switch.setAttribute('aria-checked', 'false')
79
+ this.switch.setAttribute('aria-pressed', 'false')
62
80
  this.classList.remove('ToggleSwitch--checked')
63
81
  }
64
82
 
65
83
  isOn(): boolean {
66
- return this.switch.getAttribute('aria-checked') === 'true'
84
+ return this.switch.getAttribute('aria-pressed') === 'true'
67
85
  }
68
86
 
69
87
  isOff(): boolean {
@@ -71,15 +89,15 @@ class ToggleSwitchElement extends HTMLElement {
71
89
  }
72
90
 
73
91
  isDisabled(): boolean {
74
- return this.switch.getAttribute('aria-disabled') === 'true'
92
+ return this.switch.getAttribute('disabled') != null
75
93
  }
76
94
 
77
95
  disable(): void {
78
- this.switch.setAttribute('aria-disabled', 'true')
96
+ this.switch.setAttribute('disabled', 'disabled')
79
97
  }
80
98
 
81
99
  enable(): void {
82
- this.switch.setAttribute('aria-disabled', 'false')
100
+ this.switch.removeAttribute('disabled')
83
101
  }
84
102
 
85
103
  private performToggle(): void {
@@ -91,9 +109,11 @@ class ToggleSwitchElement extends HTMLElement {
91
109
  }
92
110
 
93
111
  private setLoadingState(): void {
94
- this.disable()
95
112
  this.errorIcon.setAttribute('hidden', 'hidden')
96
113
  this.loadingSpinner.removeAttribute('hidden')
114
+
115
+ const event = new CustomEvent('toggleSwitchLoading', {bubbles: true})
116
+ this.dispatchEvent(event)
97
117
  }
98
118
 
99
119
  private setSuccessState(): void {
@@ -116,10 +136,8 @@ class ToggleSwitchElement extends HTMLElement {
116
136
  }
117
137
 
118
138
  this.loadingSpinner.setAttribute('hidden', 'hidden')
119
- this.enable()
120
139
  }
121
140
 
122
- @debounce(300)
123
141
  private async submitForm() {
124
142
  const body = new FormData()
125
143
 
@@ -127,36 +145,27 @@ class ToggleSwitchElement extends HTMLElement {
127
145
  body.append(this.csrfField, this.csrf)
128
146
  }
129
147
 
130
- body.append('value', this.isOn() ? '0' : '1')
148
+ body.append('value', this.isOn() ? '1' : '0')
131
149
 
132
- try {
133
- if (!this.src) throw new Error('invalid src')
134
-
135
- let response
136
-
137
- try {
138
- response = await fetch(this.src, {
139
- credentials: 'same-origin',
140
- method: 'POST',
141
- headers: {
142
- 'Requested-With': 'XMLHttpRequest'
143
- },
144
- body
145
- })
146
- } catch (error) {
147
- throw new Error('A network error occurred, please try again.')
148
- }
150
+ if (!this.src) throw new Error('invalid src')
149
151
 
150
- if (response.ok) {
151
- this.setSuccessState()
152
- this.performToggle()
153
- } else {
154
- throw new Error(await response.text())
155
- }
152
+ let response
153
+
154
+ try {
155
+ response = await fetch(this.src, {
156
+ credentials: 'same-origin',
157
+ method: 'POST',
158
+ headers: {
159
+ 'Requested-With': 'XMLHttpRequest'
160
+ },
161
+ body
162
+ })
156
163
  } catch (error) {
157
- if (error instanceof Error) {
158
- this.setErrorState(error.message || 'An error occurred, please try again.')
159
- }
164
+ throw new Error('A network error occurred, please try again.')
165
+ }
166
+
167
+ if (!response.ok) {
168
+ throw new Error(await response.text())
160
169
  }
161
170
  }
162
171
  }
@@ -41,7 +41,7 @@ module Primer
41
41
  system_arguments[:tag] = :ul
42
42
  system_arguments[:id] = @list_id
43
43
  system_arguments[:classes] = class_names(
44
- "ActionListWrap",
44
+ "ActionListWrap ActionListWrap--inset",
45
45
  system_arguments[:classes]
46
46
  )
47
47