phlex-style_variants 0.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +126 -0
- data/lib/phlex/style_variants/builder.rb +25 -0
- data/lib/phlex/style_variants/config.rb +32 -0
- data/lib/phlex/style_variants/set.rb +36 -0
- data/lib/phlex/style_variants/style_instance.rb +26 -0
- data/lib/phlex/style_variants/variant_group_builder.rb +23 -0
- data/lib/phlex/style_variants/version.rb +7 -0
- data/lib/phlex/style_variants.rb +129 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 907f0ff19a28e5800d9a1d4a0ac29697f0f8425f0c64f1547c4ce012f3a6574e
|
4
|
+
data.tar.gz: 1759598841f67559cde325ad73930280e05640896b9ba19db344e20cdf3b312d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dfcd75133c9301daf29326149906cbea0215fafc48fcb2136cb6baf3af0cab3179445bb0a6f70eaa05260ae1977cb93d9e3d8c4a02b65d61fe37e348a5ff82d3
|
7
|
+
data.tar.gz: 89e7feffcef312ed19a97af4043d5c338dc767e1ea9be457004b6b32a9cfa277a0e2ba6a107ce464c3ab43cc534ba0c3f9782f253610bce4b877997e785e22dd
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 omarluq
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# phlex-style_variants
|
2
|
+
|
3
|
+
[](https://rubygems.org/gems/phlex-style_variants)
|
4
|
+
[](https://www.ruby-toolbox.com/projects/phlex-style_variants)
|
5
|
+
[](https://github.com/omarluq/phlex-style_variants/actions/workflows/ci.yml)
|
6
|
+
[](https://codeclimate.com/github/omarluq/phlex-style_variants)
|
7
|
+
|
8
|
+
Powerful Variant API for phlex component built on top of Literal
|
9
|
+
|
10
|
+
> [!WARNING]
|
11
|
+
> This gem is currently in development and is considered unstable. The API is subject to change, and you may encounter bugs or incomplete features.
|
12
|
+
> Use it at your own risk, and contribute by reporting issues or suggesting improvements!
|
13
|
+
|
14
|
+
---
|
15
|
+
|
16
|
+
- [Quick start](#quick-start)
|
17
|
+
- [Support](#support)
|
18
|
+
- [License](#license)
|
19
|
+
- [Code of conduct](#code-of-conduct)
|
20
|
+
- [Contribution guide](#contribution-guide)
|
21
|
+
|
22
|
+
## Features
|
23
|
+
|
24
|
+
- Declarative Style Definitions: Easily define base styles and variant groups using a clean DSL.
|
25
|
+
- Automatic Enum Generation: Automatically generates enum classes for variant groups, ensuring type safety and easy usage.
|
26
|
+
- Inheritance Support: Create style sets that inherit from other style sets, promoting DRY (Don't Repeat Yourself) principles.
|
27
|
+
- Variant Predicates: Built-in predicate methods to check active variants (e.g., `size.md?`).
|
28
|
+
- Flexible Compilation: Compile styles based on active variants, allowing dynamic class generation.
|
29
|
+
- Seamless Integration with Phlex: Designed to work effortlessly with Phlex components, enhancing their styling capabilities.
|
30
|
+
|
31
|
+
## Quick start
|
32
|
+
|
33
|
+
```
|
34
|
+
gem install phlex-style_variants
|
35
|
+
```
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
require "phlex/style_variants"
|
39
|
+
```
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
```rb
|
44
|
+
require "phlex/style_variants"
|
45
|
+
|
46
|
+
class Button < Phlex::HTML
|
47
|
+
include Phlex::StyleVariants
|
48
|
+
|
49
|
+
# Define the primary style set
|
50
|
+
style do
|
51
|
+
base { 'inline-block px-4 py-2 rounded font-semibold' }
|
52
|
+
|
53
|
+
variants do
|
54
|
+
color do
|
55
|
+
primary { 'bg-blue-500 text-white' }
|
56
|
+
danger { 'bg-red-500 text-white' }
|
57
|
+
end
|
58
|
+
|
59
|
+
size do
|
60
|
+
sm { 'text-sm' }
|
61
|
+
md { 'text-base' }
|
62
|
+
lg { 'text-lg' }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Define a named style set called :wrapper with inheritance
|
68
|
+
style(:wrapper) do
|
69
|
+
base { 'p-4 bg-gray-100' }
|
70
|
+
|
71
|
+
variants do
|
72
|
+
border_style do
|
73
|
+
none { '' }
|
74
|
+
dashed { 'border-2 border-dashed border-gray-400' }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
prop :text, String, default: -> { 'Click Me!' }, reader: :public
|
80
|
+
|
81
|
+
def view_template
|
82
|
+
div(class: style(:wrapper, border_style:).compiled) do
|
83
|
+
button(class: style(color:, size:).compiled) do
|
84
|
+
text
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Rendering the Button component
|
91
|
+
button = Button.new(
|
92
|
+
text: 'Delete',
|
93
|
+
color: Button::ColorEnum::Danger,
|
94
|
+
size: Button::SizeEnum::Lg,
|
95
|
+
border_style: Button::BorderStyleEnum::Dashed
|
96
|
+
)
|
97
|
+
button.call
|
98
|
+
```
|
99
|
+
|
100
|
+
output:
|
101
|
+
|
102
|
+
```html
|
103
|
+
<div class="p-4 bg-gray-100 border-2 border-dashed border-gray-400">
|
104
|
+
<button
|
105
|
+
class="inline-block px-4 py-2 rounded font-semibold bg-red-500 text-white text-lg"
|
106
|
+
>
|
107
|
+
Delete
|
108
|
+
</button>
|
109
|
+
</div>
|
110
|
+
```
|
111
|
+
|
112
|
+
## Support
|
113
|
+
|
114
|
+
If you want to report a bug, or have ideas, feedback or questions about the gem, [let me know via GitHub issues](https://github.com/omarluq/phlex-style_variants/issues/new) and I will do my best to provide a helpful answer. Happy hacking!
|
115
|
+
|
116
|
+
## License
|
117
|
+
|
118
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
|
119
|
+
|
120
|
+
## Code of conduct
|
121
|
+
|
122
|
+
Everyone interacting in this project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
|
123
|
+
|
124
|
+
## Contribution guide
|
125
|
+
|
126
|
+
Pull requests are welcome!
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/phlex/style_variants/builder.rb
|
4
|
+
|
5
|
+
module Phlex
|
6
|
+
module StyleVariants
|
7
|
+
class Builder
|
8
|
+
def build(&)
|
9
|
+
@variants = {}
|
10
|
+
instance_eval(&) if block_given?
|
11
|
+
@variants
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(name, &)
|
15
|
+
group = VariantGroupBuilder.new(name)
|
16
|
+
group.instance_eval(&) if block_given?
|
17
|
+
@variants[name.to_sym] = group.options
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to_missing?(name, include_private = false)
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/phlex/style_variants/config.rb
|
4
|
+
|
5
|
+
module Phlex
|
6
|
+
module StyleVariants
|
7
|
+
# Configuration class to hold multiple style sets
|
8
|
+
class Config < Literal::Struct
|
9
|
+
prop :styles, _Hash(_Symbol, Set), default: -> { {} }
|
10
|
+
|
11
|
+
# Define a new style set
|
12
|
+
def define(name, &)
|
13
|
+
styles[name.to_sym] = Set.new
|
14
|
+
styles[name.to_sym].instance_eval(&)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Compile a style by name and variants
|
18
|
+
def compile(name, **variants)
|
19
|
+
style_set = styles[name.to_sym]
|
20
|
+
return '' unless style_set
|
21
|
+
|
22
|
+
style_set.compile(**variants)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create a hard copy of the config, duplicating each style set
|
26
|
+
def hard_copy
|
27
|
+
copied_styles = styles.transform_values(&:dup)
|
28
|
+
self.class.new(styles: copied_styles)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/phlex/style_variants/set.rb
|
4
|
+
|
5
|
+
module Phlex
|
6
|
+
module StyleVariants
|
7
|
+
# Represents a set of styles with base and variants
|
8
|
+
class Set < Literal::Struct
|
9
|
+
# Make 'base' _Nilable and default to nil
|
10
|
+
prop :__base, _Nilable(String), default: -> {}
|
11
|
+
prop :__variants, _Hash(_Symbol, _Hash(_Symbol, String)), default: -> { {} }
|
12
|
+
|
13
|
+
# Define base styles
|
14
|
+
def base(&)
|
15
|
+
self.__base = instance_eval(&)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Define variants using the Builder DSL
|
19
|
+
def variants(&)
|
20
|
+
builder = Builder.new
|
21
|
+
self.__variants = builder.build(&)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Compile the class string based on variants
|
25
|
+
def compile(**opts)
|
26
|
+
classes = __base ? __base.dup : ''
|
27
|
+
|
28
|
+
opts.each do |variant_group, variant_value|
|
29
|
+
classes += " #{variant_value.value}"
|
30
|
+
end
|
31
|
+
|
32
|
+
classes.strip
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/phlex/style_variants/instance.rb
|
4
|
+
|
5
|
+
module Phlex
|
6
|
+
module StyleVariants
|
7
|
+
# Represents the compiled classes and variant enums
|
8
|
+
class StyleInstance < Literal::Struct
|
9
|
+
prop :compiled, String
|
10
|
+
prop :variants, _Hash(_Symbol, Literal::Enum), default: -> { {} }
|
11
|
+
|
12
|
+
# Delegate variant predicate methods (e.g., size.md?) to the enums
|
13
|
+
def method_missing(name, *args, &block)
|
14
|
+
if @variants.key?(name.to_sym) && @enums.key?(name.to_sym)
|
15
|
+
@variants[name.to_sym].__send__("#{variants[name.to_sym]}?")
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def respond_to_missing?(name, include_private = false)
|
22
|
+
@variants.key?(name.to_sym) || super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlex
|
4
|
+
module StyleVariants
|
5
|
+
# Helper class to build individual variants within a group
|
6
|
+
class VariantGroupBuilder
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def initialize(name)
|
10
|
+
@name = name
|
11
|
+
@options = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(option_name, &)
|
15
|
+
@options[option_name.to_sym] = instance_eval(&)
|
16
|
+
end
|
17
|
+
|
18
|
+
def respond_to_missing?(name, include_private = false)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# lib/phlex/style_variants/style_variants.rb
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'literal'
|
6
|
+
require 'debug'
|
7
|
+
|
8
|
+
module Phlex
|
9
|
+
module StyleVariants
|
10
|
+
autoload :VERSION, 'phlex/style_variants/version'
|
11
|
+
autoload :Builder, 'phlex/style_variants/builder'
|
12
|
+
autoload :VariantGroupBuilder, 'phlex/style_variants/variant_group_builder'
|
13
|
+
autoload :Config, 'phlex/style_variants/config'
|
14
|
+
autoload :Set, 'phlex/style_variants/set'
|
15
|
+
autoload :StyleInstance, 'phlex/style_variants/style_instance'
|
16
|
+
|
17
|
+
# Define ClassMethods for style definitions
|
18
|
+
module ClassMethods
|
19
|
+
# Define a new style set with optional inheritance
|
20
|
+
# Usage:
|
21
|
+
# style do
|
22
|
+
# base { "..." }
|
23
|
+
# variants do
|
24
|
+
# color do
|
25
|
+
# primary { "..." }
|
26
|
+
# danger { "..." }
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# style(:wrapper, inherit: true) do
|
32
|
+
# base { "..." }
|
33
|
+
# variants do
|
34
|
+
# border { "..." }
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
|
38
|
+
def style_config
|
39
|
+
@style_config ||= if superclass.respond_to?(:style_config)
|
40
|
+
superclass.style_config.hard_copy
|
41
|
+
else
|
42
|
+
Config.new
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def style(name = default_style_name, &)
|
47
|
+
style_config.define(name.to_sym, &)
|
48
|
+
|
49
|
+
# After defining the style set, define enums and props
|
50
|
+
style_set = style_config.styles[name.to_sym]
|
51
|
+
style_set.__variants.each do |variant_group, options|
|
52
|
+
# Define Enum Class for the Variant Group
|
53
|
+
enum_class = Class.new(Literal::Enum) do
|
54
|
+
prop :value, String
|
55
|
+
options.each do |option_key, option_value|
|
56
|
+
const_set(option_key.to_s.split('_').collect(&:capitalize).join, new(value: option_value))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
enum_const_name = "#{variant_group.to_s.split('_').collect(&:capitalize).join}Enum".freeze
|
60
|
+
# Assign the Enum Class to the Component's Namespace
|
61
|
+
const_set(enum_const_name, enum_class)
|
62
|
+
|
63
|
+
# Define Props for the Variant Group
|
64
|
+
# Set the first option as the default value
|
65
|
+
default_variant = options.keys.first.to_s.capitalize
|
66
|
+
prop variant_group.to_sym, enum_class, default: enum_class.const_get(default_variant), reader: :public
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Helper method to remove module namespaces
|
71
|
+
def demodulize(class_name)
|
72
|
+
class_name.split('::').last
|
73
|
+
end
|
74
|
+
|
75
|
+
# Helper method to convert CamelCase to snake_case
|
76
|
+
def underscore(camel_cased_word)
|
77
|
+
word = camel_cased_word.dup
|
78
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
79
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
80
|
+
word.tr!('-', '_')
|
81
|
+
word.downcase!
|
82
|
+
word
|
83
|
+
end
|
84
|
+
|
85
|
+
# Helper method to return the string if present, else nil
|
86
|
+
def presence(string)
|
87
|
+
(string.nil? || string.strip.empty?) ? nil : string
|
88
|
+
end
|
89
|
+
|
90
|
+
# Determine the default style name based on the class name
|
91
|
+
def default_style_name
|
92
|
+
@default_style_name ||= begin
|
93
|
+
# Get the class name as a string
|
94
|
+
class_name = name
|
95
|
+
|
96
|
+
# Demodulize: Remove module namespaces
|
97
|
+
base_name = demodulize(class_name)
|
98
|
+
|
99
|
+
# Remove "Component" or "::Component" suffix
|
100
|
+
base_name = base_name.sub(/Component$/, '')
|
101
|
+
|
102
|
+
# Convert CamelCase to snake_case
|
103
|
+
style_name = underscore(base_name)
|
104
|
+
|
105
|
+
# Return the style_name if present; otherwise, default to 'component'
|
106
|
+
presence(style_name) || 'component'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Define InstanceMethods for style compilation
|
112
|
+
module InstanceMethods
|
113
|
+
# Compile styles based on provided variants
|
114
|
+
# Usage:
|
115
|
+
# style(color: :primary, size: :md).size.md? # => true
|
116
|
+
def style(name = self.class.default_style_name, **variants)
|
117
|
+
compiled = self.class.style_config.compile(name.to_sym, **variants).strip
|
118
|
+
StyleInstance.new(compiled:, variants:)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Hook to include ClassMethods and InstanceMethods when included
|
123
|
+
def self.included(base)
|
124
|
+
base.extend(ClassMethods)
|
125
|
+
base.extend(Literal::Properties)
|
126
|
+
base.include(InstanceMethods)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: phlex-style_variants
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- omarluq
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-01-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: literal
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- omar.luqman@hey.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- LICENSE.txt
|
35
|
+
- README.md
|
36
|
+
- lib/phlex/style_variants.rb
|
37
|
+
- lib/phlex/style_variants/builder.rb
|
38
|
+
- lib/phlex/style_variants/config.rb
|
39
|
+
- lib/phlex/style_variants/set.rb
|
40
|
+
- lib/phlex/style_variants/style_instance.rb
|
41
|
+
- lib/phlex/style_variants/variant_group_builder.rb
|
42
|
+
- lib/phlex/style_variants/version.rb
|
43
|
+
homepage: https://github.com/omarluq/phlex-style_variants
|
44
|
+
licenses:
|
45
|
+
- MIT
|
46
|
+
metadata:
|
47
|
+
bug_tracker_uri: https://github.com/omarluq/phlex-style_variants/issues
|
48
|
+
changelog_uri: https://github.com/omarluq/phlex-style_variants/releases
|
49
|
+
source_code_uri: https://github.com/omarluq/phlex-style_variants
|
50
|
+
homepage_uri: https://github.com/omarluq/phlex-style_variants
|
51
|
+
rubygems_mfa_required: 'true'
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3.1'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubygems_version: 3.5.16
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: A powerful variant API for phlex components
|
71
|
+
test_files: []
|