phlex-style_variants 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|