shadcn_phlexcomponents 0.1.11 → 0.1.14

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/app/javascript/controllers/accordion_controller.ts +65 -62
  3. data/app/javascript/controllers/alert_dialog_controller.ts +12 -0
  4. data/app/javascript/controllers/avatar_controller.ts +7 -2
  5. data/app/javascript/controllers/checkbox_controller.ts +11 -4
  6. data/app/javascript/controllers/collapsible_controller.ts +12 -5
  7. data/app/javascript/controllers/combobox_controller.ts +270 -39
  8. data/app/javascript/controllers/command_controller.ts +223 -51
  9. data/app/javascript/controllers/date_picker_controller.ts +185 -125
  10. data/app/javascript/controllers/date_range_picker_controller.ts +89 -79
  11. data/app/javascript/controllers/dialog_controller.ts +59 -57
  12. data/app/javascript/controllers/dropdown_menu_controller.ts +212 -36
  13. data/app/javascript/controllers/dropdown_menu_sub_controller.ts +31 -29
  14. data/app/javascript/controllers/form_field_controller.ts +6 -1
  15. data/app/javascript/controllers/hover_card_controller.ts +36 -26
  16. data/app/javascript/controllers/loading_button_controller.ts +6 -1
  17. data/app/javascript/controllers/popover_controller.ts +42 -65
  18. data/app/javascript/controllers/progress_controller.ts +9 -3
  19. data/app/javascript/controllers/radio_group_controller.ts +16 -9
  20. data/app/javascript/controllers/select_controller.ts +206 -65
  21. data/app/javascript/controllers/slider_controller.ts +23 -16
  22. data/app/javascript/controllers/switch_controller.ts +11 -4
  23. data/app/javascript/controllers/tabs_controller.ts +26 -18
  24. data/app/javascript/controllers/theme_switcher_controller.ts +6 -1
  25. data/app/javascript/controllers/toast_container_controller.ts +6 -1
  26. data/app/javascript/controllers/toast_controller.ts +7 -1
  27. data/app/javascript/controllers/toggle_controller.ts +28 -0
  28. data/app/javascript/controllers/toggle_group_controller.ts +28 -0
  29. data/app/javascript/controllers/tooltip_controller.ts +43 -31
  30. data/app/javascript/shadcn_phlexcomponents.ts +29 -25
  31. data/app/javascript/utils/command.ts +544 -0
  32. data/app/javascript/utils/floating_ui.ts +196 -0
  33. data/app/javascript/utils/index.ts +417 -0
  34. data/app/stylesheets/date_picker.css +118 -0
  35. data/lib/shadcn_phlexcomponents/alias.rb +3 -0
  36. data/lib/shadcn_phlexcomponents/components/accordion.rb +2 -1
  37. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +18 -15
  38. data/lib/shadcn_phlexcomponents/components/base.rb +14 -0
  39. data/lib/shadcn_phlexcomponents/components/collapsible.rb +1 -2
  40. data/lib/shadcn_phlexcomponents/components/combobox.rb +87 -57
  41. data/lib/shadcn_phlexcomponents/components/command.rb +77 -47
  42. data/lib/shadcn_phlexcomponents/components/date_picker.rb +25 -81
  43. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +21 -4
  44. data/lib/shadcn_phlexcomponents/components/dialog.rb +14 -12
  45. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +5 -4
  46. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +2 -1
  47. data/lib/shadcn_phlexcomponents/components/form/form_combobox.rb +64 -0
  48. data/lib/shadcn_phlexcomponents/components/form.rb +14 -0
  49. data/lib/shadcn_phlexcomponents/components/hover_card.rb +3 -2
  50. data/lib/shadcn_phlexcomponents/components/popover.rb +3 -3
  51. data/lib/shadcn_phlexcomponents/components/select.rb +10 -25
  52. data/lib/shadcn_phlexcomponents/components/sheet.rb +15 -11
  53. data/lib/shadcn_phlexcomponents/components/table.rb +1 -1
  54. data/lib/shadcn_phlexcomponents/components/tabs.rb +1 -1
  55. data/lib/shadcn_phlexcomponents/components/toast_container.rb +1 -1
  56. data/lib/shadcn_phlexcomponents/components/toggle.rb +54 -0
  57. data/lib/shadcn_phlexcomponents/components/tooltip.rb +3 -2
  58. data/lib/shadcn_phlexcomponents/engine.rb +1 -5
  59. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  60. metadata +9 -4
  61. data/app/javascript/controllers/command_root_controller.ts +0 -355
  62. data/app/javascript/controllers/dropdown_menu_root_controller.ts +0 -234
  63. data/app/javascript/utils.ts +0 -437
