phlexy_ui 0.1.1 → 0.1.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 649e11b247a2b1cb86b47caaba5c5fbf69210a6f563aaf05bdc86d97799faf8f
4
- data.tar.gz: 64c75e9f793b708663902157539672abf743407100d247093467d7f94473b735
3
+ metadata.gz: afd51b066ca04f9497e96ce00132d883007730221925fab4bfb3d6d5cb57aec2
4
+ data.tar.gz: fd72b9a2411ec3eef6f9f9b5ca7ad88a3cba8c639d6e79e9a4d3b1500b545788
5
5
  SHA512:
6
- metadata.gz: b1a9b52e5ca14871a3aea5d2f792dd8d549ca7a31cb4dd06bfbd90573998768db3ece9752d9a27af1b2d2fed2f0aea8c7ffd8efdec1373a306a74556a19b235f
7
- data.tar.gz: 3ab85f2c5f6d9a09c04d57bf978c43d82abb2ed3e4157e55b5de179c7b4a7b38c60ef1e619a491512a18590799d803ad36f40c279fbc3dd36fad89fe710482f5
6
+ metadata.gz: 93938b5e9e0365f144100e4d00ff757a33f50f7b7a264326ac0709506d5a4006fed131dda192d0546a3969ba777d27cfe59bf63a3f657f2cd456b2ae32ac7cf2
7
+ data.tar.gz: 3c9652fa3434d8deb8ddf5eb51c86a631d93057883630ea54b5b790a0ce8b8da3f414806b84c49fc31fe292dd2864b4c5a5d24c0a7e937712376768cec7c48a3
@@ -0,0 +1,28 @@
1
+ module PhlexyUI
2
+ class AttributeSet
3
+ def initialize(base_modifiers, attributes_map)
4
+ @base_modifiers = base_modifiers
5
+ @attributes_map = attributes_map
6
+ end
7
+
8
+ def to_h
9
+ attributes_modifiers.each_with_object({}) do |modifier, final_attributes_hash|
10
+ value = attributes_map[modifier]
11
+
12
+ if value.is_a?(Hash)
13
+ final_attributes_hash.merge!(value)
14
+ elsif value
15
+ final_attributes_hash[modifier] = value
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :base_modifiers, :attributes_map
23
+
24
+ def attributes_modifiers
25
+ base_modifiers.select { |modifier| attributes_map.key?(modifier) }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexyUI
4
+ class Avatar < Base
5
+ def initialize(*, as: :div, **)
6
+ super(*, **)
7
+ @as = as
8
+ end
9
+
10
+ def view_template(&)
11
+ generate_classes!(
12
+ component_html_class: :avatar,
13
+ modifiers_map: modifiers,
14
+ base_modifiers:,
15
+ options:
16
+ ).then do |classes|
17
+ public_send(as, class: classes, **options, &)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ register_modifiers(
24
+ # "sm:online"
25
+ # "md:online"
26
+ # "lg:online"
27
+ online: "online",
28
+ # "sm:offline"
29
+ # "md:offline"
30
+ # "lg:offline"
31
+ offline: "offline",
32
+ # "sm:placeholder"
33
+ # "md:placeholder"
34
+ # "lg:placeholder"
35
+ placeholder: "placeholder"
36
+ )
37
+ end
38
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexyUI
4
+ class Badge < Base
5
+ def initialize(*, as: :span, **)
6
+ super(*, **)
7
+ @as = as
8
+ end
9
+
10
+ def view_template(&)
11
+ generate_classes!(
12
+ component_html_class: :badge,
13
+ modifiers_map: modifiers,
14
+ base_modifiers:,
15
+ options:
16
+ ).then do |classes|
17
+ public_send(as, class: classes, **options, &)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ register_modifiers(
24
+ # "sm:badge-neutral"
25
+ # "md:badge-neutral"
26
+ # "lg:badge-neutral"
27
+ neutral: "badge-neutral",
28
+ # "sm:badge-primary"
29
+ # "md:badge-primary"
30
+ # "lg:badge-primary"
31
+ primary: "badge-primary",
32
+ # "sm:badge-secondary"
33
+ # "md:badge-secondary"
34
+ # "lg:badge-secondary"
35
+ secondary: "badge-secondary",
36
+ # "sm:badge-accent"
37
+ # "md:badge-accent"
38
+ # "lg:badge-accent"
39
+ accent: "badge-accent",
40
+ # "sm:badge-ghost"
41
+ # "md:badge-ghost"
42
+ # "lg:badge-ghost"
43
+ ghost: "badge-ghost",
44
+ # "sm:badge-info"
45
+ # "md:badge-info"
46
+ # "lg:badge-info"
47
+ info: "badge-info",
48
+ # "sm:badge-success"
49
+ # "md:badge-success"
50
+ # "lg:badge-success"
51
+ success: "badge-success",
52
+ # "sm:badge-warning"
53
+ # "md:badge-warning"
54
+ # "lg:badge-warning"
55
+ warning: "badge-warning",
56
+ # "sm:badge-error"
57
+ # "md:badge-error"
58
+ # "lg:badge-error"
59
+ error: "badge-error",
60
+ # "sm:badge-outline"
61
+ # "md:badge-outline"
62
+ # "lg:badge-outline"
63
+ outline: "badge-outline",
64
+ # "sm:badge-lg"
65
+ # "md:badge-lg"
66
+ # "lg:badge-lg"
67
+ lg: "badge-lg",
68
+ # "sm:badge-md"
69
+ # "md:badge-md"
70
+ # "lg:badge-md"
71
+ md: "badge-md",
72
+ # "sm:badge-sm"
73
+ # "md:badge-sm"
74
+ # "lg:badge-sm"
75
+ sm: "badge-sm",
76
+ # "sm:badge-xs"
77
+ # "md:badge-xs"
78
+ # "lg:badge-xs"
79
+ xs: "badge-xs"
80
+ )
81
+ end
82
+ end
@@ -2,47 +2,61 @@
2
2
 
