phlex_ui 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/lib/phlex_ui/alert/description.rb +17 -0
  3. data/lib/phlex_ui/alert/title.rb +17 -0
  4. data/lib/phlex_ui/alert.rb +36 -0
  5. data/lib/phlex_ui/alert_dialog/action.rb +17 -0
  6. data/lib/phlex_ui/alert_dialog/cancel.rb +21 -0
  7. data/lib/phlex_ui/alert_dialog/content.rb +45 -0
  8. data/lib/phlex_ui/alert_dialog/description.rb +17 -0
  9. data/lib/phlex_ui/alert_dialog/footer.rb +17 -0
  10. data/lib/phlex_ui/alert_dialog/header.rb +17 -0
  11. data/lib/phlex_ui/alert_dialog/title.rb +17 -0
  12. data/lib/phlex_ui/alert_dialog/trigger.rb +18 -0
  13. data/lib/phlex_ui/alert_dialog.rb +26 -0
  14. data/lib/phlex_ui/aspect_ratio.rb +33 -0
  15. data/lib/phlex_ui/avatar/fallback.rb +17 -0
  16. data/lib/phlex_ui/avatar/image.rb +26 -0
  17. data/lib/phlex_ui/avatar.rb +49 -0
  18. data/lib/phlex_ui/badge.rb +46 -62
  19. data/lib/phlex_ui/button.rb +86 -63
  20. data/lib/phlex_ui/card/content.rb +17 -0
  21. data/lib/phlex_ui/card/description.rb +17 -0
  22. data/lib/phlex_ui/card/footer.rb +17 -0
  23. data/lib/phlex_ui/card/header.rb +17 -0
  24. data/lib/phlex_ui/card/title.rb +17 -0
  25. data/lib/phlex_ui/card.rb +17 -0
  26. data/lib/phlex_ui/checkbox.rb +18 -0
  27. data/lib/phlex_ui/clipboard/popover.rb +36 -0
  28. data/lib/phlex_ui/clipboard/source.rb +19 -0
  29. data/lib/phlex_ui/clipboard/trigger.rb +20 -0
  30. data/lib/phlex_ui/clipboard.rb +39 -0
  31. data/lib/phlex_ui/codeblock.rb +105 -0
  32. data/lib/phlex_ui/collapsible/content.rb +18 -0
  33. data/lib/phlex_ui/collapsible/trigger.rb +19 -0
  34. data/lib/phlex_ui/collapsible.rb +25 -0
  35. data/lib/phlex_ui/context_menu/content.rb +25 -0
  36. data/lib/phlex_ui/context_menu/item.rb +66 -0
  37. data/lib/phlex_ui/context_menu/label.rb +24 -0
  38. data/lib/phlex_ui/context_menu/separator.rb +19 -0
  39. data/lib/phlex_ui/context_menu/trigger.rb +20 -0
  40. data/lib/phlex_ui/context_menu.rb +26 -0
  41. data/lib/phlex_ui/dialog/content.rb +78 -0
  42. data/lib/phlex_ui/dialog/description.rb +17 -0
  43. data/lib/phlex_ui/dialog/footer.rb +17 -0
  44. data/lib/phlex_ui/dialog/header.rb +17 -0
  45. data/lib/phlex_ui/dialog/middle.rb +17 -0
  46. data/lib/phlex_ui/dialog/title.rb +17 -0
  47. data/lib/phlex_ui/dialog/trigger.rb +19 -0
  48. data/lib/phlex_ui/dialog.rb +25 -0
  49. data/lib/phlex_ui/dropdown_menu/content.rb +22 -0
  50. data/lib/phlex_ui/dropdown_menu/item.rb +28 -0
  51. data/lib/phlex_ui/dropdown_menu/label.rb +17 -0
  52. data/lib/phlex_ui/dropdown_menu/separator.rb +19 -0
  53. data/lib/phlex_ui/dropdown_menu/trigger.rb +17 -0
  54. data/lib/phlex_ui/dropdown_menu.rb +26 -0
  55. data/lib/phlex_ui/form/item.rb +17 -0
  56. data/lib/phlex_ui/form/spacer.rb +17 -0
  57. data/lib/phlex_ui/form.rb +34 -0
  58. data/lib/phlex_ui/hint.rb +17 -0
  59. data/lib/phlex_ui/hover_card/content.rb +22 -0
  60. data/lib/phlex_ui/hover_card/trigger.rb +19 -0
  61. data/lib/phlex_ui/hover_card.rb +27 -0
  62. data/lib/phlex_ui/input.rb +29 -0
  63. data/lib/phlex_ui/input_error.rb +17 -0
  64. data/lib/phlex_ui/label.rb +17 -0
  65. data/lib/phlex_ui/link.rb +97 -0
  66. data/lib/phlex_ui/popover/content.rb +22 -0
  67. data/lib/phlex_ui/popover/trigger.rb +19 -0
  68. data/lib/phlex_ui/popover.rb +25 -0
  69. data/lib/phlex_ui/shortcut_key.rb +17 -0
  70. data/lib/phlex_ui/table/body.rb +17 -0
  71. data/lib/phlex_ui/table/builder.rb +77 -0
  72. data/lib/phlex_ui/table/caption.rb +17 -0
  73. data/lib/phlex_ui/table/cell.rb +17 -0
  74. data/lib/phlex_ui/table/footer.rb +17 -0
  75. data/lib/phlex_ui/table/head.rb +17 -0
  76. data/lib/phlex_ui/table/header.rb +17 -0
  77. data/lib/phlex_ui/table/row.rb +17 -0
  78. data/lib/phlex_ui/table.rb +19 -0
  79. data/lib/phlex_ui/tabs/content.rb +26 -0
  80. data/lib/phlex_ui/tabs/list.rb +17 -0
  81. data/lib/phlex_ui/tabs/trigger.rb +28 -0
  82. data/lib/phlex_ui/tabs.rb +25 -0
  83. data/lib/phlex_ui/theme_toggle.rb +41 -0
  84. data/lib/phlex_ui/tooltip/content.rb +22 -0
  85. data/lib/phlex_ui/tooltip/trigger.rb +17 -0
  86. data/lib/phlex_ui/tooltip.rb +25 -0
  87. data/lib/phlex_ui/typography/blockquote.rb +17 -0
  88. data/lib/phlex_ui/typography/h1.rb +17 -0
  89. data/lib/phlex_ui/typography/h2.rb +17 -0
  90. data/lib/phlex_ui/typography/h3.rb +17 -0
  91. data/lib/phlex_ui/typography/h4.rb +17 -0
  92. data/lib/phlex_ui/typography/inline_code.rb +17 -0
  93. data/lib/phlex_ui/typography/large.rb +17 -0
  94. data/lib/phlex_ui/typography/lead.rb +17 -0
  95. data/lib/phlex_ui/typography/list.rb +47 -0
  96. data/lib/phlex_ui/typography/list_item.rb +17 -0
  97. data/lib/phlex_ui/typography/muted.rb +17 -0
  98. data/lib/phlex_ui/typography/p.rb +17 -0
  99. data/lib/phlex_ui/typography/small.rb +17 -0
  100. metadata +112 -2
