phlexy_ui 0.1.1 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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