3
3
  module PhlexyUI
4
4
  class Base < Phlex::HTML
5
- def initialize(*normal_conditions, sm: [], md: [], lg: [], **options)
6
- @normal_conditions = normal_conditions
7
- @sm_conditions = Array(sm)
8
- @md_conditions = Array(md)
9
- @lg_conditions = Array(lg)
5
+ def initialize(*base_modifiers, **options)
6
+ @base_modifiers = base_modifiers
10
7
  @options = options
11
- @data = options.delete(:data)
12
8
  end
13
9
 
14
- private
10
+ class << self
11
+ attr_reader :modifiers
12
+
13
+ private
15
14
 
16
- attr_reader :normal_conditions,
17
- :sm_conditions,
18
- :md_conditions,
19
- :lg_conditions,
20
- :options,
21
- :data,
22
- :as
23
-
24
- def classes
25
- [
26
- prefixed(self.class::BASE_HTML_CLASS),
27
- *html_classes_for_conditions(normal_conditions),
28
- *html_classes_for_conditions(sm_conditions, responsive_prefix: :sm),
29
- *html_classes_for_conditions(md_conditions, responsive_prefix: :md),
30
- *html_classes_for_conditions(lg_conditions, responsive_prefix: :lg)
31
- ]
15
+ def register_modifiers(modifiers)
16
+ @modifiers ||= {}
17
+ @modifiers.merge!(modifiers)
18
+ end
32
19
  end
33
20
 
34
- def html_classes_for_conditions(conditions, responsive_prefix: nil)
35
- conditions.map do |condition|
36
- class_name = prefixed self.class::CONDITIONS_CLASSES.fetch(condition)
21
+ private
22
+
23
+ attr_reader :base_modifiers, :options, :as, :id
37
24
 
