markawesome 0.2.0 → 0.3.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 +24 -0
- data/lib/markawesome/attribute_parser.rb +41 -0
- data/lib/markawesome/transformers/badge_transformer.rb +34 -10
- data/lib/markawesome/transformers/button_transformer.rb +47 -13
- data/lib/markawesome/transformers/callout_transformer.rb +30 -49
- data/lib/markawesome/transformers/card_transformer.rb +35 -27
- data/lib/markawesome/transformers/carousel_transformer.rb +28 -10
- data/lib/markawesome/transformers/copy_button_transformer.rb +77 -11
- data/lib/markawesome/transformers/details_transformer.rb +33 -31
- data/lib/markawesome/transformers/dialog_transformer.rb +15 -13
- data/lib/markawesome/transformers/image_dialog_transformer.rb +5 -5
- data/lib/markawesome/transformers/tabs_transformer.rb +40 -8
- data/lib/markawesome/transformers/tag_transformer.rb +64 -14
- data/lib/markawesome/version.rb +1 -1
- data/markawesome.gemspec +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: adad1d3e9850c4d1f82b4cbe73eb56ac9c82c413e66ea0adb1291794a3a9a25a
|
|
4
|
+
data.tar.gz: 07b7741a6e96f7031d38d1e10d6ca6a0314d5edd05341222f1f7d7964904d1c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4af697c90bf1d4a0d7d10de6fac07f784319b01d45b58fdcfb190475e97f4e5164b7b5fe7e7fd7475aece2651c78cd31181059cb7a8c1ec01ea33c05a154f11
|
|
7
|
+
data.tar.gz: ecca3f31fb0b27db1572cea1c82a2989434948699fe2125f8020f0cdd04e39f03a7d2426fbacfff8d65eba0252e6cdf0056a05d93ff7587ae85d39652e076553
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.3.0] - 2026-02-08
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **TagTransformer**: Comprehensive attribute support including variant, size, appearance, pill, and removable
|
|
12
|
+
- **TabsTransformer**: Added activation, active, and no-scroll-controls attributes
|
|
13
|
+
- **DetailsTransformer**: Added disabled, open, and name attributes
|
|
14
|
+
- **CopyButtonTransformer**: Comprehensive attribute support including variant, size, appearance, and disabled
|
|
15
|
+
- **CarouselTransformer**: Added autoplay-interval attribute support
|
|
16
|
+
- **CardTransformer**: Added orientation support
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **AttributeParser Refactoring**: Migrated DetailsTransformer and CarouselTransformer to use AttributeParser for consistent, flexible attribute handling
|
|
21
|
+
- **DialogTransformer**: Refactored to use AttributeParser for improved attribute parsing
|
|
22
|
+
- **CardTransformer**: Refactored to use AttributeParser
|
|
23
|
+
- Added RuboCop configuration for better code quality management
|
|
24
|
+
|
|
25
|
+
### Improved
|
|
26
|
+
|
|
27
|
+
- Consistent attribute parsing across all major transformers
|
|
28
|
+
- Better test coverage for all new attributes
|
|
29
|
+
- More flexible, order-independent parameter syntax
|
|
30
|
+
|
|
7
31
|
## [0.2.0] - 2025-10-27
|
|
8
32
|
|
|
9
33
|
### Added
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Markawesome
|
|
4
|
+
# Parses space-separated attributes from a parameter string
|
|
5
|
+
# Supports multiple values per attribute with rightmost-wins semantics
|
|
6
|
+
# Useful for flexible syntax like "pill pulse brand" where order doesn't matter
|
|
7
|
+
class AttributeParser
|
|
8
|
+
# Parse space-separated parameters into an attribute hash
|
|
9
|
+
# @param params_string [String] Space-separated parameter string
|
|
10
|
+
# @param attribute_schema [Hash] Schema defining attributes and allowed values
|
|
11
|
+
# Example: { variant: %w[brand success], pill: [true], attention: %w[pulse bounce] }
|
|
12
|
+
# @return [Hash] Parsed attributes with resolved values
|
|
13
|
+
def self.parse(params_string, attribute_schema)
|
|
14
|
+
return {} if params_string.nil? || params_string.strip.empty?
|
|
15
|
+
|
|
16
|
+
parsed = {}
|
|
17
|
+
tokens = params_string.strip.split(/\s+/)
|
|
18
|
+
|
|
19
|
+
tokens.each do |token|
|
|
20
|
+
attribute_schema.each do |attr_name, allowed_values|
|
|
21
|
+
next unless allowed_values.include?(token)
|
|
22
|
+
|
|
23
|
+
# Rightmost-wins: latest value for this attribute wins
|
|
24
|
+
parsed[attr_name] = token
|
|
25
|
+
break # Move to next token once we've matched it
|
|
26
|
+
end
|
|
27
|
+
# Tokens that don't match any attribute are silently ignored
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
parsed
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Check if a token is valid for an attribute
|
|
34
|
+
# @param token [String] The token to check
|
|
35
|
+
# @param allowed_values [Array] Allowed values for the attribute
|
|
36
|
+
# @return [Boolean] True if token is in allowed values
|
|
37
|
+
def self.valid_token?(token, allowed_values)
|
|
38
|
+
allowed_values.include?(token)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -1,23 +1,39 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
4
5
|
|
|
5
6
|
module Markawesome
|
|
6
7
|
# Transforms badge syntax into wa-badge elements
|
|
7
|
-
# Primary syntax: !!!
|
|
8
|
-
# Alternative syntax: :::wa-badge
|
|
8
|
+
# Primary syntax: !!!params?\ncontent\n!!!
|
|
9
|
+
# Alternative syntax: :::wa-badge params?\ncontent\n:::
|
|
10
|
+
#
|
|
11
|
+
# Params: space-separated tokens, any order (rightmost-wins for conflicts)
|
|
9
12
|
# Variants: brand, success, neutral, warning, danger
|
|
13
|
+
# Appearance: accent, filled, outlined, filled-outlined
|
|
14
|
+
# Attention: none, pulse, bounce
|
|
15
|
+
# Flags: pill
|
|
10
16
|
class BadgeTransformer < BaseTransformer
|
|
17
|
+
BADGE_ATTRIBUTES = {
|
|
18
|
+
variant: %w[brand success neutral warning danger],
|
|
19
|
+
appearance: %w[accent filled outlined filled-outlined],
|
|
20
|
+
attention: %w[none pulse bounce],
|
|
21
|
+
pill: %w[pill]
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
11
24
|
def self.transform(content)
|
|
12
|
-
# Define both regex patterns
|
|
13
|
-
primary_regex = /^!!!(
|
|
14
|
-
alternative_regex = /^:::wa-badge\s*(
|
|
25
|
+
# Define both regex patterns - capture params as a single string
|
|
26
|
+
primary_regex = /^!!!(.*?)\n(.*?)\n!!!/m
|
|
27
|
+
alternative_regex = /^:::wa-badge\s*(.*?)\n(.*?)\n:::/m
|
|
15
28
|
|
|
16
29
|
# Define shared transformation logic
|
|
17
|
-
transform_proc = proc do |
|
|
30
|
+
transform_proc = proc do |params_string, badge_content|
|
|
18
31
|
badge_content = badge_content.strip
|
|
19
32
|
|
|
20
|
-
|
|
33
|
+
# Parse space-separated parameters
|
|
34
|
+
attributes = AttributeParser.parse(params_string, BADGE_ATTRIBUTES)
|
|
35
|
+
|
|
36
|
+
build_badge_html(badge_content, attributes)
|
|
21
37
|
end
|
|
22
38
|
|
|
23
39
|
# Apply both patterns
|
|
@@ -28,8 +44,7 @@ module Markawesome
|
|
|
28
44
|
class << self
|
|
29
45
|
private
|
|
30
46
|
|
|
31
|
-
def build_badge_html(content,
|
|
32
|
-
variant_attr = variant ? " variant=\"#{variant}\"" : ''
|
|
47
|
+
def build_badge_html(content, attributes)
|
|
33
48
|
badge_html = markdown_to_html(content).strip
|
|
34
49
|
|
|
35
50
|
# Remove paragraph tags if the content is just text
|
|
@@ -39,7 +54,16 @@ module Markawesome
|
|
|
39
54
|
# Replace spaces after closing tags with non-breaking spaces to prevent CSS collapse
|
|
40
55
|
badge_html = badge_html.gsub(%r{(</\w+>)\s+}, '\1 ')
|
|
41
56
|
|
|
42
|
-
|
|
57
|
+
# Build HTML attributes
|
|
58
|
+
attr_parts = []
|
|
59
|
+
attr_parts << "variant=\"#{attributes[:variant]}\"" if attributes[:variant]
|
|
60
|
+
attr_parts << "appearance=\"#{attributes[:appearance]}\"" if attributes[:appearance]
|
|
61
|
+
attr_parts << "attention=\"#{attributes[:attention]}\"" if attributes[:attention]
|
|
62
|
+
attr_parts << 'pill' if attributes[:pill]
|
|
63
|
+
|
|
64
|
+
attrs_string = attr_parts.empty? ? '' : " #{attr_parts.join(' ')}"
|
|
65
|
+
|
|
66
|
+
"<wa-badge#{attrs_string}>#{badge_html}</wa-badge>"
|
|
43
67
|
end
|
|
44
68
|
end
|
|
45
69
|
end
|
|
@@ -1,26 +1,49 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
4
5
|
|
|
5
6
|
module Markawesome
|
|
6
7
|
# Transforms button syntax into wa-button elements
|
|
7
|
-
# Primary syntax: %%%
|
|
8
|
-
# Alternative syntax: :::wa-button
|
|
9
|
-
#
|
|
8
|
+
# Primary syntax: %%%attributes?\ncontent\n%%%
|
|
9
|
+
# Alternative syntax: :::wa-button attributes?\ncontent\n:::
|
|
10
|
+
#
|
|
11
|
+
# Attributes:
|
|
12
|
+
# - variant: brand, success, neutral, warning, danger
|
|
13
|
+
# - appearance: accent, filled, outlined, filled-outlined, plain
|
|
14
|
+
# - size: small, medium, large
|
|
15
|
+
# - pill: pill (rounded edges)
|
|
16
|
+
# - caret: caret (dropdown indicator)
|
|
17
|
+
# - loading: loading (loading state)
|
|
18
|
+
# - disabled: disabled (disabled state)
|
|
10
19
|
#
|
|
11
20
|
# Link buttons: %%%brand\n[Text](url)\n%%%
|
|
12
|
-
# Regular buttons: %%%brand\nText\n%%%
|
|
21
|
+
# Regular buttons: %%%brand large pill\nText\n%%%
|
|
13
22
|
class ButtonTransformer < BaseTransformer
|
|
23
|
+
# Define the schema for button attributes
|
|
24
|
+
BUTTON_ATTRIBUTES = {
|
|
25
|
+
variant: %w[brand success neutral warning danger],
|
|
26
|
+
appearance: %w[accent filled outlined filled-outlined plain],
|
|
27
|
+
size: %w[small medium large],
|
|
28
|
+
pill: %w[pill],
|
|
29
|
+
caret: %w[caret],
|
|
30
|
+
loading: %w[loading],
|
|
31
|
+
disabled: %w[disabled]
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
14
34
|
def self.transform(content)
|
|
15
|
-
# Define both regex patterns
|
|
16
|
-
primary_regex = /^%%%(
|
|
17
|
-
alternative_regex = /^:::wa-button\s*(
|
|
35
|
+
# Define both regex patterns - capture all space-separated parameters
|
|
36
|
+
primary_regex = /^%%%([^\n]*)\n(.*?)\n%%%/m
|
|
37
|
+
alternative_regex = /^:::wa-button\s*([^\n]*)\n(.*?)\n:::/m
|
|
18
38
|
|
|
19
39
|
# Define shared transformation logic
|
|
20
|
-
transform_proc = proc do |
|
|
40
|
+
transform_proc = proc do |params_string, button_content|
|
|
21
41
|
button_content = button_content.strip
|
|
22
42
|
|
|
23
|
-
|
|
43
|
+
# Parse attributes using AttributeParser
|
|
44
|
+
attributes = AttributeParser.parse(params_string, BUTTON_ATTRIBUTES)
|
|
45
|
+
|
|
46
|
+
build_button_html(button_content, attributes)
|
|
24
47
|
end
|
|
25
48
|
|
|
26
49
|
# Apply both patterns
|
|
@@ -31,8 +54,19 @@ module Markawesome
|
|
|
31
54
|
class << self
|
|
32
55
|
private
|
|
33
56
|
|
|
34
|
-
def build_button_html(content,
|
|
35
|
-
|
|
57
|
+
def build_button_html(content, attributes)
|
|
58
|
+
# Build HTML attributes from parsed attributes
|
|
59
|
+
html_attrs = []
|
|
60
|
+
|
|
61
|
+
html_attrs << "variant=\"#{attributes[:variant]}\"" if attributes[:variant]
|
|
62
|
+
html_attrs << "appearance=\"#{attributes[:appearance]}\"" if attributes[:appearance]
|
|
63
|
+
html_attrs << "size=\"#{attributes[:size]}\"" if attributes[:size]
|
|
64
|
+
html_attrs << 'pill' if attributes[:pill]
|
|
65
|
+
html_attrs << 'with-caret' if attributes[:caret]
|
|
66
|
+
html_attrs << 'loading' if attributes[:loading]
|
|
67
|
+
html_attrs << 'disabled' if attributes[:disabled]
|
|
68
|
+
|
|
69
|
+
attrs_string = html_attrs.empty? ? '' : " #{html_attrs.join(' ')}"
|
|
36
70
|
|
|
37
71
|
# Check if content contains a markdown link
|
|
38
72
|
link_match = content.match(/^\[([^\]]+)\]\(([^)]+)\)$/)
|
|
@@ -49,7 +83,7 @@ module Markawesome
|
|
|
49
83
|
# Fix whitespace issues like in badges
|
|
50
84
|
button_html = button_html.gsub(%r{(</\w+>)\s+}, '\1 ')
|
|
51
85
|
|
|
52
|
-
"<wa-button#{
|
|
86
|
+
"<wa-button#{attrs_string} href=\"#{link_url}\">#{button_html}</wa-button>"
|
|
53
87
|
else
|
|
54
88
|
# It's a regular button
|
|
55
89
|
button_html = markdown_to_html(content).strip
|
|
@@ -58,7 +92,7 @@ module Markawesome
|
|
|
58
92
|
# Fix whitespace issues like in badges
|
|
59
93
|
button_html = button_html.gsub(%r{(</\w+>)\s+}, '\1 ')
|
|
60
94
|
|
|
61
|
-
"<wa-button#{
|
|
95
|
+
"<wa-button#{attrs_string}>#{button_html}</wa-button>"
|
|
62
96
|
end
|
|
63
97
|
end
|
|
64
98
|
end
|
|
@@ -1,29 +1,42 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
4
5
|
|
|
5
6
|
module Markawesome
|
|
6
7
|
# Transforms callout syntax into wa-callout elements
|
|
7
|
-
# Primary syntax: :::variant\ncontent\n:::
|
|
8
|
-
# Alternative syntax: :::wa-callout variant\ncontent\n:::
|
|
9
|
-
# Variants: info, success, neutral, warning, danger
|
|
8
|
+
# Primary syntax: :::variant [size] [appearance]\ncontent\n:::
|
|
9
|
+
# Alternative syntax: :::wa-callout variant [size] [appearance]\ncontent\n:::
|
|
10
|
+
# Variants: brand, info (alias for brand), success, neutral, warning, danger
|
|
11
|
+
# Size: small, medium, large
|
|
12
|
+
# Appearance: accent, filled, outlined, plain, filled-outlined
|
|
10
13
|
class CalloutTransformer < BaseTransformer
|
|
14
|
+
VARIANTS = %w[info brand success neutral warning danger].freeze
|
|
15
|
+
CALLOUT_ATTRIBUTES = {
|
|
16
|
+
size: %w[small medium large],
|
|
17
|
+
appearance: %w[accent filled outlined plain filled-outlined]
|
|
18
|
+
}.freeze
|
|
19
|
+
VARIANT_ALIASES = { 'info' => 'brand' }.freeze
|
|
20
|
+
|
|
11
21
|
def self.transform(content)
|
|
12
|
-
|
|
13
|
-
primary_regex = /^:::(
|
|
14
|
-
alternative_regex = /^:::wa-callout\s+(
|
|
22
|
+
variant_pattern = VARIANTS.join('|')
|
|
23
|
+
primary_regex = /^:::(#{variant_pattern})([^\n]*)\n(.*?)\n:::/m
|
|
24
|
+
alternative_regex = /^:::wa-callout\s+(#{variant_pattern})([^\n]*)\n(.*?)\n:::/m
|
|
25
|
+
|
|
26
|
+
transform_proc = proc do |variant, extra_params, inner_content|
|
|
27
|
+
actual_variant = VARIANT_ALIASES.fetch(variant, variant)
|
|
28
|
+
extra_attrs = AttributeParser.parse(extra_params, CALLOUT_ATTRIBUTES)
|
|
15
29
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
30
|
+
attr_parts = ["variant=\"#{actual_variant}\""]
|
|
31
|
+
attr_parts << "appearance=\"#{extra_attrs[:appearance]}\"" if extra_attrs[:appearance]
|
|
32
|
+
attr_parts << "size=\"#{extra_attrs[:size]}\"" if extra_attrs[:size]
|
|
19
33
|
|
|
20
|
-
|
|
21
|
-
html_content = "#{
|
|
34
|
+
icon_html = icon_for(actual_variant)
|
|
35
|
+
html_content = "#{icon_html}#{markdown_to_html(inner_content)}"
|
|
22
36
|
|
|
23
|
-
"
|
|
37
|
+
"<wa-callout #{attr_parts.join(' ')}>#{html_content}</wa-callout>"
|
|
24
38
|
end
|
|
25
39
|
|
|
26
|
-
# Apply both patterns
|
|
27
40
|
patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
|
|
28
41
|
apply_multiple_patterns(content, patterns)
|
|
29
42
|
end
|
|
@@ -31,48 +44,16 @@ module Markawesome
|
|
|
31
44
|
class << self
|
|
32
45
|
private
|
|
33
46
|
|
|
34
|
-
def
|
|
35
|
-
# Get icon from configuration if available
|
|
47
|
+
def icon_for(variant)
|
|
36
48
|
config = Markawesome.configuration
|
|
37
49
|
icons = config&.callout_icons || default_icons
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
when 'info'
|
|
41
|
-
{
|
|
42
|
-
additional_params: ' variant="brand"',
|
|
43
|
-
inner_prepend: "<wa-icon slot=\"icon\" name=\"#{icons[:info]}\" variant=\"solid\"></wa-icon>"
|
|
44
|
-
}
|
|
45
|
-
when 'success'
|
|
46
|
-
{
|
|
47
|
-
additional_params: ' variant="success"',
|
|
48
|
-
inner_prepend: "<wa-icon slot=\"icon\" name=\"#{icons[:success]}\" variant=\"solid\"></wa-icon>"
|
|
49
|
-
}
|
|
50
|
-
when 'neutral'
|
|
51
|
-
{
|
|
52
|
-
additional_params: ' variant="neutral"',
|
|
53
|
-
inner_prepend: "<wa-icon slot=\"icon\" name=\"#{icons[:neutral]}\" variant=\"solid\"></wa-icon>"
|
|
54
|
-
}
|
|
55
|
-
when 'warning'
|
|
56
|
-
{
|
|
57
|
-
additional_params: ' variant="warning"',
|
|
58
|
-
inner_prepend: "<wa-icon slot=\"icon\" name=\"#{icons[:warning]}\" variant=\"solid\"></wa-icon>"
|
|
59
|
-
}
|
|
60
|
-
when 'danger'
|
|
61
|
-
{
|
|
62
|
-
additional_params: ' variant="danger"',
|
|
63
|
-
inner_prepend: "<wa-icon slot=\"icon\" name=\"#{icons[:danger]}\" variant=\"solid\"></wa-icon>"
|
|
64
|
-
}
|
|
65
|
-
else
|
|
66
|
-
{
|
|
67
|
-
additional_params: '',
|
|
68
|
-
inner_prepend: ''
|
|
69
|
-
}
|
|
70
|
-
end
|
|
50
|
+
icon_name = icons[variant.to_sym]
|
|
51
|
+
"<wa-icon slot=\"icon\" name=\"#{icon_name}\" variant=\"solid\"></wa-icon>"
|
|
71
52
|
end
|
|
72
53
|
|
|
73
54
|
def default_icons
|
|
74
55
|
{
|
|
75
|
-
|
|
56
|
+
brand: 'circle-info',
|
|
76
57
|
success: 'circle-check',
|
|
77
58
|
neutral: 'gear',
|
|
78
59
|
warning: 'triangle-exclamation',
|
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
4
5
|
|
|
5
6
|
module Markawesome
|
|
6
7
|
# Transforms card syntax into wa-card elements
|
|
7
|
-
# Primary syntax: ===
|
|
8
|
-
# Alternative syntax: :::wa-card
|
|
9
|
-
#
|
|
8
|
+
# Primary syntax: ===params?\ncontent\n===
|
|
9
|
+
# Alternative syntax: :::wa-card params?\ncontent\n:::
|
|
10
|
+
# Supported attributes:
|
|
11
|
+
# appearance: outlined (default), filled, filled-outlined, plain, accent
|
|
12
|
+
# orientation: vertical (default), horizontal
|
|
10
13
|
class CardTransformer < BaseTransformer
|
|
14
|
+
CARD_ATTRIBUTES = {
|
|
15
|
+
appearance: %w[outlined filled filled-outlined plain accent],
|
|
16
|
+
orientation: %w[vertical horizontal]
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
11
19
|
def self.transform(content)
|
|
12
|
-
# Define both regex patterns
|
|
13
|
-
primary_regex = /^===(
|
|
14
|
-
alternative_regex = /^:::wa-card\s*(
|
|
20
|
+
# Define both regex patterns - capture any params
|
|
21
|
+
primary_regex = /^===([^\n]*)\n(.*?)\n===/m
|
|
22
|
+
alternative_regex = /^:::wa-card\s*([^\n]*)\n(.*?)\n:::/m
|
|
15
23
|
|
|
16
24
|
# Define shared transformation logic
|
|
17
|
-
transform_proc = proc do |
|
|
25
|
+
transform_proc = proc do |params_string, card_content|
|
|
18
26
|
card_content = card_content.strip
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
attributes = AttributeParser.parse(params_string, CARD_ATTRIBUTES)
|
|
21
29
|
card_parts = parse_card_content(card_content)
|
|
22
30
|
|
|
23
|
-
build_card_html(card_parts,
|
|
31
|
+
build_card_html(card_parts, attributes)
|
|
24
32
|
end
|
|
25
33
|
|
|
26
34
|
# Apply both patterns
|
|
@@ -31,15 +39,6 @@ module Markawesome
|
|
|
31
39
|
class << self
|
|
32
40
|
private
|
|
33
41
|
|
|
34
|
-
def normalize_appearance(appearance_param)
|
|
35
|
-
case appearance_param
|
|
36
|
-
when 'filled', 'filled-outlined', 'plain', 'accent'
|
|
37
|
-
appearance_param
|
|
38
|
-
else
|
|
39
|
-
'outlined' # default
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
42
|
def parse_card_content(content)
|
|
44
43
|
parts = {
|
|
45
44
|
media: nil,
|
|
@@ -81,21 +80,28 @@ module Markawesome
|
|
|
81
80
|
parts
|
|
82
81
|
end
|
|
83
82
|
|
|
84
|
-
def build_card_html(parts,
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
def build_card_html(parts, attributes)
|
|
84
|
+
# Extract appearance and orientation from attributes
|
|
85
|
+
appearance = attributes[:appearance] || 'outlined'
|
|
86
|
+
orientation = attributes[:orientation] || 'vertical'
|
|
87
|
+
|
|
88
|
+
html_attrs = []
|
|
89
|
+
html_attrs << "appearance=\"#{appearance}\"" if appearance != 'outlined'
|
|
90
|
+
html_attrs << "orientation=\"#{orientation}\"" if orientation != 'vertical'
|
|
87
91
|
|
|
88
92
|
# Add SSR attributes if slots are present
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
html_attrs << 'with-media' if parts[:media]
|
|
94
|
+
html_attrs << 'with-header' if parts[:header]
|
|
95
|
+
html_attrs << 'with-footer' if parts[:footer]
|
|
92
96
|
|
|
93
|
-
attr_string =
|
|
97
|
+
attr_string = html_attrs.empty? ? '' : " #{html_attrs.join(' ')}"
|
|
94
98
|
|
|
95
99
|
html_parts = []
|
|
96
100
|
|
|
97
101
|
# Media slot
|
|
98
|
-
|
|
102
|
+
if parts[:media]
|
|
103
|
+
html_parts << "<img slot=\"media\" src=\"#{parts[:media][:src]}\" alt=\"#{parts[:media][:alt]}\">"
|
|
104
|
+
end
|
|
99
105
|
|
|
100
106
|
# Header slot
|
|
101
107
|
if parts[:header]
|
|
@@ -110,7 +116,9 @@ module Markawesome
|
|
|
110
116
|
end
|
|
111
117
|
|
|
112
118
|
# Footer slot
|
|
113
|
-
|
|
119
|
+
if parts[:footer]
|
|
120
|
+
html_parts << "<div slot=\"footer\"><wa-button href=\"#{parts[:footer][:href]}\">#{parts[:footer][:text]}</wa-button></div>"
|
|
121
|
+
end
|
|
114
122
|
|
|
115
123
|
"<wa-card#{attr_string}>#{html_parts.join}</wa-card>"
|
|
116
124
|
end
|
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
4
5
|
|
|
5
6
|
module Markawesome
|
|
6
7
|
# Transforms carousel syntax into wa-carousel elements
|
|
7
8
|
# Primary syntax: ~~~~~~params\n~~~ slide1\ncontent\n~~~\n~~~ slide2\ncontent\n~~~\n~~~~~~
|
|
8
9
|
# Alternative syntax: :::wa-carousel params\n~~~ slide1\ncontent\n~~~\n~~~ slide2\ncontent\n~~~\n:::
|
|
9
10
|
# Params can include: numbers (slides-per-page, slides-per-move), keywords (loop, navigation, pagination,
|
|
10
|
-
# autoplay, mouse-dragging, vertical),
|
|
11
|
+
# autoplay, mouse-dragging, vertical), key-value pairs (autoplay-interval:value), and CSS properties
|
|
12
|
+
# (scroll-hint:value, aspect-ratio:value, slide-gap:value)
|
|
11
13
|
class CarouselTransformer < BaseTransformer
|
|
14
|
+
COMPONENT_ATTRIBUTES = {
|
|
15
|
+
loop: %w[loop],
|
|
16
|
+
navigation: %w[navigation],
|
|
17
|
+
pagination: %w[pagination],
|
|
18
|
+
autoplay: %w[autoplay],
|
|
19
|
+
'mouse-dragging': %w[mouse-dragging],
|
|
20
|
+
vertical: %w[vertical]
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
12
23
|
def self.transform(content)
|
|
13
24
|
# Define both regex patterns
|
|
14
25
|
# Match: ~~~~~~params (optional)
|
|
@@ -44,14 +55,29 @@ module Markawesome
|
|
|
44
55
|
css_vars: {}
|
|
45
56
|
}
|
|
46
57
|
|
|
58
|
+
# Parse boolean flags using AttributeParser
|
|
59
|
+
attributes = AttributeParser.parse(params, COMPONENT_ATTRIBUTES)
|
|
60
|
+
|
|
61
|
+
# Convert boolean attributes to result format
|
|
62
|
+
attributes.each_key do |key|
|
|
63
|
+
if key == :vertical
|
|
64
|
+
result[:attributes]['orientation'] = 'vertical'
|
|
65
|
+
else
|
|
66
|
+
result[:attributes][key.to_s] = true
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Parse numeric and key:value tokens manually
|
|
47
71
|
tokens = params.strip.split(/\s+/)
|
|
48
72
|
numeric_count = 0
|
|
49
73
|
|
|
50
74
|
tokens.each do |token|
|
|
51
|
-
# Check for CSS custom properties
|
|
75
|
+
# Check for key:value pairs (attributes and CSS custom properties)
|
|
52
76
|
if token.include?(':')
|
|
53
77
|
key, value = token.split(':', 2)
|
|
54
78
|
case key
|
|
79
|
+
when 'autoplay-interval'
|
|
80
|
+
result[:attributes]['autoplay-interval'] = value
|
|
55
81
|
when 'scroll-hint'
|
|
56
82
|
result[:css_vars]['--scroll-hint'] = value
|
|
57
83
|
when 'aspect-ratio'
|
|
@@ -69,14 +95,6 @@ module Markawesome
|
|
|
69
95
|
elsif numeric_count == 2
|
|
70
96
|
result[:attributes]['slides-per-move'] = token
|
|
71
97
|
end
|
|
72
|
-
# Check for boolean flags
|
|
73
|
-
elsif %w[loop navigation pagination autoplay mouse-dragging vertical].include?(token)
|
|
74
|
-
# For orientation, we need to handle it specially
|
|
75
|
-
if token == 'vertical'
|
|
76
|
-
result[:attributes]['orientation'] = 'vertical'
|
|
77
|
-
else
|
|
78
|
-
result[:attributes][token] = true
|
|
79
|
-
end
|
|
80
98
|
end
|
|
81
99
|
end
|
|
82
100
|
|
|
@@ -1,31 +1,64 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
4
5
|
|
|
5
6
|
module Markawesome
|
|
6
7
|
# Transforms copy button syntax into wa-copy-button elements
|
|
7
|
-
# Primary syntax:
|
|
8
|
-
# Alternative syntax: :::wa-copy-button
|
|
8
|
+
# Primary syntax: <<<params?\ncontent\n<<<
|
|
9
|
+
# Alternative syntax: :::wa-copy-button params?\ncontent\n:::
|
|
10
|
+
#
|
|
11
|
+
# Params: space-separated tokens, any order (rightmost-wins for conflicts)
|
|
12
|
+
# Placement: top, right, bottom, left
|
|
13
|
+
# Duration: numeric value (feedback-duration in milliseconds)
|
|
14
|
+
# Flags: disabled
|
|
15
|
+
# Labels: copy-label="text", success-label="text", error-label="text"
|
|
16
|
+
# From: from="element-id" (copy from another element)
|
|
9
17
|
#
|
|
10
18
|
# Usage:
|
|
11
19
|
# <<<
|
|
12
20
|
# This text will be copied to clipboard
|
|
13
21
|
# <<<
|
|
14
22
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
23
|
+
# <<<disabled top 2000
|
|
24
|
+
# Disabled copy button with top tooltip and 2s feedback
|
|
25
|
+
# <<<
|
|
26
|
+
#
|
|
27
|
+
# <<<copy-label="Click to copy" success-label="Copied!"
|
|
28
|
+
# Custom labels
|
|
29
|
+
# <<<
|
|
30
|
+
#
|
|
31
|
+
# :::wa-copy-button right from="my-element"
|
|
32
|
+
# Copy from element with ID "my-element"
|
|
17
33
|
# :::
|
|
18
34
|
class CopyButtonTransformer < BaseTransformer
|
|
35
|
+
COPY_BUTTON_ATTRIBUTES = {
|
|
36
|
+
placement: %w[top right bottom left],
|
|
37
|
+
disabled: %w[disabled]
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
19
40
|
def self.transform(content)
|
|
20
|
-
# Define both regex patterns
|
|
21
|
-
primary_regex =
|
|
22
|
-
alternative_regex = /^:::wa-copy-button\n(.*?)\n:::/m
|
|
41
|
+
# Define both regex patterns - capture params as a single string
|
|
42
|
+
primary_regex = /^<<<(.*?)\n(.*?)\n<<</m
|
|
43
|
+
alternative_regex = /^:::wa-copy-button\s*(.*?)\n(.*?)\n:::/m
|
|
23
44
|
|
|
24
45
|
# Define shared transformation logic
|
|
25
|
-
transform_proc = proc do |copy_content|
|
|
46
|
+
transform_proc = proc do |params_string, copy_content|
|
|
26
47
|
copy_content = copy_content.strip
|
|
27
48
|
|
|
28
|
-
|
|
49
|
+
# Parse space-separated parameters
|
|
50
|
+
attributes = AttributeParser.parse(params_string, COPY_BUTTON_ATTRIBUTES)
|
|
51
|
+
|
|
52
|
+
# Extract quoted labels and from attribute
|
|
53
|
+
attributes[:copy_label] = extract_quoted_attribute(params_string, 'copy-label')
|
|
54
|
+
attributes[:success_label] = extract_quoted_attribute(params_string, 'success-label')
|
|
55
|
+
attributes[:error_label] = extract_quoted_attribute(params_string, 'error-label')
|
|
56
|
+
attributes[:from] = extract_quoted_attribute(params_string, 'from')
|
|
57
|
+
|
|
58
|
+
# Extract numeric feedback-duration
|
|
59
|
+
attributes[:feedback_duration] = ::Regexp.last_match(1) if params_string =~ /\b(\d+)\b/
|
|
60
|
+
|
|
61
|
+
build_copy_button_html(copy_content, attributes)
|
|
29
62
|
end
|
|
30
63
|
|
|
31
64
|
# Apply both patterns
|
|
@@ -36,11 +69,44 @@ module Markawesome
|
|
|
36
69
|
class << self
|
|
37
70
|
private
|
|
38
71
|
|
|
39
|
-
def build_copy_button_html(content)
|
|
72
|
+
def build_copy_button_html(content, attributes)
|
|
40
73
|
# Escape the content for the value attribute
|
|
41
74
|
escaped_content = content.gsub('"', '"').gsub("'", ''')
|
|
42
75
|
|
|
43
|
-
|
|
76
|
+
# Build HTML attributes
|
|
77
|
+
attr_parts = []
|
|
78
|
+
|
|
79
|
+
# Add value or from attribute
|
|
80
|
+
attr_parts << if attributes[:from]
|
|
81
|
+
"from=\"#{attributes[:from]}\""
|
|
82
|
+
else
|
|
83
|
+
"value=\"#{escaped_content}\""
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Add tooltip placement
|
|
87
|
+
attr_parts << "tooltip-placement=\"#{attributes[:placement]}\"" if attributes[:placement]
|
|
88
|
+
|
|
89
|
+
# Add custom labels
|
|
90
|
+
attr_parts << "copy-label=\"#{attributes[:copy_label]}\"" if attributes[:copy_label]
|
|
91
|
+
attr_parts << "success-label=\"#{attributes[:success_label]}\"" if attributes[:success_label]
|
|
92
|
+
attr_parts << "error-label=\"#{attributes[:error_label]}\"" if attributes[:error_label]
|
|
93
|
+
|
|
94
|
+
# Add feedback duration
|
|
95
|
+
attr_parts << "feedback-duration=\"#{attributes[:feedback_duration]}\"" if attributes[:feedback_duration]
|
|
96
|
+
|
|
97
|
+
# Add disabled flag
|
|
98
|
+
attr_parts << 'disabled' if attributes[:disabled]
|
|
99
|
+
|
|
100
|
+
attrs_string = attr_parts.join(' ')
|
|
101
|
+
|
|
102
|
+
"<wa-copy-button #{attrs_string}></wa-copy-button>"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def extract_quoted_attribute(params_string, attr_name)
|
|
106
|
+
# Match attr-name="value" or attr-name='value'
|
|
107
|
+
return unless params_string =~ /#{Regexp.escape(attr_name)}=["']([^"']+)["']/
|
|
108
|
+
|
|
109
|
+
::Regexp.last_match(1)
|
|
44
110
|
end
|
|
45
111
|
end
|
|
46
112
|
end
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
4
5
|
|
|
5
6
|
module Markawesome
|
|
6
7
|
# Transforms summary/details syntax into wa-details elements
|
|
7
|
-
# Primary syntax: ^^^appearance? icon-placement?\nsummary\n>>>\ndetails\n^^^
|
|
8
|
-
# Alternative syntax: :::wa-details appearance? icon-placement?\nsummary\n>>>\ndetails\n:::
|
|
8
|
+
# Primary syntax: ^^^appearance? icon-placement? disabled? open? name:value?\nsummary\n>>>\ndetails\n^^^
|
|
9
|
+
# Alternative syntax: :::wa-details appearance? icon-placement? disabled? open? name:value?\nsummary\n>>>\ndetails\n:::
|
|
9
10
|
# Appearances: outlined (default), filled, filled-outlined, plain
|
|
10
11
|
# Icon placement: start, end (default)
|
|
12
|
+
# Boolean flags: disabled, open
|
|
13
|
+
# Name: name:group-id for accordion behavior
|
|
11
14
|
class DetailsTransformer < BaseTransformer
|
|
15
|
+
COMPONENT_ATTRIBUTES = {
|
|
16
|
+
appearance: %w[outlined filled filled-outlined plain],
|
|
17
|
+
icon_placement: %w[start end],
|
|
18
|
+
disabled: %w[disabled],
|
|
19
|
+
open: %w[open]
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
12
22
|
def self.transform(content)
|
|
13
23
|
# Define both regex patterns - capture parameter string
|
|
14
24
|
primary_regex = /^\^\^\^?(.*?)\n(.*?)\n^>>>\n(.*?)\n^\^\^\^?/m
|
|
@@ -19,17 +29,26 @@ module Markawesome
|
|
|
19
29
|
summary_content = summary_content.strip
|
|
20
30
|
details_content = details_content.strip
|
|
21
31
|
|
|
22
|
-
# Parse parameters
|
|
23
|
-
|
|
32
|
+
# Parse parameters using AttributeParser
|
|
33
|
+
attributes = AttributeParser.parse(params_string, COMPONENT_ATTRIBUTES)
|
|
34
|
+
|
|
35
|
+
# Extract name parameter (format: name:value) - special handling
|
|
36
|
+
name_value = extract_name_value(params_string)
|
|
24
37
|
|
|
25
|
-
appearance_class = normalize_appearance(
|
|
26
|
-
icon_placement =
|
|
38
|
+
appearance_class = normalize_appearance(attributes[:appearance])
|
|
39
|
+
icon_placement = attributes[:icon_placement] || 'end'
|
|
27
40
|
summary_html = markdown_to_html(summary_content)
|
|
28
41
|
details_html = markdown_to_html(details_content)
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
43
|
+
# Build attributes
|
|
44
|
+
attr_parts = ["appearance='#{appearance_class}'", "icon-placement='#{icon_placement}'"]
|
|
45
|
+
attr_parts << 'disabled' if attributes[:disabled]
|
|
46
|
+
attr_parts << 'open' if attributes[:open]
|
|
47
|
+
attr_parts << "name='#{name_value}'" if name_value
|
|
48
|
+
|
|
49
|
+
"<wa-details #{attr_parts.join(' ')}>" \
|
|
50
|
+
"<span slot='summary'>#{summary_html}</span>" \
|
|
51
|
+
"#{details_html}</wa-details>"
|
|
33
52
|
end
|
|
34
53
|
|
|
35
54
|
# Apply both patterns
|
|
@@ -40,19 +59,13 @@ module Markawesome
|
|
|
40
59
|
class << self
|
|
41
60
|
private
|
|
42
61
|
|
|
43
|
-
def
|
|
44
|
-
return
|
|
62
|
+
def extract_name_value(params_string)
|
|
63
|
+
return nil if params_string.nil? || params_string.strip.empty?
|
|
45
64
|
|
|
46
|
-
#
|
|
65
|
+
# Extract name parameter (format: name:value)
|
|
47
66
|
tokens = params_string.strip.split(/\s+/)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
placement_options = %w[start end]
|
|
51
|
-
|
|
52
|
-
appearance_param = tokens.find { |token| appearance_options.include?(token) }
|
|
53
|
-
icon_placement_param = tokens.find { |token| placement_options.include?(token) }
|
|
54
|
-
|
|
55
|
-
[appearance_param, icon_placement_param]
|
|
67
|
+
name_token = tokens.find { |token| token.start_with?('name:') }
|
|
68
|
+
name_token&.sub(/^name:/, '')
|
|
56
69
|
end
|
|
57
70
|
|
|
58
71
|
def normalize_appearance(appearance_param)
|
|
@@ -67,17 +80,6 @@ module Markawesome
|
|
|
67
80
|
'outlined'
|
|
68
81
|
end
|
|
69
82
|
end
|
|
70
|
-
|
|
71
|
-
def normalize_icon_placement(icon_placement_param)
|
|
72
|
-
case icon_placement_param
|
|
73
|
-
when 'start'
|
|
74
|
-
'start'
|
|
75
|
-
when 'end'
|
|
76
|
-
'end'
|
|
77
|
-
else
|
|
78
|
-
'end'
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
83
|
end
|
|
82
84
|
end
|
|
83
85
|
end
|
|
@@ -2,14 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
require 'digest'
|
|
4
4
|
require_relative 'base_transformer'
|
|
5
|
+
require_relative '../attribute_parser'
|
|
5
6
|
|
|
6
7
|
module Markawesome
|
|
7
8
|
# Transforms dialog syntax into wa-dialog elements with trigger buttons
|
|
8
9
|
# Primary syntax: ???params\nbutton text\n>>>\ncontent\n???
|
|
9
10
|
# Alternative syntax: :::wa-dialog params\nbutton text\n>>>\ncontent\n:::
|
|
10
|
-
#
|
|
11
|
+
#
|
|
12
|
+
# Params: space-separated tokens (order doesn't matter)
|
|
13
|
+
# Flags: light-dismiss
|
|
14
|
+
# Width: CSS unit value (e.g., 500px, 50vw, 40em)
|
|
11
15
|
# Note: Header with close X button is always enabled for accessibility
|
|
12
16
|
class DialogTransformer < BaseTransformer
|
|
17
|
+
DIALOG_ATTRIBUTES = {
|
|
18
|
+
light_dismiss: %w[light-dismiss]
|
|
19
|
+
}.freeze
|
|
13
20
|
def self.transform(content)
|
|
14
21
|
# Define both regex patterns - capture parameter string, button text, and content
|
|
15
22
|
# Params are on the same line as the opening delimiter
|
|
@@ -48,22 +55,17 @@ module Markawesome
|
|
|
48
55
|
class << self
|
|
49
56
|
private
|
|
50
57
|
|
|
51
|
-
# Parse parameters from the params string
|
|
58
|
+
# Parse parameters from the params string using AttributeParser
|
|
52
59
|
def parse_parameters(params_string)
|
|
53
60
|
return [false, nil] if params_string.nil? || params_string.strip.empty?
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
light_dismiss =
|
|
62
|
+
# Parse attributes using AttributeParser
|
|
63
|
+
attributes = AttributeParser.parse(params_string, DIALOG_ATTRIBUTES)
|
|
64
|
+
light_dismiss = attributes[:light_dismiss] == 'light-dismiss'
|
|
58
65
|
|
|
59
|
-
# Look for width parameter (
|
|
60
|
-
|
|
61
|
-
tokens.
|
|
62
|
-
if token.match?(/^\d+(\.\d+)?(px|em|rem|vw|vh|%|ch)$/)
|
|
63
|
-
width = token
|
|
64
|
-
break
|
|
65
|
-
end
|
|
66
|
-
end
|
|
66
|
+
# Look for width parameter (CSS unit value, not from predefined list)
|
|
67
|
+
tokens = params_string.strip.split(/\s+/)
|
|
68
|
+
width = tokens.find { |token| token.match?(/^\d+(\.\d+)?(px|em|rem|vw|vh|%|ch)$/) }
|
|
67
69
|
|
|
68
70
|
[light_dismiss, width]
|
|
69
71
|
end
|
|
@@ -86,21 +86,21 @@ module Markawesome
|
|
|
86
86
|
# Must protect both markdown syntax (|||...|||) and already-transformed HTML
|
|
87
87
|
def protect_comparisons(content)
|
|
88
88
|
comparison_blocks = []
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
# First protect markdown comparison syntax: |||...|||
|
|
91
91
|
protected = content.gsub(/\|\|\|(\d+)?\n.*?\n\|\|\|/m) do |match|
|
|
92
92
|
placeholder = "<!--IMAGE_DIALOG_COMPARISON_#{comparison_blocks.length}-->"
|
|
93
93
|
comparison_blocks << match
|
|
94
94
|
placeholder
|
|
95
95
|
end
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
# Also protect already-transformed HTML comparison blocks: <wa-comparison ...>...</wa-comparison>
|
|
98
|
-
protected = protected.gsub(
|
|
98
|
+
protected = protected.gsub(%r{<wa-comparison[^>]*>.*?</wa-comparison>}m) do |match|
|
|
99
99
|
placeholder = "<!--IMAGE_DIALOG_COMPARISON_#{comparison_blocks.length}-->"
|
|
100
100
|
comparison_blocks << match
|
|
101
101
|
placeholder
|
|
102
102
|
end
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
[protected, comparison_blocks]
|
|
105
105
|
end
|
|
106
106
|
|
|
@@ -117,7 +117,7 @@ module Markawesome
|
|
|
117
117
|
def transform_to_dialog(alt_text, image_url, title, config = {})
|
|
118
118
|
# Parse width from title if specified (e.g., "50%", "800px", "60vw")
|
|
119
119
|
width = extract_width_from_title(title)
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
# Use default width from config if no width specified in title
|
|
122
122
|
width ||= config[:default_width] if config[:default_width]
|
|
123
123
|
|
|
@@ -1,25 +1,57 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
4
5
|
|
|
5
6
|
module Markawesome
|
|
6
7
|
# Transforms tabs syntax into wa-tab-group elements
|
|
7
|
-
# Primary syntax: ++++++
|
|
8
|
-
# Alternative syntax: :::wa-
|
|
9
|
-
#
|
|
8
|
+
# Primary syntax: ++++++[attributes]\n+++tab1\ncontent\n+++\n+++tab2\ncontent\n+++\n++++++
|
|
9
|
+
# Alternative syntax: :::wa-tab-group [attributes]\n+++tab1\ncontent\n+++\n+++tab2\ncontent\n+++\n:::
|
|
10
|
+
# Attributes:
|
|
11
|
+
# - placement: top (default), bottom, start, end
|
|
12
|
+
# - activation: auto (default), manual
|
|
13
|
+
# - active: panel name to show initially (e.g., "general", "tab-2")
|
|
14
|
+
# - no-scroll-controls: disables scroll arrows
|
|
10
15
|
class TabsTransformer < BaseTransformer
|
|
16
|
+
COMPONENT_ATTRIBUTES = {
|
|
17
|
+
placement: %w[top bottom start end],
|
|
18
|
+
activation: %w[auto manual]
|
|
19
|
+
}.freeze
|
|
11
20
|
def self.transform(content)
|
|
12
21
|
# Define both regex patterns
|
|
13
|
-
|
|
14
|
-
|
|
22
|
+
# Captures optional attributes (placement, activation, active, no-scroll-controls)
|
|
23
|
+
primary_regex = /^\+{6}([^\n]*)\n((\+\+\+ [^\n]+\n.*?\n\+\+\+\n?)+)\+{6}/m
|
|
24
|
+
alternative_regex = /^:::wa-tab-group\s*([^\n]*)\n((\+\+\+ [^\n]+\n.*?\n\+\+\+\n?)+):::/m
|
|
15
25
|
|
|
16
26
|
# Define shared transformation logic
|
|
17
|
-
transform_proc = proc do |
|
|
18
|
-
|
|
27
|
+
transform_proc = proc do |params_string, tabs_block, _third_capture|
|
|
28
|
+
# Parse attributes using AttributeParser
|
|
29
|
+
attributes = AttributeParser.parse(params_string.to_s.strip, COMPONENT_ATTRIBUTES)
|
|
30
|
+
|
|
31
|
+
# Extract boolean flags
|
|
32
|
+
no_scroll_controls = params_string.to_s.include?('no-scroll-controls')
|
|
33
|
+
|
|
34
|
+
# Extract active panel name (any token that's not a placement or activation)
|
|
35
|
+
active_panel = nil
|
|
36
|
+
params_string.to_s.split.each do |token|
|
|
37
|
+
next if COMPONENT_ATTRIBUTES[:placement].include?(token)
|
|
38
|
+
next if COMPONENT_ATTRIBUTES[:activation].include?(token)
|
|
39
|
+
next if token == 'no-scroll-controls'
|
|
40
|
+
|
|
41
|
+
active_panel = token
|
|
42
|
+
break
|
|
43
|
+
end
|
|
19
44
|
|
|
20
45
|
tabs, tab_panels = extract_tabs_and_panels(tabs_block)
|
|
21
46
|
|
|
22
|
-
|
|
47
|
+
# Build HTML attributes
|
|
48
|
+
html_attrs = []
|
|
49
|
+
html_attrs << "placement=\"#{attributes[:placement] || 'top'}\""
|
|
50
|
+
html_attrs << "activation=\"#{attributes[:activation]}\"" if attributes[:activation]
|
|
51
|
+
html_attrs << "active=\"#{active_panel}\"" if active_panel
|
|
52
|
+
html_attrs << 'without-scroll-controls' if no_scroll_controls
|
|
53
|
+
|
|
54
|
+
"<wa-tab-group #{html_attrs.join(' ')}>#{tabs.join}#{tab_panels.join}</wa-tab-group>"
|
|
23
55
|
end
|
|
24
56
|
|
|
25
57
|
# Apply both patterns
|
|
@@ -1,28 +1,67 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
4
5
|
|
|
5
6
|
module Markawesome
|
|
6
7
|
# Transforms tag syntax into wa-tag elements
|
|
7
|
-
# Primary syntax: @@@
|
|
8
|
-
# Inline syntax: @@@
|
|
9
|
-
# Alternative syntax: :::wa-tag
|
|
10
|
-
#
|
|
8
|
+
# Primary syntax: @@@params?\ncontent\n@@@
|
|
9
|
+
# Inline syntax: @@@ params? content @@@
|
|
10
|
+
# Alternative syntax: :::wa-tag params?\ncontent\n:::
|
|
11
|
+
#
|
|
12
|
+
# Supported attributes:
|
|
13
|
+
# - variant: brand, success, neutral, warning, danger
|
|
14
|
+
# - appearance: accent, filled, outlined, filled-outlined
|
|
15
|
+
# - size: small, medium, large
|
|
16
|
+
# - pill: boolean (rounded edges)
|
|
17
|
+
# - with-remove: boolean (removable tag with remove button)
|
|
11
18
|
class TagTransformer < BaseTransformer
|
|
19
|
+
# Component attributes configuration for AttributeParser
|
|
20
|
+
COMPONENT_ATTRIBUTES = {
|
|
21
|
+
variant: %w[brand success neutral warning danger],
|
|
22
|
+
appearance: %w[accent filled outlined filled-outlined],
|
|
23
|
+
size: %w[small medium large],
|
|
24
|
+
pill: %w[pill],
|
|
25
|
+
'with-remove': %w[with-remove]
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
12
28
|
def self.transform(content)
|
|
13
29
|
# Define regex patterns
|
|
14
30
|
# Block syntax (multiline with newlines) - supports both LF and CRLF
|
|
15
|
-
|
|
16
|
-
|
|
31
|
+
# Capture optional parameters and content
|
|
32
|
+
primary_regex = /^@@@([^\r\n]*?)\r?\n(.*?)\r?\n@@@/m
|
|
33
|
+
alternative_regex = /^:::wa-tag\s*([^\r\n]*?)\r?\n(.*?)\r?\n:::/m
|
|
17
34
|
|
|
18
35
|
# Inline syntax (same line with spaces)
|
|
19
|
-
|
|
36
|
+
# Match the whole content between @@@ delimiters on a single line
|
|
37
|
+
# Use horizontal whitespace only (\h = spaces/tabs, not newlines)
|
|
38
|
+
inline_regex = /@@@\h*([^@\r\n]+?)\h*@@@/
|
|
20
39
|
|
|
21
40
|
# Define shared transformation logic
|
|
22
|
-
transform_proc = proc do |
|
|
41
|
+
transform_proc = proc do |params, tag_content|
|
|
23
42
|
tag_content = tag_content.strip
|
|
24
43
|
|
|
25
|
-
build_tag_html(tag_content,
|
|
44
|
+
build_tag_html(tag_content, params)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Inline transformation - split params from content
|
|
48
|
+
inline_transform_proc = proc do |full_content|
|
|
49
|
+
full_content = full_content.strip
|
|
50
|
+
tokens = full_content.split(/\s+/)
|
|
51
|
+
params_tokens = []
|
|
52
|
+
content_tokens = []
|
|
53
|
+
|
|
54
|
+
# Separate attribute tokens from content tokens
|
|
55
|
+
tokens.each do |token|
|
|
56
|
+
matched = COMPONENT_ATTRIBUTES.any? { |_attr, values| values.include?(token) }
|
|
57
|
+
matched ? params_tokens << token : content_tokens << token
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Build params and content strings
|
|
61
|
+
params = params_tokens.join(' ')
|
|
62
|
+
tag_content = content_tokens.any? ? content_tokens.join(' ') : full_content
|
|
63
|
+
|
|
64
|
+
build_tag_html(tag_content, params)
|
|
26
65
|
end
|
|
27
66
|
|
|
28
67
|
# Apply all patterns (inline first to avoid conflicts)
|
|
@@ -30,8 +69,7 @@ module Markawesome
|
|
|
30
69
|
{
|
|
31
70
|
regex: inline_regex,
|
|
32
71
|
block: proc do |_match, matchdata|
|
|
33
|
-
|
|
34
|
-
transform_proc.call(*captures)
|
|
72
|
+
inline_transform_proc.call(matchdata[1])
|
|
35
73
|
end
|
|
36
74
|
},
|
|
37
75
|
*dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
|
|
@@ -42,14 +80,26 @@ module Markawesome
|
|
|
42
80
|
class << self
|
|
43
81
|
private
|
|
44
82
|
|
|
45
|
-
def build_tag_html(content,
|
|
46
|
-
|
|
83
|
+
def build_tag_html(content, params)
|
|
84
|
+
# Parse attributes using AttributeParser
|
|
85
|
+
attributes = AttributeParser.parse(params, COMPONENT_ATTRIBUTES)
|
|
86
|
+
|
|
87
|
+
# Build HTML attributes
|
|
88
|
+
html_attrs = []
|
|
89
|
+
html_attrs << "variant=\"#{attributes[:variant]}\"" if attributes[:variant]
|
|
90
|
+
html_attrs << "appearance=\"#{attributes[:appearance]}\"" if attributes[:appearance]
|
|
91
|
+
html_attrs << "size=\"#{attributes[:size]}\"" if attributes[:size]
|
|
92
|
+
html_attrs << 'pill' if attributes[:pill]
|
|
93
|
+
html_attrs << 'with-remove' if attributes[:'with-remove']
|
|
94
|
+
|
|
95
|
+
attrs_string = html_attrs.empty? ? '' : " #{html_attrs.join(' ')}"
|
|
96
|
+
|
|
47
97
|
tag_html = markdown_to_html(content).strip
|
|
48
98
|
|
|
49
99
|
# Remove paragraph tags if the content is just text
|
|
50
100
|
tag_html = tag_html.gsub(%r{^<p>(.*)</p>$}m, '\1')
|
|
51
101
|
|
|
52
|
-
"<wa-tag#{
|
|
102
|
+
"<wa-tag#{attrs_string}>#{tag_html}</wa-tag>"
|
|
53
103
|
end
|
|
54
104
|
end
|
|
55
105
|
end
|
data/lib/markawesome/version.rb
CHANGED
data/markawesome.gemspec
CHANGED
|
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.metadata['homepage_uri'] = spec.homepage
|
|
19
19
|
spec.metadata['source_code_uri'] = "#{spec.homepage}/tree/main"
|
|
20
20
|
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
21
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
21
22
|
|
|
22
23
|
# Specify which files should be added to the gem when it is released.
|
|
23
24
|
spec.files = Dir.chdir(__dir__) do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: markawesome
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Janne Waren
|
|
@@ -91,6 +91,7 @@ files:
|
|
|
91
91
|
- LICENSE.txt
|
|
92
92
|
- README.md
|
|
93
93
|
- lib/markawesome.rb
|
|
94
|
+
- lib/markawesome/attribute_parser.rb
|
|
94
95
|
- lib/markawesome/transformer.rb
|
|
95
96
|
- lib/markawesome/transformers.rb
|
|
96
97
|
- lib/markawesome/transformers/badge_transformer.rb
|
|
@@ -116,6 +117,7 @@ metadata:
|
|
|
116
117
|
homepage_uri: https://github.com/jannewaren/markawesome
|
|
117
118
|
source_code_uri: https://github.com/jannewaren/markawesome/tree/main
|
|
118
119
|
changelog_uri: https://github.com/jannewaren/markawesome/blob/main/CHANGELOG.md
|
|
120
|
+
rubygems_mfa_required: 'true'
|
|
119
121
|
rdoc_options: []
|
|
120
122
|
require_paths:
|
|
121
123
|
- lib
|
|
@@ -130,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
130
132
|
- !ruby/object:Gem::Version
|
|
131
133
|
version: '0'
|
|
132
134
|
requirements: []
|
|
133
|
-
rubygems_version:
|
|
135
|
+
rubygems_version: 4.0.3
|
|
134
136
|
specification_version: 4
|
|
135
137
|
summary: Framework-agnostic Markdown to Web Awesome component transformer
|
|
136
138
|
test_files: []
|