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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e7ee0dc5c92fb7e9774b22ff56152d96d47a5eaf16da5f5a0181eda5e61aae57
4
- data.tar.gz: 321ab99ac9d4b1bc7355a898271bcaf14aa8edf7bbeeafebf3b307d2ce0dd041
3
+ metadata.gz: adad1d3e9850c4d1f82b4cbe73eb56ac9c82c413e66ea0adb1291794a3a9a25a
4
+ data.tar.gz: 07b7741a6e96f7031d38d1e10d6ca6a0314d5edd05341222f1f7d7964904d1c4
5
5
  SHA512:
6
- metadata.gz: de9cc698634f6e9d741317fa48a98d46619869949ca4641615a08c55504e253ecd4c292e3cc4a6b89ff7c394f277b5671c22c58b38e1f5839b751147a2cc6c5a
7
- data.tar.gz: 736f49201d6323f34cc9c4ca198db98a8cd34ff0c4ec891649aafce06360f497ea797a697f623ee93f23807461334e05fa5df1fe76b76d39019fdc363c3b109d
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: !!!variant?\ncontent\n!!!
8
- # Alternative syntax: :::wa-badge variant?\ncontent\n:::
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 = /^!!!(brand|success|neutral|warning|danger)?\n(.*?)\n!!!/m
14
- alternative_regex = /^:::wa-badge\s*(brand|success|neutral|warning|danger)?\n(.*?)\n:::/m
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 |variant, badge_content|
30
+ transform_proc = proc do |params_string, badge_content|
18
31
  badge_content = badge_content.strip
19
32
 
20
- build_badge_html(badge_content, variant)
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, variant)
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&nbsp;')
41
56
 
42
- "<wa-badge#{variant_attr}>#{badge_html}</wa-badge>"
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: %%%variant?\ncontent\n%%%
8
- # Alternative syntax: :::wa-button variant?\ncontent\n:::
9
- # Variants: brand, success, neutral, warning, danger
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 = /^%%%(brand|success|neutral|warning|danger)?\n(.*?)\n%%%/m
17
- alternative_regex = /^:::wa-button\s*(brand|success|neutral|warning|danger)?\n(.*?)\n:::/m
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 |variant, button_content|
40
+ transform_proc = proc do |params_string, button_content|
21
41
  button_content = button_content.strip
22
42
 
23
- build_button_html(button_content, variant)
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, variant)
35
- variant_attr = variant ? " variant=\"#{variant}\"" : ''
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&nbsp;')
51
85
 
52
- "<wa-button#{variant_attr} href=\"#{link_url}\">#{button_html}</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&nbsp;')
60
94
 
61
- "<wa-button#{variant_attr}>#{button_html}</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
- # Define both regex patterns
13
- primary_regex = /^:::(info|success|neutral|warning|danger)\n(.*?)\n:::/m
14
- alternative_regex = /^:::wa-callout\s+(info|success|neutral|warning|danger)\n(.*?)\n:::/m
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
- # Define shared transformation logic
17
- transform_proc = proc do |variant, inner_content|
18
- attrs = callout_attributes(variant)
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
- element_tag = "wa-callout#{attrs[:additional_params]}"
21
- html_content = "#{attrs[:inner_prepend]}#{markdown_to_html(inner_content)}"
34
+ icon_html = icon_for(actual_variant)
35
+ html_content = "#{icon_html}#{markdown_to_html(inner_content)}"
22
36
 
23
- "<#{element_tag}>#{html_content}</wa-callout>"
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 callout_attributes(variant)
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
- case variant
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
- info: 'circle-info',
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: ===appearance?\ncontent\n===
8
- # Alternative syntax: :::wa-card appearance?\ncontent\n:::
9
- # Appearances: outlined (default), filled, filled-outlined, plain, accent
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 = /^===(outlined|filled|filled-outlined|plain|accent)?\n(.*?)\n===/m
14
- alternative_regex = /^:::wa-card\s*(outlined|filled|filled-outlined|plain|accent)?\n(.*?)\n:::/m
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 |appearance_param, card_content|
25
+ transform_proc = proc do |params_string, card_content|
18
26
  card_content = card_content.strip
19
27
 
20
- appearance = normalize_appearance(appearance_param)
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, appearance)
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, appearance)
85
- attributes = []
86
- attributes << "appearance=\"#{appearance}\"" if appearance != 'outlined'
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
- attributes << 'with-media' if parts[:media]
90
- attributes << 'with-header' if parts[:header]
91
- attributes << 'with-footer' if parts[:footer]
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 = attributes.empty? ? '' : " #{attributes.join(' ')}"
97
+ attr_string = html_attrs.empty? ? '' : " #{html_attrs.join(' ')}"
94
98
 