38
- responsive_prefix ? "#{responsive_prefix}:#{class_name}" : class_name
25
+ def modifiers
26
+ {
27
+ **self.class.modifiers,
28
+ **PhlexyUI.configuration.modifiers.for(component: self.class),
29
+ **PhlexyUI.configuration.modifiers.for(component: nil)
30
+ }
31
+ end
32
+
33
+ def generate_classes!(
34
+ component_html_class: nil,
35
+ base_modifiers: [],
36
+ options: {},
37
+ modifiers_map: {}
38
+ )
39
+ ClassList.new(
40
+ component: self,
41
+ component_html_class:,
42
+ base_modifiers:,
43
+ options:,
44
+ modifiers_map:
45
+ ).to_a.then do |classes|
46
+ classes.any? ? classes : nil
39
47
  end
40
- rescue KeyError => e
41
- raise ArgumentError, "Condition `#{e.key}` is not defined for #{self.class}"
42
48
  end
43
49
 
44
- def prefixed(string)
45
- "#{PhlexyUI.configuration.prefix}#{string}"
50
+ def generate_attributes(base_modifiers, attributes_map)
51
+ AttributeSet.new(base_modifiers, attributes_map).to_h
52
+ end
53
+
54
+ def render_as(*, as:, **, &)
55
+ if as.is_a?(Symbol)
56
+ render public_send(*, as:, **, &)
57
+ else
58
+ render as.new(*, **, &)
59
+ end
46
60
  end
47
61
  end
48
62
  end
@@ -2,38 +2,119 @@
2
2
 
3
3
  module PhlexyUI
4
4
  class Button < Base
5
- CONDITIONS_CLASSES = {
5
+ def initialize(*, as: :button, **)
6
+ super(*, **)
7
+ @as = as
8
+ end
9
+
10
+ def view_template(&)
11
+ generate_classes!(
12
+ component_html_class: :btn,
13
+ modifiers_map: modifiers,
14
+ base_modifiers:,
15
+ options:
16
+ ).then do |classes|
17
+ public_send(as, class: classes, **options, &)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ register_modifiers(
6
24
  # Modifiers
25
+ # "sm:no-animation"
26
+ # "md:no-animation"
27
+ # "lg:no-animation"
7
28
  no_animation: "no-animation",
29
+ # "sm:glass"
30
+ # "md:glass"
31
+ # "lg:glass"
8
32
  glass: "glass",
33
+ # "sm:btn-ghost"
34
+ # "md:btn-ghost"
35
+ # "lg:btn-ghost"
9
36
  ghost: "btn-ghost",
37
+ # "sm:btn-link"
38
+ # "md:btn-link"
39
+ # "lg:btn-link"
10
40
  link: "btn-link",
41
+ # "sm:btn-outline"
42
+ # "md:btn-outline"
43
+ # "lg:btn-outline"
11
44
  outline: "btn-outline",
45
+ # "sm:btn-active"
46
+ # "md:btn-active"
47
+ # "lg:btn-active"
12
48
  active: "btn-active",
49
+ # "sm:btn-disabled"
50
+ # "md:btn-disabled"
51
+ # "lg:btn-disabled"
13
52
  disabled: "btn-disabled",
53
+ # "sm:btn-lg"
54
+ # "md:btn-lg"
55
+ # "lg:btn-lg"
14
56
  lg: "btn-lg",
57
+ # "sm:btn-md"
58
+ # "md:btn-md"
59
+ # "lg:btn-md"
15
60
  md: "btn-md",
61
+ # "sm:btn-sm"
62
+ # "md:btn-sm"
63
+ # "lg:btn-sm"
16
64
  sm: "btn-sm",
65
+ # "sm:btn-xs"
66
+ # "md:btn-xs"
67
+ # "lg:btn-xs"
17
68
  xs: "btn-xs",
69
+ # "sm:btn-wide"
70
+ # "md:btn-wide"
71
+ # "lg:btn-wide"
18
72
  wide: "btn-wide",
73
+ # "sm:btn-block"
74
+ # "md:btn-block"
75
+ # "lg:btn-block"
19
76
  block: "btn-block",
77
+ # "sm:btn-circle"
78
+ # "md:btn-circle"
79
+ # "lg:btn-circle"
20
80
  circle: "btn-circle",
81
+ # "sm:btn-square"
82
+ # "md:btn-square"
83
+ # "lg:btn-square"
21
84
  square: "btn-square",
22
85
  # Colors
86
+ # "sm:btn-neutral"
87
+ # "md:btn-neutral"
88
+ # "lg:btn-neutral"
23
89
  neutral: "btn-neutral",
90
+ # "sm:btn-primary"
91
+ # "md:btn-primary"
92
+ # "lg:btn-primary"
24
93
  primary: "btn-primary",
94
+ # "sm:btn-secondary"
95
+ # "md:btn-secondary"
96
+ # "lg:btn-secondary"
25
97
  secondary: "btn-secondary",
98
+ # "sm:btn-accent"
99
+ # "md:btn-accent"
100
+ # "lg:btn-accent"
26
101
  accent: "btn-accent",
102
+ # "sm:btn-info"
103
+ # "md:btn-info"
104
+ # "lg:btn-info"
27
105
  info: "btn-info",
106
+ # "sm:btn-success"
107
+ # "md:btn-success"
108
+ # "lg:btn-success"
28
109
  success: "btn-success",
110
+ # "sm:btn-warning"
111
+ # "md:btn-warning"
112
+ # "lg:btn-warning"
29
113
  warning: "btn-warning",
114
+ # "sm:btn-error"
115
+ # "md:btn-error"
116
+ # "lg:btn-error"
30
117
  error: "btn-error"
31
- }.freeze
32
-
33
- BASE_HTML_CLASS = "btn"
34
-
35
- def view_template(&)
36
- button(class: classes, data:, &)
37
- end
118
+ ).freeze
38
119
  end