@@ -1,73 +1,96 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PhlexUI
2
4
  class Button < Base
3
- def initialize(**kwargs)
4
- @variant = kwargs[:variant]&.to_sym || :primary
5
- @size = kwargs[:size]
6
- @kwargs = kwargs
7
- @kwargs[:type] ||= "button"
5
+ def initialize(variant: :primary, size: :md, icon: false, **attrs)
6
+ @variant = variant.to_sym
7
+ @size = size.to_sym
8
+ @icon = icon
9
+ super(**attrs)
8
10
  end
9
11
 
10
12
  def template(&)
13
+ button(**attrs, &)
14
+ end
15
+
16
+ private
17
+
18
+ def size_classes
19
+ if @icon
20
+ case @size
21
+ when :sm then "h-6 w-6"
22
+ when :md then "h-9 w-9"
23
+ when :lg then "h-10 w-10"
24
+ when :xl then "h-12 w-12"
25
+ end
26
+ else
27
+ case @size
28
+ when :sm then "px-3 py-1.5 h-8 text-xs"
29
+ when :md then "px-4 py-2 h-9 text-sm"
30
+ when :lg then "px-4 py-2 h-10 text-base"
31
+ when :xl then "px-6 py-3 h-12 text-base"
32
+ end
33
+ end
34
+ end
35
+
36
+ def primary_classes
37
+ tokens(
38
+ "whitespace-nowrap inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-text shadow hover:bg-primary/90",
39
+ size_classes
40
+ )
41
+ end
42
+
43
+ def link_classes
44
+ tokens(
45
+ "whitespace-nowrap inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 text-primary underline-offset-4 hover:underline",
46
+ size_classes
47
+ )
48
+ end
49
+
50
+ def secondary_classes
51
+ tokens(
52
+ "whitespace-nowrap inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-muted-background text-text shadow-sm hover:bg-opacity-80",
53
+ size_classes
54
+ )
55
+ end
56
+
57
+ def destructive_classes
58
+ tokens(
59
+ "whitespace-nowrap inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-destructive text-white shadow-sm hover:bg-destructive/90",
60
+ size_classes
61
+ )
62
+ end
63
+
64
+ def outline_classes
65
+ tokens(
66
+ "whitespace-nowrap inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 border border-input bg-background shadow-sm hover:bg-accent-background hover:text-accent-text",
67
+ size_classes
68
+ )
69
+ end
70
+
71
+ def ghost_classes
72
+ tokens(
73
+ "whitespace-nowrap inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 hover:bg-accent-background hover:text-accent-text",
74
+ size_classes
75
+ )
76
+ end
77
+
78
+ def default_classes
11
79
  case @variant