@@ -1,20 +1,25 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
  import { useHover } from 'stimulus-use'
3
- import { initFloatingUi, showContent, hideContent } from '../utils'
3
+ import { initFloatingUi } from '../utils/floating_ui'
4
+ import { showContent, hideContent } from '../utils'
4
5
 
5
- export default class extends Controller<HTMLElement> {
6
+ const HoverCardController = class extends Controller<HTMLElement> {
7
+ // targets
6
8
  static targets = ['trigger', 'content', 'contentContainer']
9
+ declare readonly triggerTarget: HTMLElement
10
+ declare readonly contentTarget: HTMLElement
11
+ declare readonly contentContainerTarget: HTMLElement
12
+
13
+ // values
7
14
  static values = {
8
15
  isOpen: Boolean,
9
16
  }
10
-
11
17
  declare isOpenValue: boolean
12
- declare readonly triggerTarget: HTMLElement
13
- declare readonly contentTarget: HTMLElement
14
- declare readonly contentContainerTarget: HTMLElement
15
- declare cleanup: () => void
18
+
19
+ // custom properties
16
20
  declare closeTimeout: number
17
21
  declare DOMKeydownListener: (event: KeyboardEvent) => void
22
+ declare cleanup: () => void
18
23
 
19
24
  connect() {
20
25
  this.DOMKeydownListener = this.onDOMKeydown.bind(this)
@@ -42,25 +47,6 @@ export default class extends Controller<HTMLElement> {
42
47
  this.close()
43
48
  }
44
49
 
45
- setupEventListeners() {
46
- document.addEventListener('keydown', this.DOMKeydownListener)
47
- }
48
-
49
- cleanupEventListeners() {
50
- if (this.cleanup) this.cleanup()
51
- document.removeEventListener('keydown', this.DOMKeydownListener)
52
- }
53
-
54
- onDOMKeydown(event: KeyboardEvent) {
55
- if (!this.isOpenValue) return
56
-
57
- const key = event.key
58
-
59
- if (key === 'Escape') {
60
- this.close()
61
- }
62
- }
63
-
64
50
  isOpenValueChanged(isOpen: boolean) {
65
51
  if (isOpen) {
66
52
  showContent({
@@ -90,4 +76,28 @@ export default class extends Controller<HTMLElement> {
90
76
  disconnect() {
91
77
  this.cleanupEventListeners()
92
78
  }
79
+
80
+ protected setupEventListeners() {
81
+ document.addEventListener('keydown', this.DOMKeydownListener)
82
+ }
83
+
84
+ protected cleanupEventListeners() {
85
+ if (this.cleanup) this.cleanup()
86
+ document.removeEventListener('keydown', this.DOMKeydownListener)
87
+ }
88
+
89
+ protected onDOMKeydown(event: KeyboardEvent) {
90
+ if (!this.isOpenValue) return
91
+
92
+ const key = event.key
93
+
94
+ if (key === 'Escape') {
95
+ this.close()
96
+ }
97
+ }
93
98
  }
99
+
100
+ type HoverCard = InstanceType<typeof HoverCardController>
101
+
102
+ export { HoverCardController }
103
+ export type { HoverCard }
@@ -1,6 +1,6 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
- export default class extends Controller<HTMLButtonElement> {
3
+ const LoadingButtonController = class extends Controller<HTMLButtonElement> {
4
4
  connect() {
5
5
  const el = this.element
6
6
  const form = el.closest('form')
@@ -13,3 +13,8 @@ export default class extends Controller<HTMLButtonElement> {
13
13
  }
14
14
  }
15
15
  }
16
+
17
+ type LoadingButton = InstanceType<typeof LoadingButtonController>
18
+
19
+ export { LoadingButtonController }
20
+ export type { LoadingButton }
@@ -1,22 +1,28 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
  import { useClickOutside } from 'stimulus-use'
3
+ import { initFloatingUi } from '../utils/floating_ui'
3
4
  import {
4
- initFloatingUi,
5
5
  focusTrigger,
6
- ON_OPEN_FOCUS_DELAY,
7
6
  getFocusableElements,
8
7
  showContent,
9
8
  hideContent,
9
+ onClickOutside,
10
+ handleTabNavigation,
11
+ focusElement,
10
12
  } from '../utils'
11
13
 
12
- export default class extends Controller<HTMLElement> {
14
+ const PopoverController = class extends Controller<HTMLElement> {
15
+ // targets
13
16
  static targets = ['trigger', 'contentContainer', 'content']
14
- static values = { isOpen: Boolean }
15
-
16
- declare isOpenValue: boolean
17
17
  declare readonly triggerTarget: HTMLElement
18
18
  declare readonly contentContainerTarget: HTMLElement
19
19
  declare readonly contentTarget: HTMLElement
20
+
21
+ // values
22
+ static values = { isOpen: Boolean }
23
+ declare isOpenValue: boolean
24
+
25
+ // custom properties
20
26
  declare DOMKeydownListener: (event: KeyboardEvent) => void
21
27
  declare cleanup: () => void
22
28
 
@@ -35,67 +41,18 @@ export default class extends Controller<HTMLElement> {
35
41
 
36
42
  open() {
37
43
  this.isOpenValue = true
38
- this.onOpen()
39
-
40
- setTimeout(() => {
41
- this.onOpenFocusedElement().focus()
42
- }, ON_OPEN_FOCUS_DELAY)
43
44
  }
44
45
 
45
46
  close() {
46
47
  this.isOpenValue = false
47
- this.onClose()
48
- }
49
-
50
- onDOMKeydown(event: KeyboardEvent) {
51
- if (!this.isOpenValue) return
52
-
53
- const key = event.key
54
-
55
- if (key === 'Escape') {
56
- this.close()
57
- } else if (key === 'Tab') {
58
- const focusableElements = getFocusableElements(this.contentTarget)
59
-
60
- const firstElement = focusableElements[0]
61
- const lastElement = focusableElements[focusableElements.length - 1]
62
-
63
- // If Shift + Tab pressed on first element, go to last element
64
- if (event.shiftKey && document.activeElement === firstElement) {
65
- event.preventDefault()
66
- lastElement.focus()
67
- }
68
- // If Tab pressed on last element, go to first element
69
- else if (!event.shiftKey && document.activeElement === lastElement) {
70
- event.preventDefault()
71
- firstElement.focus()
72
- }
73
- }
74
48
  }
75
49
 
76
50
  clickOutside(event: MouseEvent) {
77
- const target = event.target
78
- // Let #toggle to handle state when clicked on trigger
79
- if (target === this.triggerTarget) return
80
-
81
- this.close()
82
- }
83
-
84
- onOpen() {}
85
-
86
- onOpenFocusedElement() {
87
- const focusableElements = getFocusableElements(this.contentTarget)
88
- return focusableElements[0]
89
- }
90
-
91
- onClose() {}
92
-
93
- referenceElement() {
94
- return this.triggerTarget
51
+ onClickOutside(this, event)
95
52
  }
96
53
 
97
54
  isOpenValueChanged(isOpen: boolean, previousIsOpen: boolean) {
98
- if (isOpen === true) {
55
+ if (isOpen) {
99
56
  showContent({
100
57
  trigger: this.triggerTarget,
101
58
  content: this.contentTarget,
@@ -103,12 +60,16 @@ export default class extends Controller<HTMLElement> {
103
60
  })
104
61
 
105
62
  this.cleanup = initFloatingUi({
106
- referenceElement: this.referenceElement(),
63
+ referenceElement: this.triggerTarget,
107
64
  floatingElement: this.contentContainerTarget,
108
65
  side: this.contentTarget.dataset.side,
109
66
  align: this.contentTarget.dataset.align,
110
67
  sideOffset: 4,
111
68
  })
69
+
70
+ const focusableElements = getFocusableElements(this.contentTarget)
71
+ focusElement(focusableElements[0])
72
+
112
73
  this.setupEventListeners()
113
74
  } else {
114
75
  hideContent({
@@ -117,25 +78,41 @@ export default class extends Controller<HTMLElement> {
117
78
  contentContainer: this.contentContainerTarget,
118
79
  })
119
80
 
120
- this.cleanupEventListeners()
121
-
122
- // Only focus trigger when is previously opened
123
81
  if (previousIsOpen) {
124
82
  focusTrigger(this.triggerTarget)
125
83
  }
84
+
85
+ this.cleanupEventListeners()
126
86
  }
127
87
  }
128
88
 
129
- setupEventListeners() {
89
+ disconnect() {
90
+ this.cleanupEventListeners()
91
+ }
92
+
93
+ protected setupEventListeners() {
130
94
  document.addEventListener('keydown', this.DOMKeydownListener)
131
95
  }
132
96
 
133
- cleanupEventListeners() {
97
+ protected cleanupEventListeners() {
134
98
  if (this.cleanup) this.cleanup()
135
99
  document.removeEventListener('keydown', this.DOMKeydownListener)
136
100
  }
137
101
 
138
- disconnect() {
139
- this.cleanupEventListeners()
102
+ protected onDOMKeydown(event: KeyboardEvent) {
103
+ if (!this.isOpenValue) return
104
+
105
+ const key = event.key
106
+
107
+ if (key === 'Escape') {
108
+ this.close()
109
+ } else if (key === 'Tab') {
110
+ handleTabNavigation(this.contentTarget, event)
111
+ }
140
112
  }
141
113
  }
114
+
115
+ type Popover = InstanceType<typeof PopoverController>
116
+
117
+ export { PopoverController }
118
+ export type { Popover }
@@ -1,13 +1,14 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
- export default class extends Controller {
3
+ const ProgressController = class extends Controller {
4
+ // targets
4
5
  static targets = ['indicator']
6
+ declare readonly indicatorTarget: HTMLElement
5
7
 
8
+ // values
6
9
  static values = {
7
10
  percent: Number,
8
11
  }
9
-
10
- declare readonly indicatorTarget: HTMLElement
11
12
  declare percentValue: number
12
13
 
13
14
  percentValueChanged(value: number) {
@@ -15,3 +16,8 @@ export default class extends Controller {
15
16
  this.indicatorTarget.style.transform = `translateX(-${100 - value}%)`
16
17
  }
17
18
  }
19
+
20
+ type Progress = InstanceType<typeof ProgressController>
21
+
22
+ export { ProgressController }
23
+ export type { Progress }
@@ -1,14 +1,16 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
- export default class extends Controller<HTMLElement> {
3
+ const RadioGroupController = class extends Controller<HTMLElement> {
4
+ // targets
4
5
  static targets = ['item', 'input', 'indicator']
5
- static values = {
6
- selected: String,
7
- }
8
-
9
6
  declare readonly itemTargets: HTMLInputElement[]
10
7
  declare readonly inputTargets: HTMLInputElement[]
11
8
  declare readonly indicatorTargets: HTMLInputElement[]
9
+
10
+ // values
11
+ static values = {
12
+ selected: String,
13
+ }
12
14
  declare selectedValue: string
13
15
 
14
16
  connect() {
@@ -22,10 +24,6 @@ export default class extends Controller<HTMLElement> {
22
24
  this.selectedValue = item.dataset.value as string
23
25
  }
24
26
 
25
- preventDefault(event: KeyboardEvent) {
26
- event.preventDefault()
27
- }
28
-
29
27
  selectItem(event: KeyboardEvent) {
30
28
  const focusableItems = this.itemTargets.filter(
31
29
  (t) => !t.disabled,
@@ -53,6 +51,10 @@ export default class extends Controller<HTMLElement> {
53
51
  this.selectedValue = focusableItems[newIndex].dataset.value as string
54
52
  }
55
53
 
54
+ preventDefault(event: KeyboardEvent) {
55
+ event.preventDefault()
56
+ }
57
+
56
58
  focusItem() {
57
59
  const item = this.itemTargets.find(
58
60
  (i) => i.dataset.value === this.selectedValue,
@@ -104,3 +106,8 @@ export default class extends Controller<HTMLElement> {
104
106
  this.focusItem()
105
107
  }
106
108
  }
109
+
110
+ type RadioGroup = InstanceType<typeof RadioGroupController>
111
+
112
+ export { RadioGroupController }
113
+ export type { RadioGroup }