39
120
  end
@@ -2,42 +2,118 @@
2
2
 
3
3
  module PhlexyUI
4
4
  class Card < Base
5
- CONDITIONS_CLASSES = {
6
- # Modifiers
7
- image_full: "image-full",
8
- bordered: "card-bordered",
9
- normal: "card-normal",
10
- compact: "card-compact",
11
- side: "card-side"
12
- }.freeze
13
-
14
- BASE_HTML_CLASS = "card"
15
-
16
5
  def initialize(*, as: :section, **)
17
6
  super(*, **)
18
7
  @as = as
19
8
  end
20
9
 
21
10
  def view_template(&)
22
- public_send(as, class: classes, data: data, &)
11
+ generate_classes!(
12
+ component_html_class: :card,
13
+ modifiers_map: modifiers,
14
+ base_modifiers:,
15
+ options:
16
+ ).then do |classes|
17
+ public_send(as, class: classes, **options, &)
18
+ end
23
19
  end
24
20
 
25
21
  def body(**options, &)
26
- classes = ["card-body", options.delete(:class)]
27
-
28
- div(class: classes, **options, &)
22
+ generate_classes!(
23
+ component_html_class: :"card-body",
24
+ options:
25
+ ).then do |classes|
26
+ div(class: classes, **options, &)
27
+ end
29
28
  end
30
29
 
31
30
  def title(**options, &)
32
- classes = ["card-title", options.delete(:class)]
33
-
34
- header(class: classes, **options, &)
31
+ generate_classes!(
32
+ component_html_class: :"card-title",
33
+ options:
34
+ ).then do |classes|
35
+ header(class: classes, **options, &)
36
+ end
35
37
  end
36
38
 
37
39
  def actions(**options, &)
38
- classes = ["card-actions", options.delete(:class)]
39
-
40
- footer(class: classes, **options, &)
40
+ generate_classes!(
41
+ component_html_class: :"card-actions",
42
+ options:
43
+ ).then do |classes|
44
+ footer(class: classes, **options, &)
45
+ end
41
46
  end