95
99
  html_parts = []
96
100
 
97
101
  # Media slot
98
- html_parts << "<img slot=\"media\" src=\"#{parts[:media][:src]}\" alt=\"#{parts[:media][:alt]}\">" if parts[:media]
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
- html_parts << "<div slot=\"footer\"><wa-button href=\"#{parts[:footer][:href]}\">#{parts[:footer][:text]}</wa-button></div>" if parts[:footer]
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), and CSS properties (scroll-hint:value, aspect-ratio:value, slide-gap:value)
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 (key:value)
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: <<<\ncontent\n<<<
8
- # Alternative syntax: :::wa-copy-button\ncontent\n:::
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
- # :::wa-copy-button
16
- # Copy this text
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 = /^<<<\n(.*?)\n<<</m
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
- build_copy_button_html(copy_content)
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('"', '&quot;').gsub("'", '&#39;')
42
75
 
43
- "<wa-copy-button value=\"#{escaped_content}\"></wa-copy-button>"
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 from the params string
23
- appearance_param, icon_placement_param = parse_parameters(params_string)
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(appearance_param)
26
- icon_placement = normalize_icon_placement(icon_placement_param)
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
- "<wa-details appearance='#{appearance_class}' icon-placement='#{icon_placement}'>" \
31
- "<span slot='summary'>#{summary_html}</span>" \
32
- "#{details_html}</wa-details>"
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 parse_parameters(params_string)
44
- return [nil, nil] if params_string.nil? || params_string.strip.empty?
62
+ def extract_name_value(params_string)
63
+ return nil if params_string.nil? || params_string.strip.empty?
45
64
 
46
- # Split by whitespace and extract known parameters
65
+ # Extract name parameter (format: name:value)
47
66
  tokens = params_string.strip.split(/\s+/)
48
-
49
- appearance_options = %w[outlined filled filled-outlined plain]
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
- # Params: light-dismiss and optional width (e.g., 500px, 50vw, 40em)
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
- tokens = params_string.strip.split(/\s+/)
56
-
57
- light_dismiss = tokens.include?('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 (last token with CSS units)
60
- width = nil
61
- tokens.reverse_each do |token|
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(/<wa-comparison[^>]*>.*?<\/wa-comparison>/m) do |match|
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: ++++++placement?\n+++tab1\ncontent\n+++\n+++tab2\ncontent\n+++\n++++++
8
- # Alternative syntax: :::wa-tabs placement?\n+++tab1\ncontent\n+++\n+++tab2\ncontent\n+++\n:::
9
- # Placements: top (default), bottom, start, end
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
- primary_regex = /^\+{6}(top|bottom|start|end)?\n((\+\+\+ [^\n]+\n.*?\n\+\+\+\n?)+)\+{6}/m
14
- alternative_regex = /^:::wa-tabs\s*(top|bottom|start|end)?\n((\+\+\+ [^\n]+\n.*?\n\+\+\+\n?)+):::/m
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 |placement, tabs_block, _third_capture|
18
- placement ||= 'top'
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
- "<wa-tab-group placement=\"#{placement}\">#{tabs.join}#{tab_panels.join}</wa-tab-group>"
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: @@@variant?\ncontent\n@@@
8
- # Inline syntax: @@@ variant? content @@@
9
- # Alternative syntax: :::wa-tag variant?\ncontent\n:::
10
- # Variants: brand, success, neutral, warning, danger
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
- primary_regex = /^@@@(brand|success|neutral|warning|danger)?\r?\n(.*?)\r?\n@@@/m
16
- alternative_regex = /^:::wa-tag\s*(brand|success|neutral|warning|danger)?\r?\n(.*?)\r?\n:::/m
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
- inline_regex = /@@@\s*(brand|success|neutral|warning|danger)?\s+([^@\r\n]+?)\s+@@@/
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 |variant, tag_content|
41
+ transform_proc = proc do |params, tag_content|
23
42
  tag_content = tag_content.strip
24
43
 
25
- build_tag_html(tag_content, variant)
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
- captures = matchdata.captures
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, variant)
46
- variant_attr = variant ? " variant=\"#{variant}\"" : ''
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#{variant_attr}>#{tag_html}</wa-tag>"
102
+ "<wa-tag#{attrs_string}>#{tag_html}</wa-tag>"
53
103
  end
54
104
  end
55
105
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Markawesome
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
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.2.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: 3.6.9
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: []