12
- when :primary
13
- button(
14
- **@kwargs,
15
- class: tokens(
16
- "whitespace-nowrap inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-primary-600 dark:bg-primary-500 text-white shadow hover:bg-primary-700 dark:hover:bg-primary-600 h-9",
17
- -> { @size == :icon } => "h-9 w-9 justify-center",
18
- -> { @size.nil? } => "px-4 py-2",
19
- -> { @kwargs[:class] } => @kwargs[:class]
20
- ),
21
- &)
22
- when :link
23
- button(
24
- **@kwargs,
25
- class: tokens(
26
- "whitespace-nowrap inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 text-primary-600 dark:text-primary-400 underline-offset-4 hover:underline h-9 px-4 py-2",
27
- -> { @kwargs[:class] } => @kwargs[:class]
28
- ),
29
- &)
30
- when :secondary
31
- button(
32
- **@kwargs,
33
- class: tokens(
34
- "whitespace-nowrap inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-primary-100 dark:bg-accent text-primary-600 dark:text-white shadow-sm hover:bg-primary-200 hover:text-primary-700 dark:hover:bg-opacity-10 h-9",
35
- -> { @size == :icon } => "h-9 w-9 justify-center",
36
- -> { @size.nil? } => "px-4 py-2",
37
- -> { @kwargs[:class] } => @kwargs[:class]
38
- ),
39
- &)
40
- when :destructive
41
- button(
42
- **@kwargs,
43
- class: tokens(
44
- "whitespace-nowrap inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-destructive-600 hover:bg-destructive-700 dark:bg-destructive-500 dark:hover:bg-destructive-600 text-white shadow-sm h-9",
45
- -> { @size == :icon } => "h-9 w-9 justify-center",
46
- -> { @size.nil? } => "px-4 py-2",
47
- -> { @kwargs[:class] } => @kwargs[:class]
48
- ),
49
- &)
50
- when :outline
51
- button(
52
- **@kwargs,
53
- class: tokens(
54
- "whitespace-nowrap inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 border bg-transparent shadow-sm hover:bg-accent h-9",
55
- -> { @size == :icon } => "h-9 w-9 justify-center",
56
- -> { @size.nil? } => "px-4 py-2",
57
- -> { @kwargs[:class] } => @kwargs[:class]
58
- ),
59
- &)
60
- when :ghost
61
- button(
62
- **@kwargs,
63
- class: tokens(
64
- "whitespace-nowrap inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 hover:bg-accent h-9",
65
- -> { @size == :icon } => "h-9 w-9 justify-center",
66
- -> { @size.nil? } => "px-4 py-2",
67
- -> { @kwargs[:class] } => @kwargs[:class]
68
- ),
69
- &)
80
+ when :primary then primary_classes
81
+ when :link then link_classes
82
+ when :secondary then secondary_classes
83
+ when :destructive then destructive_classes
84
+ when :outline then outline_classes
85
+ when :ghost then ghost_classes
70
86
  end
71
87
  end
88
+
89
+ def default_attrs
90
+ {
91
+ type: "button",
92
+ class: default_classes,
93
+ }
94
+ end
72
95
  end