47
+
48
+ register_modifiers(
49
+ # "sm:image-full"
50
+ # "md:image-full"
51
+ # "lg:image-full"
52
+ image_full: "image-full",
53
+ # "sm:card-bordered"
54
+ # "md:card-bordered"
55
+ # "lg:card-bordered"
56
+ bordered: "card-bordered",
57
+ # "sm:card-normal"
58
+ # "md:card-normal"
59
+ # "lg:card-normal"
60
+ normal: "card-normal",
61
+ # "sm:card-compact"
62
+ # "md:card-compact"
63
+ # "lg:card-compact"
64
+ compact: "card-compact",
65
+ # "sm:card-side"
66
+ # "md:card-side"
67
+ # "lg:card-side"
68
+ side: "card-side",
69
+ # "sm:glass"
70
+ # "md:glass"
71
+ # "lg:glass"
72
+ glass: "glass",
73
+ # "sm:bg-primary sm:text-primary-content"
74
+ # "md:bg-primary md:text-primary-content"
75
+ # "lg:bg-primary lg:text-primary-content"
76
+ primary: "bg-primary text-primary-content",
77
+ # "sm:bg-secondary sm:text-secondary-content"
78
+ # "md:bg-secondary md:text-secondary-content"
79
+ # "lg:bg-secondary lg:text-secondary-content"
80
+ secondary: "bg-secondary text-secondary-content",
81
+ # "sm:bg-accent sm:text-accent-content"
82
+ # "md:bg-accent md:text-accent-content"
83
+ # "lg:bg-accent lg:text-accent-content"
84
+ accent: "bg-accent text-accent-content",
85
+ # "sm:bg-neutral sm:text-neutral-content"
86
+ # "md:bg-neutral md:text-neutral-content"
87
+ # "lg:bg-neutral lg:text-neutral-content"
88
+ neutral: "bg-neutral text-neutral-content",
89
+ # "sm:bg-base-100 sm:text-base-content"
90
+ # "md:bg-base-100 md:text-base-content"
91
+ # "lg:bg-base-100 lg:text-base-content"
92
+ base_100: "bg-base-100 text-base-content",
93
+ # "sm:bg-base-200 sm:text-base-content"
94
+ # "md:bg-base-200 md:text-base-content"
95
+ # "lg:bg-base-200 lg:text-base-content"
96
+ base_200: "bg-base-200 text-base-content",
97
+ # "sm:bg-base-300 sm:text-base-content"
98
+ # "md:bg-base-300 md:text-base-content"
99
+ # "lg:bg-base-300 lg:text-base-content"
100
+ base_300: "bg-base-300 text-base-content",
101
+ # "sm:bg-info sm:text-info-content"
102
+ # "md:bg-info sm:text-info-content"
103
+ # "lg:bg-info sm:text-info-content"
104
+ info: "bg-info text-info-content",
105
+ # "sm:bg-success sm:text-success-content"
106
+ # "md:bg-success md:text-success-content"
107
+ # "lg:bg-success lg:text-success-content"
108
+ success: "bg-success text-success-content",
109
+ # "sm:bg-warning sm:text-warning-content"
110
+ # "md:bg-warning md:text-warning-content"
111
+ # "lg:bg-warning lg:text-warning-content"
112
+ warning: "bg-warning text-warning-content",
113
+ # "sm:bg-error sm:text-error-content"
114
+ # "md:bg-error md:text-error-content"
115
+ # "lg:bg-error lg:text-error-content"
116
+ error: "bg-error text-error-content"
117
+ )
42
118
  end
43
119
  end
