switch.js 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. export default function (Alpine) {
  2. Alpine.directive('switch', (el, directive) => {
  3. if (directive.value === 'group') handleGroup(el, Alpine)
  4. else if (directive.value === 'label') handleLabel(el, Alpine)
  5. else if (directive.value === 'description') handleDescription(el, Alpine)
  6. else handleSwitch(el, Alpine)
  7. })
  8. Alpine.magic('switch', el => {
  9. let $data = Alpine.$data(el)
  10. return {
  11. get isChecked() {
  12. return $data.__value === true
  13. },
  14. }
  15. })
  16. }
  17. function handleGroup(el, Alpine) {
  18. Alpine.bind(el, {
  19. 'x-id'() { return ['alpine-switch-label', 'alpine-switch-description'] },
  20. 'x-data'() {
  21. return {
  22. __hasLabel: false,
  23. __hasDescription: false,
  24. __switchEl: undefined,
  25. }
  26. }
  27. })
  28. }
  29. function handleLabel(el, Alpine) {
  30. Alpine.bind(el, {
  31. 'x-init'() { this.$data.__hasLabel = true },
  32. ':id'() { return this.$id('alpine-switch-label') },
  33. '@click'() {
  34. this.$data.__switchEl.click()
  35. this.$data.__switchEl.focus({ preventScroll: true })
  36. },
  37. })
  38. }
  39. function handleDescription(el, Alpine) {
  40. Alpine.bind(el, {
  41. 'x-init'() { this.$data.__hasDescription = true },
  42. ':id'() { return this.$id('alpine-switch-description') },
  43. })
  44. }
  45. function handleSwitch(el, Alpine) {
  46. Alpine.bind(el, (options = {}) => ({
  47. 'x-data'() {
  48. return {
  49. init() {
  50. // Need the "microtask" here so that x-model has a chance to initialize.
  51. queueMicrotask(() => {
  52. // Set our internal "selected" every time the x-modeled value changes.
  53. Alpine.effect(() => {
  54. this.__value = this.$el._x_model.get()
  55. })
  56. })
  57. },
  58. __value: undefined,
  59. __toggle() {
  60. this.$el._x_model.set(!this.__value)
  61. },
  62. }
  63. },
  64. 'x-init'() {
  65. if (this.$el.tagName.toLowerCase() === 'button' && !this.$el.hasAttribute('type')) this.$el.type = 'button'
  66. this.$data.__switchEl = this.$el
  67. },
  68. 'role': 'switch',
  69. 'tabindex': "0",
  70. ':aria-checked'() { return !!this.__value },
  71. ':aria-labelledby'() { return this.$data.__hasLabel && this.$id('alpine-switch-label') },
  72. ':aria-describedby'() { return this.$data.__hasDescription && this.$id('alpine-switch-description') },
  73. '@click.prevent'() { this.__toggle() },
  74. '@keyup'(e) {
  75. if (e.key !== 'Tab') e.preventDefault()
  76. if (e.key === ' ') this.__toggle()
  77. },
  78. // This is needed so that we can "cancel" the click event when we use the `Enter` key on a button.
  79. '@keypress.prevent'() { },
  80. }))
  81. }