73
96
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Card::Content < Base
5
+ def template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ class: "p-6 pt-0"
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Card::Description < Base
5
+ def template(&)
6
+ p(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ class: "text-sm text-muted-text"
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Card::Footer < Base
5
+ def template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ class: "items-center p-6 pt-0"
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Card::Header < Base
5
+ def template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ class: "flex flex-col space-y-1.5 p-6"
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Card::Title < Base
5
+ def template(&)
6
+ h3(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ class: "font-semibold leading-none tracking-tight"
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Card < Base
5
+ def template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ class: "rounded-xl border bg-background shadow"
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Checkbox < Base
5
+ def template
6
+ input(**attrs)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ type: "checkbox",
14
+ class: "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 accent-primary"
15
+ }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Clipboard::Popover < Base
5
+ def initialize(type:, **attrs)
6
+ @type = type
7
+ super(**attrs)
8
+ end
9
+
10
+ def template(&)
11
+ template_tag(data: { clipboard_target: clipboard_target }) do
12
+ div(**attrs, &)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def clipboard_target
19
+ case @type
20
+ when :success
21
+ "successPopover"
22
+ when :error
23
+ "errorPopover"
24
+ end
25
+ end
26
+
27
+ def default_attrs
28
+ {
29
+ data: {
30
+ state: :open
31
+ },
32
+ class: "z-50 rounded-md text-sm border bg-background px-2 py-0.5 text-text shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
33
+ }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Clipboard::Source < Base
5
+ def template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ data: {
14
+ clipboard_target: "source",
15
+ }
16
+ }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Clipboard::Trigger < Base
5
+ def template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ data: {
14
+ clipboard_target: "trigger",
15
+ action: "click->clipboard#copy",
16
+ }
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Clipboard < Base
5
+ def initialize(success: "Copied!", error: "Copy Failed!", **attrs)
6
+ @success = success
7
+ @error = error
8
+ super(**attrs)
9
+ end
10
+
11
+ def template(&)
12
+ div(**attrs) do
13
+ div(&)
14
+ success_popover
15
+ error_popover
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def success_popover
22
+ render Clipboard::Popover.new(type: :success) { @success }
23
+ end
24
+
25
+ def error_popover
26
+ render Clipboard::Popover.new(type: :error) { @error }
27
+ end
28
+
29
+ def default_attrs
30
+ {
31
+ data: {
32
+ controller: "clipboard",
33
+ clipboard_success_value: @success,
34
+ clipboard_error_value: @error,
35
+ }
36
+ }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rouge"
4
+
5
+ module PhlexUI
6
+ class Codeblock < Base
7
+ FORMATTER = ::Rouge::Formatters::HTML.new
8
+ ROUGE_CSS = Rouge::Themes::Github.mode(:dark).render(scope: '.highlight') # See themes here: https://rouge-ruby.github.io/docs/Rouge/CSSTheme.html
9
+
10
+ def initialize(code, syntax:, clipboard: true, clipboard_success: "Copied!", clipboard_error: "Copy failed!", **attrs)
11
+ @code = code
12
+ @syntax = syntax.to_sym
13
+ @clipboard = clipboard
14
+ @clipboard_success = clipboard_success
15
+ @clipboard_error = clipboard_error
16
+
17
+ if @syntax == :ruby || @syntax == :html
18
+ @code = @code.gsub(/(?:^|\G) {2}/m, " ")
19
+ end
20
+
21
+ super(**attrs)
22
+ end
23
+
24
+ def template
25
+ style { ROUGE_CSS.html_safe } # For faster load times, move this to the head of your document. (Also move ROUGE_CSS value to head of document)
26
+ if @clipboard
27
+ with_clipboard
28
+ else
29
+ codeblock
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def default_attrs
36
+ {
37
+ class: "highlight text-sm max-h-[350px] after:content-none flex font-mono overflow-auto overflow-x rounded-md border !bg-stone-900 [&_pre]:p-4"
38
+ }
39
+ end
40
+
41
+ def with_clipboard
42
+ render PhlexUI::Clipboard.new(success: @clipboard_success, error: @clipboard_error, class: 'relative') do
43
+ render PhlexUI::Clipboard::Source.new do
44
+ codeblock
45
+ end
46
+ div(class: "absolute top-2 right-2") do
47
+ render PhlexUI::Clipboard::Trigger.new do
48
+ render PhlexUI::Button.new(variant: :ghost, size: :sm, icon: true, class: 'text-white hover:text-white hover:bg-white/20') { clipboard_icon }
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def codeblock
55
+ div(**attrs) do
56
+ div(class: "after:content-none") do
57
+ pre do
58
+ unsafe_raw FORMATTER.format(
59
+ lexer.lex(@code)
60
+ )
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def lexer
67
+ Rouge::Lexer.find(@syntax)
68
+ end
69
+
70
+ def clipboard_icon
71
+ svg(
72
+ xmlns: "http://www.w3.org/2000/svg",
73
+ fill: "none",
74
+ viewbox: "0 0 24 24",
75
+ stroke_width: "1.5",
76
+ stroke: "currentColor",
77
+ class: "w-4 h-4"
78
+ ) do |s|
79
+ s.path(
80
+ stroke_linecap: "round",
81
+ stroke_linejoin: "round",
82
+ d:
83
+ "M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"
84
+ )
85
+ end
86
+ end
87
+
88
+ def check_icon
89
+ svg(
90
+ xmlns: "http://www.w3.org/2000/svg",
91
+ fill: "none",
92
+ viewbox: "0 0 24 24",
93
+ stroke_width: "1.5",
94
+ stroke: "currentColor",
95
+ class: "w-4 h-4"
96
+ ) do |s|
97
+ s.path(
98
+ stroke_linecap: "round",
99
+ stroke_linejoin: "round",
100
+ d: "M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
101
+ )
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Collapsible::Content < Base
5
+ def template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ data: { collapsible_target: "content" },
14
+ class: 'overflow-y-hidden'
15
+ }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Collapsible::Trigger < Base
5
+ def template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ data: {
14
+ action: "click->collapsible#toggle"
15
+ }
16
+ }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class Collapsible < Base
5
+ def initialize(open: false, **attrs)
6
+ @open = open
7
+ super(**attrs)
8
+ end
9
+
10
+ def template(&)
11
+ div(**attrs, &)
12
+ end
13
+
14
+ private
15
+
16
+ def default_attrs
17
+ {
18
+ data: {
19
+ controller: 'collapsible',
20
+ collapsible_open_value: @open
21
+ }
22
+ }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class ContextMenu::Content < Base
5
+ def template(&)
6
+ template_tag(data: { popover_target: "content" }) do
7
+ div(**attrs, &)
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def default_attrs
14
+ {
15
+ role: "menu",
16
+ aria_orientation: "vertical",
17
+ data_state: "open",
18
+ class:
19
+ "z-50 min-w-[8rem] outline-none pointer-events-auto overflow-hidden rounded-md border bg-background p-1 text-text shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
20
+ tabindex: "-1",
21
+ data_orientation: "vertical",
22
+ }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexUI
4
+ class ContextMenu::Item < Base
5
+ def initialize(href: "#", checked: false, shortcut: nil, disabled: false, **attrs)
6
+ @href = href
7
+ @checked = checked
8
+ @shortcut = shortcut
9
+ @disabled = disabled
10
+
11
+ super(**attrs)
12
+ end
13
+
14
+ def template(&)
15
+ a(**attrs) do
16
+ render_checkmark if @checked
17
+ yield
18
+ render_shortcut if @shortcut
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def render_checkmark
25
+ span(class: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center") do
26
+ span(data_state: "checked") do
27
+ svg(
28
+ width: "15",
29
+ height: "15",
30
+ viewbox: "0 0 15 15",
31
+ fill: "none",
32
+ xmlns: "http://www.w3.org/2000/svg",
33
+ class: "h-4 w-4"
34
+ ) do |s|
35
+ s.path(
36
+ d:
37
+ "M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z",
38
+ fill: "currentColor",
39
+ fill_rule: "evenodd",
40
+ clip_rule: "evenodd"
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def render_shortcut
48
+ span(class: "ml-auto text-xs tracking-widest text-muted-text") { @shortcut }
49
+ end
50
+
51
+ def default_attrs
52
+ {
53
+ href: @href,
54
+ role: "menuitem",
55
+ class:
56
+ "relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent-background hover:text-accent-text focus:bg-accent-background focus:text-accent-text aria-selected:bg-accent-background aria-selected:text-accent-text data-[disabled]:pointer-events-none data-[disabled]:opacity-50 pl-8",
57
+ tabindex: "-1",
58
+ data_orientation: "vertical",
59
+ data_action: "click->popover#close",
60
+ data_popover_target: "menuItem",
61
+ data_disabled: @disabled,
62
+ disabled: @disabled
63
+ }
64
+ end
65
+ end
66
+ end