@@ -0,0 +1,104 @@
1
+ module PhlexyUI
2
+ class ClassList
3
+ RESPONSIVE_PREFIXES = %i[sm md lg xl].freeze
4
+
5
+ def initialize(
6
+ component:,
7
+ component_html_class: nil,
8
+ base_modifiers: [],
9
+ options: {},
10
+ modifiers_map: {}
11
+ )
12
+ @component_html_class = component_html_class
13
+ @base_modifiers = base_modifiers
14
+ @options = options
15
+ @modifiers_map = modifiers_map
16
+ end
17
+
18
+ def to_a
19
+ classes = []
20
+ add_component_class(classes)
21
+ add_selected_modifiers_classes(classes)
22
+ add_conditioned_modifiers_classes(classes)
23
+ add_responsive_modifiers_classes(classes)
24
+ add_class_option_classes(classes)
25
+ classes
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :component_html_class, :base_modifiers, :options, :modifiers_map
31
+
32
+ def selected_base_modifiers
33
+ base_modifiers.select { |modifier| modifiers_map.key?(modifier) }
34
+ end
35
+
36
+ def add_component_class(classes)
37
+ return unless component_html_class
38
+
39
+ classes << with_config_prefix(component_html_class)
40
+ end
41
+
42
+ def add_selected_modifiers_classes(classes)
43
+ classes.concat(
44
+ html_classes_for_modifiers(
45
+ selected_base_modifiers
46
+ )
47
+ )
48
+ end
49
+
50
+ def add_conditioned_modifiers_classes(classes)
51
+ modifiers_map.each do |modifier, class_name|
52
+ next unless options.delete(modifier)
53
+
54
+ classes << with_config_prefix(class_name)
55
+ end
56
+ end
57
+
58
+ def html_classes_for_modifiers(modifiers, responsive_prefix: nil)
59
+ modifiers.map do |modifier|
60
+ with_responsive_prefix(
61
+ with_config_prefix(
62
+ modifiers_map.fetch(modifier)
63
+ ),
64
+ responsive_prefix
65
+ )
66
+ end
67
+ end
68
+
69
+ def add_responsive_modifiers_classes(classes)
70
+ return unless (responsive_options = options.delete(:responsive))
71
+
72
+ RESPONSIVE_PREFIXES.each do |responsive_prefix|
73
+ if (values = responsive_options[responsive_prefix])
74
+ classes.concat(
75
+ html_classes_for_modifiers(
76
+ Array(values),
77
+ responsive_prefix:
78
+ )
79
+ )
80
+ end
81
+ end
82
+ end
83
+
84
+ def add_class_option_classes(classes)
85
+ classes << options.delete(:class) if options[:class]
86
+ end
87
+
88
+ def with_config_prefix(string)
89
+ string.to_s.split.map do |word|
90
+ "#{PhlexyUI.configuration.prefix}#{word}"
91
+ end.join(" ")
92
+ end
93
+
94
+ def with_responsive_prefix(string, responsive_prefix = nil)
95
+ string.split.map do |word|
96
+ if responsive_prefix
97
+ "#{responsive_prefix}:#{word}"
98
+ else
99
+ word
100
+ end
101
+ end.join(" ")
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhlexyUI
4
+ # @private
5
+ class CollapsibleSubMenu < Base
6
+ include Phlex::DeferredRender
7
+
8
+ def initialize(*, **)
9
+ super
10
+ @items ||= []
11
+ end
12
+
13
+ def view_template(&)
14
+ attributes = generate_attributes(base_modifiers, ATTRIBUTES_MAP)
15
+
16
+ details(**attributes) do
17
+ if @title
18
+ summary do
19
+ render @title
20
+ end
21
+ else
22
+ raise ArgumentError, "A collapsible submenu requires a title"
23
+ end
24
+
25
+ if @items.any?
26
+ ul do
27
+ @items.each do |item|
28
+ render item
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def title(&block)
36
+ if @title
37
+ raise ArgumentError, "A collapsible submenu can only have one title"
38
+ else
39
+ @title = block
40
+ end
41
+ end
42
+
43
+ def item(*, **, &)
44
+ @items << MenuItem.new(*, **, &)
45
+ end
46
+
47
+ private
48
+
49
+ ATTRIBUTES_MAP = {
50
+ open: true
51
+ }.freeze
52
+ end
53
+ end