daisyui 1.3.1 → 2.0.0
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 +4 -4
- data/CHANGELOG.md +41 -0
- data/LICENSE-tailwind_merge.txt +21 -0
- data/README.md +58 -0
- data/lib/daisy_ui/accordion.rb +32 -28
- data/lib/daisy_ui/alert.rb +97 -94
- data/lib/daisy_ui/aura.rb +37 -37
- data/lib/daisy_ui/avatar.rb +16 -14
- data/lib/daisy_ui/badge.rb +159 -156
- data/lib/daisy_ui/base.rb +38 -4
- data/lib/daisy_ui/button.rb +148 -142
- data/lib/daisy_ui/card.rb +57 -54
- data/lib/daisy_ui/carousel.rb +36 -32
- data/lib/daisy_ui/chat.rb +16 -15
- data/lib/daisy_ui/checkbox.rb +95 -93
- data/lib/daisy_ui/class_merge/cache.rb +40 -0
- data/lib/daisy_ui/class_merge/class_group_utils.rb +139 -0
- data/lib/daisy_ui/class_merge/configuration.rb +77 -0
- data/lib/daisy_ui/class_merge/daisy_groups.rb +83 -0
- data/lib/daisy_ui/class_merge/merger.rb +126 -0
- data/lib/daisy_ui/class_merge/parse_class_name.rb +110 -0
- data/lib/daisy_ui/class_merge/sort_modifiers.rb +36 -0
- data/lib/daisy_ui/class_merge/tailwind_config.rb +2505 -0
- data/lib/daisy_ui/class_merge/validators.rb +202 -0
- data/lib/daisy_ui/class_merge.rb +47 -0
- data/lib/daisy_ui/collapse.rb +32 -28
- data/lib/daisy_ui/configurable.rb +31 -3
- data/lib/daisy_ui/divider.rb +84 -78
- data/lib/daisy_ui/dock.rb +37 -35
- data/lib/daisy_ui/dropdown.rb +67 -65
- data/lib/daisy_ui/file_input.rb +95 -91
- data/lib/daisy_ui/footer.rb +16 -14
- data/lib/daisy_ui/indicator.rb +40 -36
- data/lib/daisy_ui/input.rb +95 -91
- data/lib/daisy_ui/join.rb +16 -14
- data/lib/daisy_ui/kbd.rb +37 -35
- data/lib/daisy_ui/link.rb +58 -58
- data/lib/daisy_ui/loading.rb +85 -81
- data/lib/daisy_ui/mask.rb +123 -119
- data/lib/daisy_ui/megamenu.rb +53 -50
- data/lib/daisy_ui/menu.rb +57 -53
- data/lib/daisy_ui/modal.rb +49 -47
- data/lib/daisy_ui/otp.rb +95 -93
- data/lib/daisy_ui/pagination.rb +16 -14
- data/lib/daisy_ui/progress.rb +58 -56
- data/lib/daisy_ui/radio.rb +95 -91
- data/lib/daisy_ui/range.rb +95 -91
- data/lib/daisy_ui/rating.rb +37 -35
- data/lib/daisy_ui/select.rb +95 -91
- data/lib/daisy_ui/stack.rb +24 -22
- data/lib/daisy_ui/stat.rb +16 -14
- data/lib/daisy_ui/status.rb +95 -91
- data/lib/daisy_ui/steps.rb +16 -14
- data/lib/daisy_ui/swap.rb +16 -14
- data/lib/daisy_ui/table.rb +41 -39
- data/lib/daisy_ui/tabs.rb +80 -77
- data/lib/daisy_ui/textarea.rb +95 -91
- data/lib/daisy_ui/timeline.rb +16 -14
- data/lib/daisy_ui/toast.rb +46 -42
- data/lib/daisy_ui/toggle.rb +95 -91
- data/lib/daisy_ui/tooltip.rb +111 -105
- data/lib/daisy_ui/updated_at.rb +1 -1
- data/lib/daisy_ui/version.rb +1 -1
- metadata +18 -1
data/lib/daisy_ui/checkbox.rb
CHANGED
|
@@ -30,99 +30,101 @@ module DaisyUI
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
register_modifiers(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
33
|
+
color: {
|
|
34
|
+
# "sm:checkbox-neutral"
|
|
35
|
+
# "@sm:checkbox-neutral"
|
|
36
|
+
# "md:checkbox-neutral"
|
|
37
|
+
# "@md:checkbox-neutral"
|
|
38
|
+
# "lg:checkbox-neutral"
|
|
39
|
+
# "@lg:checkbox-neutral"
|
|
40
|
+
neutral: "checkbox-neutral",
|
|
41
|
+
# "sm:checkbox-primary"
|
|
42
|
+
# "@sm:checkbox-primary"
|
|
43
|
+
# "md:checkbox-primary"
|
|
44
|
+
# "@md:checkbox-primary"
|
|
45
|
+
# "lg:checkbox-primary"
|
|
46
|
+
# "@lg:checkbox-primary"
|
|
47
|
+
primary: "checkbox-primary",
|
|
48
|
+
# "sm:checkbox-secondary"
|
|
49
|
+
# "@sm:checkbox-secondary"
|
|
50
|
+
# "md:checkbox-secondary"
|
|
51
|
+
# "@md:checkbox-secondary"
|
|
52
|
+
# "lg:checkbox-secondary"
|
|
53
|
+
# "@lg:checkbox-secondary"
|
|
54
|
+
secondary: "checkbox-secondary",
|
|
55
|
+
# "sm:checkbox-accent"
|
|
56
|
+
# "@sm:checkbox-accent"
|
|
57
|
+
# "md:checkbox-accent"
|
|
58
|
+
# "@md:checkbox-accent"
|
|
59
|
+
# "lg:checkbox-accent"
|
|
60
|
+
# "@lg:checkbox-accent"
|
|
61
|
+
accent: "checkbox-accent",
|
|
62
|
+
# "sm:checkbox-success"
|
|
63
|
+
# "@sm:checkbox-success"
|
|
64
|
+
# "md:checkbox-success"
|
|
65
|
+
# "@md:checkbox-success"
|
|
66
|
+
# "lg:checkbox-success"
|
|
67
|
+
# "@lg:checkbox-success"
|
|
68
|
+
success: "checkbox-success",
|
|
69
|
+
# "sm:checkbox-warning"
|
|
70
|
+
# "@sm:checkbox-warning"
|
|
71
|
+
# "md:checkbox-warning"
|
|
72
|
+
# "@md:checkbox-warning"
|
|
73
|
+
# "lg:checkbox-warning"
|
|
74
|
+
# "@lg:checkbox-warning"
|
|
75
|
+
warning: "checkbox-warning",
|
|
76
|
+
# "sm:checkbox-info"
|
|
77
|
+
# "@sm:checkbox-info"
|
|
78
|
+
# "md:checkbox-info"
|
|
79
|
+
# "@md:checkbox-info"
|
|
80
|
+
# "lg:checkbox-info"
|
|
81
|
+
# "@lg:checkbox-info"
|
|
82
|
+
info: "checkbox-info",
|
|
83
|
+
# "sm:checkbox-error"
|
|
84
|
+
# "@sm:checkbox-error"
|
|
85
|
+
# "md:checkbox-error"
|
|
86
|
+
# "@md:checkbox-error"
|
|
87
|
+
# "lg:checkbox-error"
|
|
88
|
+
# "@lg:checkbox-error"
|
|
89
|
+
error: "checkbox-error"
|
|
90
|
+
},
|
|
91
|
+
size: {
|
|
92
|
+
# "sm:checkbox-xs"
|
|
93
|
+
# "@sm:checkbox-xs"
|
|
94
|
+
# "md:checkbox-xs"
|
|
95
|
+
# "@md:checkbox-xs"
|
|
96
|
+
# "lg:checkbox-xs"
|
|
97
|
+
# "@lg:checkbox-xs"
|
|
98
|
+
xs: "checkbox-xs",
|
|
99
|
+
# "sm:checkbox-sm"
|
|
100
|
+
# "@sm:checkbox-sm"
|
|
101
|
+
# "md:checkbox-sm"
|
|
102
|
+
# "@md:checkbox-sm"
|
|
103
|
+
# "lg:checkbox-sm"
|
|
104
|
+
# "@lg:checkbox-sm"
|
|
105
|
+
sm: "checkbox-sm",
|
|
106
|
+
# "sm:checkbox-md"
|
|
107
|
+
# "@sm:checkbox-md"
|
|
108
|
+
# "md:checkbox-md"
|
|
109
|
+
# "@md:checkbox-md"
|
|
110
|
+
# "lg:checkbox-md"
|
|
111
|
+
# "@lg:checkbox-md"
|
|
112
|
+
md: "checkbox-md",
|
|
113
|
+
# "sm:checkbox-lg"
|
|
114
|
+
# "@sm:checkbox-lg"
|
|
115
|
+
# "md:checkbox-lg"
|
|
116
|
+
# "@md:checkbox-lg"
|
|
117
|
+
# "lg:checkbox-lg"
|
|
118
|
+
# "@lg:checkbox-lg"
|
|
119
|
+
lg: "checkbox-lg",
|
|
120
|
+
# "sm:checkbox-xl"
|
|
121
|
+
# "@sm:checkbox-xl"
|
|
122
|
+
# "md:checkbox-xl"
|
|
123
|
+
# "@md:checkbox-xl"
|
|
124
|
+
# "lg:checkbox-xl"
|
|
125
|
+
# "@lg:checkbox-xl"
|
|
126
|
+
xl: "checkbox-xl"
|
|
127
|
+
}
|
|
126
128
|
)
|
|
127
129
|
end
|
|
128
130
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "monitor"
|
|
4
|
+
|
|
5
|
+
module DaisyUI
|
|
6
|
+
module ClassMerge
|
|
7
|
+
# Thread-safe LRU cache. Ruby hashes keep insertion order, so re-inserting
|
|
8
|
+
# a key on every hit moves it to the end and the first key is always the
|
|
9
|
+
# least recently used one.
|
|
10
|
+
class Cache
|
|
11
|
+
def initialize(max_size)
|
|
12
|
+
@max_size = max_size
|
|
13
|
+
@data = {}
|
|
14
|
+
@monitor = Monitor.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns the cached value for key, or stores and returns the block's
|
|
18
|
+
# result. A nil result is not stored.
|
|
19
|
+
def getset(key)
|
|
20
|
+
@monitor.synchronize do
|
|
21
|
+
if @data.key?(key)
|
|
22
|
+
value = @data.delete(key)
|
|
23
|
+
@data[key] = value
|
|
24
|
+
return value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
value = yield
|
|
28
|
+
return value if value.nil?
|
|
29
|
+
|
|
30
|
+
@data.shift if @data.size >= @max_size
|
|
31
|
+
@data[key] = value
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def size
|
|
36
|
+
@monitor.synchronize { @data.size }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Ported from tailwind_merge 1.5.6 (https://github.com/gjtorikian/tailwind_merge)
|
|
4
|
+
# Copyright (c) 2022 Garen J. Torikian. MIT License, see LICENSE-tailwind_merge.txt.
|
|
5
|
+
|
|
6
|
+
module DaisyUI
|
|
7
|
+
module ClassMerge
|
|
8
|
+
class ClassGroupUtils
|
|
9
|
+
attr_reader :class_map
|
|
10
|
+
|
|
11
|
+
CLASS_PART_SEPARATOR = "-"
|
|
12
|
+
|
|
13
|
+
ARBITRARY_PROPERTY_REGEX = /^\[(.+)\]$/
|
|
14
|
+
|
|
15
|
+
def initialize(config)
|
|
16
|
+
@config = config
|
|
17
|
+
@class_map = create_class_map(config)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def class_group_id(class_name)
|
|
21
|
+
class_parts = class_name.split(CLASS_PART_SEPARATOR)
|
|
22
|
+
|
|
23
|
+
# Classes like `-inset-1` produce an empty string as first class_part.
|
|
24
|
+
# Assume that classes for negative values are used correctly and remove it from class_parts.
|
|
25
|
+
class_parts.shift if class_parts.first == "" && class_parts.length != 1
|
|
26
|
+
|
|
27
|
+
get_group_recursive(class_parts, @class_map) || get_group_id_for_arbitrary_property(class_name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def get_group_recursive(class_parts, class_part_object, index = 0)
|
|
31
|
+
return class_part_object[:class_group_id] if index == class_parts.length
|
|
32
|
+
|
|
33
|
+
current_class_part = class_parts[index]
|
|
34
|
+
|
|
35
|
+
next_class_part_object = class_part_object[:next_part][current_class_part]
|
|
36
|
+
|
|
37
|
+
if next_class_part_object
|
|
38
|
+
class_group_from_next_class_part = get_group_recursive(class_parts, next_class_part_object, index + 1)
|
|
39
|
+
return class_group_from_next_class_part if class_group_from_next_class_part
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
return if class_part_object[:validators].empty?
|
|
43
|
+
|
|
44
|
+
class_rest = index.zero? ? class_parts.join(CLASS_PART_SEPARATOR) : class_parts[index..].join(CLASS_PART_SEPARATOR)
|
|
45
|
+
|
|
46
|
+
# Theme procs are expanded at trie-build time (see process_classes_recursively),
|
|
47
|
+
# so validators here are always plain value validators.
|
|
48
|
+
result = class_part_object[:validators].find do |v|
|
|
49
|
+
v[:validator].call(class_rest)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
result&.fetch(:class_group_id, nil)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def get_conflicting_class_group_ids(class_group_id, has_postfix_modifier)
|
|
56
|
+
conflicts = @config[:conflicting_class_groups][class_group_id] || []
|
|
57
|
+
|
|
58
|
+
return [*conflicts, *@config[:conflicting_class_group_modifiers][class_group_id]] if has_postfix_modifier && @config[:conflicting_class_group_modifiers][class_group_id]
|
|
59
|
+
|
|
60
|
+
conflicts
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def create_class_map(config)
|
|
66
|
+
theme = config[:theme]
|
|
67
|
+
class_groups = config[:class_groups]
|
|
68
|
+
class_map = {
|
|
69
|
+
next_part: {},
|
|
70
|
+
validators: []
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
class_groups.each do |(class_group_id, class_group)|
|
|
74
|
+
process_classes_recursively(class_group, class_map, class_group_id, theme)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class_map
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def process_classes_recursively(class_group, class_part_object, class_group_id, theme)
|
|
81
|
+
class_group.each do |class_definition|
|
|
82
|
+
if class_definition.is_a?(String)
|
|
83
|
+
class_part_object_to_edit = class_definition.empty? ? class_part_object : get_class_part(class_part_object, class_definition)
|
|
84
|
+
class_part_object_to_edit[:class_group_id] = class_group_id
|
|
85
|
+
elsif class_definition.is_a?(Proc)
|
|
86
|
+
if from_theme?(class_definition)
|
|
87
|
+
process_classes_recursively(class_definition.call(@config), class_part_object, class_group_id, theme)
|
|
88
|
+
else
|
|
89
|
+
class_part_object[:validators] << {
|
|
90
|
+
validator: class_definition,
|
|
91
|
+
class_group_id: class_group_id
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
class_definition.each do |key, nested_class_group|
|
|
96
|
+
process_classes_recursively(
|
|
97
|
+
nested_class_group,
|
|
98
|
+
get_class_part(class_part_object, key),
|
|
99
|
+
class_group_id,
|
|
100
|
+
theme
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def get_class_part(class_part_object, path)
|
|
108
|
+
current_class_part_object = class_part_object
|
|
109
|
+
|
|
110
|
+
path.to_s.split(CLASS_PART_SEPARATOR).each do |path_part|
|
|
111
|
+
unless current_class_part_object[:next_part].key?(path_part)
|
|
112
|
+
current_class_part_object[:next_part][path_part] = {
|
|
113
|
+
next_part: {},
|
|
114
|
+
validators: []
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
current_class_part_object = current_class_part_object[:next_part][path_part]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
current_class_part_object
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def get_group_id_for_arbitrary_property(class_name)
|
|
125
|
+
match = ARBITRARY_PROPERTY_REGEX.match(class_name)
|
|
126
|
+
return unless match
|
|
127
|
+
|
|
128
|
+
property = match[1].to_s.split(":", 2).first
|
|
129
|
+
|
|
130
|
+
# Use two dots here because one dot is used as prefix for class groups in plugins
|
|
131
|
+
"arbitrary..#{property}" if property && !property.empty?
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def from_theme?(validator)
|
|
135
|
+
TailwindConfig::VALID_THEME_IDS.include?(validator.object_id)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DaisyUI
|
|
4
|
+
module ClassMerge
|
|
5
|
+
# Settings for DaisyUI::ClassMerge, reached through
|
|
6
|
+
# `DaisyUI.configure { |c| c.class_merge }`.
|
|
7
|
+
#
|
|
8
|
+
# DaisyUI.configure do |c|
|
|
9
|
+
# c.class_merge.enabled = true
|
|
10
|
+
# c.class_merge.utility("text-display", "text-hero", like: "text-lg")
|
|
11
|
+
# c.class_merge.theme(spacing: %w[gutter])
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# Custom colors (`bg-brand`, `text-brand`) need no registration.
|
|
15
|
+
class Configuration
|
|
16
|
+
THEME_KEYS = TailwindConfig::DEFAULTS[:theme].keys.freeze
|
|
17
|
+
|
|
18
|
+
attr_accessor :enabled
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@enabled = true
|
|
22
|
+
@theme = {}
|
|
23
|
+
@class_groups = {}
|
|
24
|
+
@conflicts = {}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Registers utilities that behave like an existing one:
|
|
28
|
+
# `utility("text-display", like: "text-lg")` makes `text-display` a font
|
|
29
|
+
# size, so it replaces `text-sm` and sits next to `text-error`.
|
|
30
|
+
def utility(*names, like:)
|
|
31
|
+
group = ClassMerge.tailwind_utils.class_group_id(like) or
|
|
32
|
+
raise ArgumentError, "#{like.inspect} is not a known utility to model #{names.join(', ')} on"
|
|
33
|
+
|
|
34
|
+
class_groups(group => names.map(&:to_s))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Appends values to Tailwind theme scales, which feed every utility using
|
|
38
|
+
# the scale: `spacing: %w[gutter]` covers `p-gutter`, `m-gutter`, `gap-gutter`.
|
|
39
|
+
def theme(**scales)
|
|
40
|
+
scales = scales.transform_keys { |key| key.to_s.tr("_", "-") }
|
|
41
|
+
unknown = scales.keys - THEME_KEYS
|
|
42
|
+
raise ArgumentError, "unknown theme key #{unknown.map(&:inspect).join(', ')}; use one of #{THEME_KEYS.join(', ')}" if unknown.any?
|
|
43
|
+
|
|
44
|
+
append(@theme, scales.transform_values { |values| Array(values).map(&:to_s) })
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Raw tailwind_merge class groups: `{ "group-id" => ["class", ...] }`.
|
|
48
|
+
# Classes in the same group conflict with each other.
|
|
49
|
+
def class_groups(groups)
|
|
50
|
+
append(@class_groups, groups)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Raw tailwind_merge conflicts: `{ "group-id" => ["other-group-id", ...] }`
|
|
54
|
+
# removes earlier classes of the listed groups when a class of the key
|
|
55
|
+
# group appears later.
|
|
56
|
+
def conflicts(conflicts)
|
|
57
|
+
append(@conflicts, conflicts)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @api private The config passed to Merger.new, including the daisyUI groups.
|
|
61
|
+
def merger_config
|
|
62
|
+
{
|
|
63
|
+
theme: @theme,
|
|
64
|
+
class_groups: DaisyGroups.build.merge(@class_groups) { |_id, existing, added| existing + added },
|
|
65
|
+
conflicting_class_groups: @conflicts
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def append(target, entries)
|
|
72
|
+
entries.each { |id, values| (target[id.to_s] ||= []).concat(Array(values)) }
|
|
73
|
+
ClassMerge.reset!
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DaisyUI
|
|
4
|
+
module ClassMerge
|
|
5
|
+
# Builds merger class groups from the modifier groups components declare:
|
|
6
|
+
#
|
|
7
|
+
# register_modifiers(size: { sm: "btn-sm", lg: "btn-lg" })
|
|
8
|
+
#
|
|
9
|
+
# gives the group `daisy:group:btn-size` = ["btn-sm", "btn-lg"], so
|
|
10
|
+
# `btn-sm btn-lg` merges to `btn-lg`. Ungrouped modifiers never conflict.
|
|
11
|
+
# Tokens Tailwind already classifies (`bg-primary text-primary-content`)
|
|
12
|
+
# are left to the Tailwind groups, which resolve them per utility.
|
|
13
|
+
#
|
|
14
|
+
# Every gem component base class (`btn`, `table`) also gets a group of its
|
|
15
|
+
# own and is never dropped, even where Tailwind has a utility of the same
|
|
16
|
+
# name (`table`, `collapse`).
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
19
|
+
module DaisyGroups
|
|
20
|
+
class << self
|
|
21
|
+
# Class groups for the merger: `{ "daisy:group:btn-size" => ["btn-xs", ...], ... }`.
|
|
22
|
+
def build(prefix: DaisyUI.configuration.prefix)
|
|
23
|
+
components = self.components
|
|
24
|
+
groups = {}
|
|
25
|
+
|
|
26
|
+
gem_components(components).each do |component|
|
|
27
|
+
next unless (base = component.component_class)
|
|
28
|
+
|
|
29
|
+
groups["daisy:base:#{base}"] = ["#{prefix}#{base}"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
components.each do |component|
|
|
33
|
+
component.modifier_groups.each do |group, members|
|
|
34
|
+
tokens = members.flat_map { |member| component.modifiers[member].to_s.split }
|
|
35
|
+
add(groups, "daisy:group:#{component.component_class || component.name}-#{group}", tokens, prefix)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Configured modifiers join the component's own group of that name.
|
|
40
|
+
DaisyUI.configuration.modifiers.groups.each do |(component, group), classes|
|
|
41
|
+
owner = component ? component.component_class || component.name : "global"
|
|
42
|
+
add(groups, "daisy:group:#{owner}-#{group}", classes.flat_map(&:split), prefix)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
groups
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def tailwind_class?(token)
|
|
49
|
+
!ClassMerge.tailwind_utils.class_group_id(token).nil?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Every named DaisyUI::Base subclass: the gem's components (eager
|
|
53
|
+
# loaded) and any app component loaded so far. An app component that
|
|
54
|
+
# loads later resets the merger through `register_modifiers`.
|
|
55
|
+
def components
|
|
56
|
+
Zeitwerk::Loader.eager_load_namespace(DaisyUI)
|
|
57
|
+
descendants(DaisyUI::Base).select(&:name)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Only the gem's own base classes are protected: an app component's
|
|
61
|
+
# derived name (`UI::Grid` -> "grid") may well be a Tailwind utility.
|
|
62
|
+
def gem_components(components = self.components)
|
|
63
|
+
components.select { |component| component.name.start_with?("DaisyUI::") }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
# Subclasses share their parent's component_class and group names, so
|
|
69
|
+
# their tokens land in the same group id and are unioned.
|
|
70
|
+
def add(groups, id, tokens, prefix)
|
|
71
|
+
tokens = tokens.reject { |token| tailwind_class?(token) }.map { |token| "#{prefix}#{token}" }
|
|
72
|
+
return if tokens.empty?
|
|
73
|
+
|
|
74
|
+
groups[id] = (groups[id] || []) | tokens
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def descendants(klass)
|
|
78
|
+
klass.subclasses.flat_map { |subclass| [subclass, *descendants(subclass)] }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Ported from tailwind_merge 1.5.6 (https://github.com/gjtorikian/tailwind_merge)
|
|
4
|
+
# Copyright (c) 2022 Garen J. Torikian. MIT License, see LICENSE-tailwind_merge.txt.
|
|
5
|
+
|
|
6
|
+
module DaisyUI
|
|
7
|
+
module ClassMerge
|
|
8
|
+
class Merger
|
|
9
|
+
include TailwindConfig
|
|
10
|
+
include ParseClassName
|
|
11
|
+
include SortModifiers
|
|
12
|
+
|
|
13
|
+
def initialize(config: {})
|
|
14
|
+
@config = merge_config(config)
|
|
15
|
+
@class_utils = ClassGroupUtils.new(@config)
|
|
16
|
+
@cache = Cache.new(@config[:cache_size])
|
|
17
|
+
@postfix_lookup_class_group_ids = build_postfix_lookup_class_group_ids(@config[:postfix_lookup_class_groups])
|
|
18
|
+
@order_sensitive_modifiers = Set.new(@config[:order_sensitive_modifiers]).freeze
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def merge(classes)
|
|
22
|
+
normalized = classes.is_a?(Array) ? classes.compact.join(" ") : classes.to_s
|
|
23
|
+
|
|
24
|
+
@cache.getset(normalized) do
|
|
25
|
+
merge_class_list(normalized).freeze
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def merge_class_list(class_list)
|
|
32
|
+
# Set of class_group_ids in following format:
|
|
33
|
+
# `{importantModifier}{variantModifiers}{classGroupId}`
|
|
34
|
+
# @example 'float'
|
|
35
|
+
# @example 'hover:focus:bg-color'
|
|
36
|
+
# @example 'md:!pr'
|
|
37
|
+
class_names = class_list.split
|
|
38
|
+
return "" if class_names.empty?
|
|
39
|
+
|
|
40
|
+
class_groups_in_conflict = Set.new
|
|
41
|
+
|
|
42
|
+
merged_classes = []
|
|
43
|
+
|
|
44
|
+
class_names.reverse_each do |original_class_name|
|
|
45
|
+
result = parse_class_name(original_class_name, prefix: @config[:prefix])
|
|
46
|
+
is_external = result.is_external
|
|
47
|
+
modifiers = result.modifiers
|
|
48
|
+
has_important_modifier = result.has_important_modifier
|
|
49
|
+
base_class_name = result.base_class_name
|
|
50
|
+
maybe_postfix_modifier_position = result.maybe_postfix_modifier_position
|
|
51
|
+
|
|
52
|
+
if is_external
|
|
53
|
+
merged_classes.push(original_class_name)
|
|
54
|
+
next
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
has_postfix_modifier = maybe_postfix_modifier_position ? true : false
|
|
58
|
+
|
|
59
|
+
if has_postfix_modifier
|
|
60
|
+
# maybe_postfix_modifier_position is a byte offset (see ParseClassName)
|
|
61
|
+
base_class_name_without_postfix = base_class_name.byteslice(0, maybe_postfix_modifier_position)
|
|
62
|
+
class_group_id = @class_utils.class_group_id(base_class_name_without_postfix)
|
|
63
|
+
|
|
64
|
+
if class_group_id && @postfix_lookup_class_group_ids[class_group_id]
|
|
65
|
+
class_group_id_with_postfix = @class_utils.class_group_id(base_class_name)
|
|
66
|
+
if class_group_id_with_postfix && class_group_id_with_postfix != class_group_id
|
|
67
|
+
class_group_id = class_group_id_with_postfix
|
|
68
|
+
has_postfix_modifier = false
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
class_group_id = @class_utils.class_group_id(base_class_name)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
unless class_group_id
|
|
76
|
+
unless has_postfix_modifier
|
|
77
|
+
# Not a Tailwind class
|
|
78
|
+
merged_classes << original_class_name
|
|
79
|
+
next
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class_group_id = @class_utils.class_group_id(base_class_name)
|
|
83
|
+
|
|
84
|
+
unless class_group_id
|
|
85
|
+
# Not a Tailwind class
|
|
86
|
+
merged_classes << original_class_name
|
|
87
|
+
next
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
has_postfix_modifier = false
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
variant_modifier = sort_modifiers(modifiers, @order_sensitive_modifiers).join(":")
|
|
94
|
+
|
|
95
|
+
modifier_id = has_important_modifier ? "#{variant_modifier}#{IMPORTANT_MODIFIER}" : variant_modifier
|
|
96
|
+
has_modifier_id = !modifier_id.empty?
|
|
97
|
+
# `to_s` matches the previous interpolation behavior: group ids may be
|
|
98
|
+
# Symbols (custom configs) or Strings (defaults, where to_s is free)
|
|
99
|
+
class_id = has_modifier_id ? "#{modifier_id}#{class_group_id}" : class_group_id.to_s
|
|
100
|
+
|
|
101
|
+
# Tailwind class omitted due to conflict
|
|
102
|
+
next if class_groups_in_conflict.include?(class_id)
|
|
103
|
+
|
|
104
|
+
class_groups_in_conflict << class_id
|
|
105
|
+
|
|
106
|
+
@class_utils.get_conflicting_class_group_ids(class_group_id, has_postfix_modifier).each do |conflicting_id|
|
|
107
|
+
class_groups_in_conflict << (has_modifier_id ? "#{modifier_id}#{conflicting_id}" : conflicting_id.to_s)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Tailwind class not in conflict
|
|
111
|
+
merged_classes << original_class_name
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
merged_classes.reverse!.join(" ")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def build_postfix_lookup_class_group_ids(class_group_ids)
|
|
118
|
+
lookup = {}
|
|
119
|
+
return lookup unless class_group_ids
|
|
120
|
+
|
|
121
|
+
class_group_ids.each { |id| lookup[id] = true }
|
|
122
|
+
lookup
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|