phlexy_ui 0.1.1 → 0.1.5

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: da2c28aae3a33b6153c19f22a2d8845d1c92a7a94ad5f9ea0f326c22f419cd1d
4
+ data.tar.gz: 7fe16b4951c08d429c1e6c5848f7cab70bb3cfbc8170cfca56fccbddc1ddd3f9
5
5
  SHA512:
6
- metadata.gz: b1a9b52e5ca14871a3aea5d2f792dd8d549ca7a31cb4dd06bfbd90573998768db3ece9752d9a27af1b2d2fed2f0aea8c7ffd8efdec1373a306a74556a19b235f
7
- data.tar.gz: 3ab85f2c5f6d9a09c04d57bf978c43d82abb2ed3e4157e55b5de179c7b4a7b38c60ef1e619a491512a18590799d803ad36f40c279fbc3dd36fad89fe710482f5
6
+ metadata.gz: 8f10b63d841c5cec345f9a1da3e83ce402cd10924506a79820b6f115695d47db463e933d11943556754a88877645e6b8e05e8b4ceda64a92673bf74da967f65b
7
+ data.tar.gz: 37c963d0c597523910d93a7ba3a147663dd1dfcf980801ed52e2342eb53eb664ea2b898c85e083e8f8c45c56d4bad86b03d9d3e347df8a74dd9c274edcf41274
@@ -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,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: BADGE_MODIFIERS_MAP,
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
+ BADGE_MODIFIERS_MAP = {
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
+ }.freeze
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: BUTTON_MODIFIERS_MAP,
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
+ BUTTON_MODIFIERS_MAP = {
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
118
  }.freeze
32
-
33
- BASE_HTML_CLASS = "btn"
34
-
35
- def view_template(&)
36
- button(class: classes, data:, &)
37
- end
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,93 @@
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_responsive_modifiers_classes(classes)
23
+ add_class_option_classes(classes)
24
+ classes
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :component_html_class, :base_modifiers, :options, :modifiers_map
30
+
31
+ def selected_base_modifiers
32
+ base_modifiers.select { |modifier| modifiers_map.key?(modifier) }
33
+ end
34
+
35
+ def add_component_class(classes)
36
+ return unless component_html_class
37
+
38
+ classes << with_config_prefix(component_html_class)
39
+ end
40
+
41
+ def add_selected_modifiers_classes(classes)
42
+ classes.concat(
43
+ html_classes_for_modifiers(
44
+ selected_base_modifiers
45
+ )
46
+ )
47
+ end
48
+
49
+ def html_classes_for_modifiers(modifiers, responsive_prefix: nil)
50
+ modifiers.map do |modifier|
51
+ with_responsive_prefix(
52
+ with_config_prefix(
53
+ modifiers_map.fetch(modifier)
54
+ ),
55
+ responsive_prefix
56
+ )
57
+ end
58
+ end
59
+
60
+ def add_responsive_modifiers_classes(classes)
61
+ RESPONSIVE_PREFIXES.each do |responsive_prefix|
62
+ if (values = options.delete(responsive_prefix))
63
+ classes.concat(
64
+ html_classes_for_modifiers(
65
+ Array(values),
66
+ responsive_prefix:
67
+ )
68
+ )
69
+ end
70
+ end
71
+ end
72
+
73
+ def add_class_option_classes(classes)
74
+ classes << options.delete(:class) if options[:class]
75
+ end
76
+
77
+ def with_config_prefix(string)
78
+ string.to_s.split.map do |word|
79
+ "#{PhlexyUI.configuration.prefix}#{word}"
80
+ end.join(" ")
81
+ end
82
+
83
+ def with_responsive_prefix(string, responsive_prefix = nil)
84
+ string.split.map do |word|
85
+ if responsive_prefix
86
+ "#{responsive_prefix}:#{word}"
87
+ else
88
+ word
89
+ end
90
+ end.join(" ")
91
+ end
92
+ end
93
+ 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
@@ -10,12 +10,36 @@ module PhlexyUI
10
10
  @configuration ||= Configuration.new
11
11
  end
12
12
 
13
+ class Modifiers
14
+ def initialize
15
+ @modifiers = {}
16
+ end
17
+
18
+ def add(modifier, classes:, component: nil)
19
+ @modifiers[component] ||= {}
20
+ @modifiers[component][modifier] = classes
21
+ end
22
+
23
+ def remove(modifier, component: nil)
24
+ @modifiers[component] ||= {}
25
+ @modifiers[component]&.delete(modifier)
26
+ end
27
+
28
+ def for(component: nil)
29
+ @modifiers[component] || {}
30
+ end
31
+ end
32
+
13
33
  class Configuration
14
34
  attr_accessor :prefix
15
35
 
16
36
  def initialize
17
37
  @prefix = nil
18
38
  end
39
+
40
+ def modifiers
41
+ @modifiers ||= Modifiers.new
42
+ end
19
43
  end
20
44
  end
21
45
  end