markawesome 0.19.0 → 0.20.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 +13 -0
- data/README.md +18 -0
- data/lib/markawesome/plain_markdown_renderer.rb +7 -3
- data/lib/markawesome/transformer.rb +7 -0
- data/lib/markawesome/transformers/accordion_transformer.rb +1 -1
- data/lib/markawesome/transformers/divider_transformer.rb +125 -0
- data/lib/markawesome/transformers/random_content_transformer.rb +1 -1
- data/lib/markawesome/transformers/stepper_transformer.rb +201 -0
- data/lib/markawesome/transformers.rb +2 -0
- data/lib/markawesome/version.rb +1 -1
- 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: '09defacfcfa437cfd7c5406b0705161597633880fac312488a7fa29c3055754d'
|
|
4
|
+
data.tar.gz: 369cd9e0d91dc6a423501367f98e2e54391a04aaebc4cf9da9c79d1c3a4267aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fa42c7657b0abac3554309763a38a75a8c6bc08d39b8a28e85b9fa0b57ea931207705efa0ff194c097582996986871a382b09872508dad2cfe325fc957350efa
|
|
7
|
+
data.tar.gz: 4a509a216fd0ce3aea3238e85dbf8cfea8fc60c7d55e9209e7e9915862b933708afd896a9582cc8c630bbbaf41caf759285f8687a5bff8df45c386d7a058617a
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.20.0] - 2026-10-09
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Labeled divider (`<wa-divider>` label, Web Awesome 3.13.0+).** New `DividerTransformer` with a mirrored one-line shorthand, `--- label ---`, and a `:::wa-divider params?` alternative whose body is the label (empty for a bare divider). Leading tokens are consumed from the left: `start`/`center`/`end` → `label-placement`, `horizontal`/`vertical` → `orientation`, `icon:name` → a `<wa-icon>` before the label. The rest is the label, rendered as inline Markdown. Output: `<wa-divider orientation="…" label-placement="…"><wa-icon name="…"></wa-icon> Label</wa-divider>`, attributes only when given. Plain `---` and `--- ---` are untouched and stay `<hr>`, and so is any line made only of `|`, `:`, `-` and whitespace, which keeps pipe-less GFM table separators such as `--- | ---` intact. The plain-Markdown renderer degrades it to `---` followed by the label as its own paragraph (icon dropped), with a leading blank line so the rule can't turn a preceding line into a setext heading. Known limitation, shared with every transformer: only backtick code fences are protected, so a `--- x ---` line inside a `~~~` fence is transformed.
|
|
14
|
+
- **Display-only stepper (`<wa-stepper>` / `<wa-step>`, Web Awesome 3.14.0, experimental).** New `StepperTransformer`: a `>>>>>> params?` fence (or `:::wa-stepper params?`) wrapping a Markdown task list. Each unindented list line is a step: `[x]`/`[X]` → `completed`; leading `active`, `loading`, `disabled`, a variant (`neutral`/`brand`/`success`/`warning`/`danger`), an attention (`none`/`pulse`/`bounce`) and `icon:name` (→ `slot="icon"`) are flags; the rest is the label as inline Markdown. Lines indented two or more spaces under a step are joined into `<span slot="description">`. Steps are named `step-1…N` and the container's `active` is the step flagged `active` (last wins), else the first incomplete step, else the last. Container params: `horizontal`/`vertical`/`auto` → `orientation`, `label:"…"` → `label` (HTML-escaped). Interactivity (`clickable`, `linear`, step invokers) is intentionally omitted — the steps have no content panels to navigate between. The `>>>>>>` fence never collides with the `^>>>$` separator of details/dialog/popover/tooltip/random-content. Plain-Markdown degrade: an ordered task list keeping `[x]`/`[ ]`, the current step in bold, and descriptions as indented continuation lines.
|
|
15
|
+
- Both run **first** in the pipeline (right after code-block protection, before layouts), so a divider or stepper can sit inside layouts, cards, callouts, tabs, accordion and dialog bodies — e.g. `--- vertical vs ---` between two buttons in a `::::cluster`. Same position in `PlainMarkdownRenderer::PIPELINE` (new `:divider` / `:stepper` override keys).
|
|
16
|
+
- Manifest coverage now also guards `wa-divider` `orientation`/`label-placement`, `wa-stepper` `orientation` and `wa-step` `variant`/`attention` (`DIVIDER_ATTRIBUTES`, `STEPPER_ATTRIBUTES`, `STEP_ATTRIBUTES`).
|
|
17
|
+
|
|
18
|
+
### Internal
|
|
19
|
+
|
|
20
|
+
- **Refreshed the Web Awesome manifest fixture to 3.14.0** (September 24th, 2026; covers 3.13.0 too). `rake wa:manifest[3.14.0]` regenerated `spec/fixtures/webawesome-enums.json` and `WA_VERSION` moved to `3.14.0`. The diff is **purely additive** — three new tags (`wa-step`, `wa-stepper`, `wa-tag-input`) and a `label-placement` enum on `wa-divider` — with **zero DRIFT and zero GAP** against the enums we accept, so no transformer constant changed. `<wa-accordion>` and `<wa-random-content>` are now **stable** in Web Awesome, so the "experimental" wording was dropped from their transformer comments. Fixture remains byte-identical to `markawesome-js`'s copy. Example-site CDN pins bumped to 3.14.0 in `jekyll-webawesome` and `eleventy-plugin-webawesome`.
|
|
21
|
+
|
|
9
22
|
## [0.19.0] - 2026-08-26
|
|
10
23
|
|
|
11
24
|
### Fixed
|
data/README.md
CHANGED
|
@@ -25,9 +25,11 @@ Used as the transformation engine for the [jekyll-webawesome](https://github.com
|
|
|
25
25
|
| **Copy Button** | `<<<placement? tooltip:full\|copy\|none?` | `:::wa-copy-button placement? tooltip:full\|copy\|none?` | `<wa-copy-button value="content" tooltip-placement="top" tooltip="copy"></wa-copy-button>` |
|
|
26
26
|
| **Details** | `^^^appearance? icon-placement?` | `:::wa-details appearance? icon-placement?` | `<wa-details>content</wa-details>` |
|
|
27
27
|
| **Dialog** | `???params?` | `:::wa-dialog params?` | `<wa-dialog>` with trigger button and content |
|
|
28
|
+
| **Divider** | `--- label ---` (e.g. `--- start icon:star New ---`) | `:::wa-divider params?` (body is the label) | `<wa-divider label-placement="start"><wa-icon name="star"></wa-icon> New</wa-divider>` |
|
|
28
29
|
| **Icon** | `$$$icon-name` | `:::wa-icon icon-name` | `<wa-icon name="icon-name"></wa-icon>` |
|
|
29
30
|
| **Image Dialog** | `` | — | Wraps images in clickable `<wa-dialog>` overlays |
|
|
30
31
|
| **Popover** | `&&¶ms?` or inline `&&&trigger >>> content&&&` | `:::wa-popover params?` | `<wa-popover>` with trigger button and content |
|
|
32
|
+
| **Stepper** | `>>>>>> params?` wrapping a task list | `:::wa-stepper params?` | `<wa-stepper active="step-2"><wa-step name="step-1" completed>…</wa-step>…</wa-stepper>` |
|
|
31
33
|
| **Tab Group** | `++++++` | `:::wa-tabs` | `<wa-tab-group><wa-tab>content</wa-tab></wa-tab-group>` |
|
|
32
34
|
| **Tag** | `@@@brand` | `:::wa-tag brand` | `<wa-tag variant="brand">content</wa-tag>` |
|
|
33
35
|
|
|
@@ -38,6 +40,22 @@ Used as the transformation engine for the [jekyll-webawesome](https://github.com
|
|
|
38
40
|
> listener (e.g. a single global handler that removes `event.target`). The markup
|
|
39
41
|
> we emit is valid either way.
|
|
40
42
|
|
|
43
|
+
> **Labeled divider:** `--- label ---` needs text between the mirrored dashes,
|
|
44
|
+
> so a plain `---` stays an `<hr>`. Leading `start`/`center`/`end`
|
|
45
|
+
> (label placement), `horizontal`/`vertical` (orientation) and `icon:name`
|
|
46
|
+
> tokens are consumed from the left; the rest is the label, as inline Markdown.
|
|
47
|
+
> A line made only of `|`, `:` and `-` (a pipe-less table separator) is left
|
|
48
|
+
> alone. Use `:::wa-divider` when a label must start with one of those words.
|
|
49
|
+
|
|
50
|
+
> **Stepper (display-only):** each unindented list line is a step. `[x]` marks
|
|
51
|
+
> it completed; leading `active`, `loading`, `disabled`, a variant, an attention
|
|
52
|
+
> (`pulse`/`bounce`/`none`) and `icon:name` are flags; lines indented under a
|
|
53
|
+
> step become its description. The active step is the one flagged `active`,
|
|
54
|
+
> else the first incomplete step, else the last. Container params are
|
|
55
|
+
> `horizontal`/`vertical`/`auto` and `label:"…"`. `wa-stepper` is experimental
|
|
56
|
+
> in Web Awesome 3.14.0, and its interactive features (`clickable`, `linear`)
|
|
57
|
+
> are deliberately not exposed.
|
|
58
|
+
|
|
41
59
|
## Layout Utilities
|
|
42
60
|
|
|
43
61
|
Layout utilities use `::::` (quadruple colon) syntax to wrap content in CSS layout containers. Inner content is not markdown-converted, so component `:::` syntax inside layouts works normally.
|
|
@@ -25,9 +25,9 @@ module Markawesome
|
|
|
25
25
|
# component without forking the gem.
|
|
26
26
|
#
|
|
27
27
|
# @param component [Symbol] one of :accordion, :callout, :badge, :button,
|
|
28
|
-
# :card, :carousel, :comparison, :copy_button, :details, :dialog,
|
|
29
|
-
# :
|
|
30
|
-
# :tooltip.
|
|
28
|
+
# :card, :carousel, :comparison, :copy_button, :details, :dialog,
|
|
29
|
+
# :divider, :icon, :image_dialog, :layout, :popover, :random_content,
|
|
30
|
+
# :stepper, :tabs, :tag, :tooltip, :tree.
|
|
31
31
|
# @yield [content, options] Proc that receives the full source content
|
|
32
32
|
# and the renderer options; returns the content with that component
|
|
33
33
|
# syntax replaced.
|
|
@@ -44,6 +44,8 @@ module Markawesome
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
PIPELINE = %i[
|
|
47
|
+
divider
|
|
48
|
+
stepper
|
|
47
49
|
layout
|
|
48
50
|
popover
|
|
49
51
|
tooltip
|
|
@@ -68,6 +70,8 @@ module Markawesome
|
|
|
68
70
|
].freeze
|
|
69
71
|
|
|
70
72
|
TRANSFORMER_MAP = {
|
|
73
|
+
divider: DividerTransformer,
|
|
74
|
+
stepper: StepperTransformer,
|
|
71
75
|
layout: LayoutTransformer,
|
|
72
76
|
popover: PopoverTransformer,
|
|
73
77
|
tooltip: TooltipTransformer,
|
|
@@ -10,6 +10,13 @@ module Markawesome
|
|
|
10
10
|
def self.process(content, options = {})
|
|
11
11
|
content, tokens = CodeBlockProtector.protect(content)
|
|
12
12
|
|
|
13
|
+
# Divider and stepper run FIRST: their content is inline-only (a label,
|
|
14
|
+
# a list of one-line steps), so emitting them before any container lets
|
|
15
|
+
# them sit inside layouts, cards, callouts, tabs, accordion and dialog
|
|
16
|
+
# bodies — e.g. `--- vertical vs ---` inside a `::::cluster`.
|
|
17
|
+
content = DividerTransformer.transform(content)
|
|
18
|
+
content = StepperTransformer.transform(content)
|
|
19
|
+
|
|
13
20
|
content = LayoutTransformer.transform(content)
|
|
14
21
|
content = PopoverTransformer.transform(content)
|
|
15
22
|
content = TooltipTransformer.transform(content)
|
|
@@ -23,7 +23,7 @@ module Markawesome
|
|
|
23
23
|
# - expanded -> expanded; disabled -> disabled
|
|
24
24
|
# - icon:name -> <wa-icon slot="icon" name="name"></wa-icon> as first child
|
|
25
25
|
#
|
|
26
|
-
# wa-accordion
|
|
26
|
+
# wa-accordion was added in Web Awesome 3.7 (stable since 3.13). expanded/disabled
|
|
27
27
|
# and the mode/appearance/icon-placement attributes are all static-safe.
|
|
28
28
|
class AccordionTransformer < BaseTransformer
|
|
29
29
|
CONTAINER_ATTRIBUTES = {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
5
|
+
require_relative '../icon_slot_parser'
|
|
6
|
+
|
|
7
|
+
module Markawesome
|
|
8
|
+
# Transforms labeled-divider syntax into <wa-divider> elements with a label in
|
|
9
|
+
# the default slot (Web Awesome 3.13.0+).
|
|
10
|
+
#
|
|
11
|
+
# Primary syntax (one line, mirrored dashes):
|
|
12
|
+
# --- or ---
|
|
13
|
+
# --- start Release notes ---
|
|
14
|
+
# --- end icon:star New ---
|
|
15
|
+
# --- icon:quote-left ---
|
|
16
|
+
# --- vertical vs ---
|
|
17
|
+
# Alternative syntax (the body is the label, and may be empty):
|
|
18
|
+
# :::wa-divider start icon:star
|
|
19
|
+
# Release **notes**
|
|
20
|
+
# :::
|
|
21
|
+
#
|
|
22
|
+
# Leading tokens (consumed from the left until the first non-token word):
|
|
23
|
+
# - start, center, end -> label-placement
|
|
24
|
+
# - horizontal, vertical -> orientation
|
|
25
|
+
# - icon:name -> <wa-icon name="name"> before the label
|
|
26
|
+
# The rest of the line is the label, rendered as inline Markdown. To start a
|
|
27
|
+
# label with one of those words, use the alternative syntax.
|
|
28
|
+
#
|
|
29
|
+
# A plain `---` (and `--- ---`) is left alone and stays an <hr>. So is a line
|
|
30
|
+
# made only of `|`, `:`, `-` and whitespace (e.g. a pipe-less GFM table
|
|
31
|
+
# separator such as `--- | ---`).
|
|
32
|
+
class DividerTransformer < BaseTransformer
|
|
33
|
+
DIVIDER_ATTRIBUTES = {
|
|
34
|
+
orientation: %w[horizontal vertical],
|
|
35
|
+
label_placement: %w[start center end]
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
# Content slot: emits <wa-icon name="..."> with no slot attribute.
|
|
39
|
+
ICON_SLOTS = {
|
|
40
|
+
default: 'content',
|
|
41
|
+
slots: %w[content],
|
|
42
|
+
slot_map: { 'content' => 'content' }
|
|
43
|
+
}.freeze
|
|
44
|
+
|
|
45
|
+
PRIMARY_REGEX = /^---[ \t]+(\S[^\n]*?)[ \t]+---[ \t]*$/
|
|
46
|
+
ALTERNATIVE_REGEX = /^:::wa-divider[ \t]*([^\n]*)\n(?:(.*?)\n)?:::[ \t]*$/m
|
|
47
|
+
TABLE_SEPARATOR_REGEX = /\A[|:\-\s]*\z/
|
|
48
|
+
|
|
49
|
+
def self.transform(content)
|
|
50
|
+
content = content.gsub(PRIMARY_REGEX) do |match|
|
|
51
|
+
inner = Regexp.last_match(1)
|
|
52
|
+
next match if inner.match?(TABLE_SEPARATOR_REGEX)
|
|
53
|
+
|
|
54
|
+
lead, label = split_leading_tokens(inner)
|
|
55
|
+
build_html(lead, label)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
content.gsub(ALTERNATIVE_REGEX) do
|
|
59
|
+
params_string = Regexp.last_match(1)
|
|
60
|
+
body = Regexp.last_match(2).to_s
|
|
61
|
+
build_html(params_string, body.strip)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Degrade to a thematic break followed by the label as its own paragraph
|
|
66
|
+
# (icon dropped). The leading blank line stops `---` from turning a
|
|
67
|
+
# preceding paragraph line into a setext heading.
|
|
68
|
+
def self.render_as_markdown(content, _options = {})
|
|
69
|
+
content = content.gsub(PRIMARY_REGEX) do |match|
|
|
70
|
+
inner = Regexp.last_match(1)
|
|
71
|
+
next match if inner.match?(TABLE_SEPARATOR_REGEX)
|
|
72
|
+
|
|
73
|
+
_lead, label = split_leading_tokens(inner)
|
|
74
|
+
markdown_break(label)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
content.gsub(ALTERNATIVE_REGEX) do
|
|
78
|
+
markdown_break(Regexp.last_match(2).to_s.strip)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class << self
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
# Consume leading placement/orientation/icon tokens; the rest is the label.
|
|
86
|
+
def split_leading_tokens(inner)
|
|
87
|
+
tokens = inner.strip.split(/\s+/)
|
|
88
|
+
lead = []
|
|
89
|
+
lead << tokens.shift while tokens.any? && leading_token?(tokens.first)
|
|
90
|
+
[lead.join(' '), tokens.join(' ')]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def leading_token?(token)
|
|
94
|
+
token.start_with?('icon:') || DIVIDER_ATTRIBUTES.values.any? { |values| values.include?(token) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def build_html(params_string, label)
|
|
98
|
+
attributes = AttributeParser.parse(params_string, DIVIDER_ATTRIBUTES)
|
|
99
|
+
icon_result = IconSlotParser.parse(params_string, ICON_SLOTS)
|
|
100
|
+
|
|
101
|
+
attr_parts = []
|
|
102
|
+
attr_parts << "orientation=\"#{attributes[:orientation]}\"" if attributes[:orientation]
|
|
103
|
+
attr_parts << "label-placement=\"#{attributes[:label_placement]}\"" if attributes[:label_placement]
|
|
104
|
+
attrs_string = attr_parts.empty? ? '' : " #{attr_parts.join(' ')}"
|
|
105
|
+
|
|
106
|
+
icon_html = IconSlotParser.to_html(icon_result[:icons], ICON_SLOTS[:slot_map])
|
|
107
|
+
label_html = render_inline(label)
|
|
108
|
+
separator = !icon_html.empty? && !label_html.empty? ? ' ' : ''
|
|
109
|
+
|
|
110
|
+
"<wa-divider#{attrs_string}>#{icon_html}#{separator}#{label_html}</wa-divider>"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Inline Markdown: render, then drop the wrapping <p> (the badge idiom).
|
|
114
|
+
def render_inline(text)
|
|
115
|
+
return '' if text.empty?
|
|
116
|
+
|
|
117
|
+
markdown_to_html(text).strip.sub(%r{\A<p>(.*)</p>\z}m, '\1')
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def markdown_break(label)
|
|
121
|
+
label.empty? ? "\n---" : "\n---\n\n#{label}\n"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -4,7 +4,7 @@ require_relative 'base_transformer'
|
|
|
4
4
|
require_relative '../attribute_parser'
|
|
5
5
|
|
|
6
6
|
module Markawesome
|
|
7
|
-
# Transforms random-content syntax into Web Awesome's
|
|
7
|
+
# Transforms random-content syntax into Web Awesome's
|
|
8
8
|
# <wa-random-content> element, which shows one or more of its direct element
|
|
9
9
|
# children at random (optionally rotating them) and hides the rest — all in
|
|
10
10
|
# WA's own runtime, so the author writes zero JavaScript. Ideal for a "little
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
5
|
+
require_relative '../icon_slot_parser'
|
|
6
|
+
|
|
7
|
+
module Markawesome
|
|
8
|
+
# Transforms stepper syntax into a display-only <wa-stepper> / <wa-step>
|
|
9
|
+
# progress indicator, written as a Markdown task list.
|
|
10
|
+
#
|
|
11
|
+
# Primary syntax:
|
|
12
|
+
# >>>>>> vertical label:"Checkout progress"
|
|
13
|
+
# - [x] Cart
|
|
14
|
+
# - [x] Shipping
|
|
15
|
+
# Standard, 3–5 days
|
|
16
|
+
# - [ ] pulse icon:credit-card Payment
|
|
17
|
+
# - [ ] Review
|
|
18
|
+
# >>>>>>
|
|
19
|
+
# Alternative syntax: :::wa-stepper params? ...same list... :::
|
|
20
|
+
#
|
|
21
|
+
# Container params:
|
|
22
|
+
# - horizontal, vertical, auto -> orientation
|
|
23
|
+
# - label:"text" -> label (accessible name)
|
|
24
|
+
# Steps are unindented list lines (`-`, `*` or `+`):
|
|
25
|
+
# - [x] / [X] marks the step completed; [ ] or no box means not completed
|
|
26
|
+
# - leading flags: active, loading, disabled, a variant (neutral, brand,
|
|
27
|
+
# success, warning, danger), an attention (none, pulse, bounce), and
|
|
28
|
+
# icon:name (slotted into the step's icon slot)
|
|
29
|
+
# - the rest of the line is the label, rendered as inline Markdown
|
|
30
|
+
# - lines indented by two or more spaces under a step are joined into its
|
|
31
|
+
# description slot, rendered as inline Markdown
|
|
32
|
+
#
|
|
33
|
+
# Steps are named step-1..N and the container's `active` points at the step
|
|
34
|
+
# flagged `active` (last one wins), else the first incomplete step, else the
|
|
35
|
+
# last step. Interactivity (`clickable`, `linear`, step invokers) is omitted
|
|
36
|
+
# on purpose: these steps have no content panels, so there is nothing to
|
|
37
|
+
# navigate between without app JavaScript.
|
|
38
|
+
class StepperTransformer < BaseTransformer
|
|
39
|
+
STEPPER_ATTRIBUTES = {
|
|
40
|
+
orientation: %w[horizontal vertical auto]
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
STEP_ATTRIBUTES = {
|
|
44
|
+
variant: %w[neutral brand success warning danger],
|
|
45
|
+
attention: %w[none pulse bounce]
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
STEP_FLAGS = %w[active loading disabled].freeze
|
|
49
|
+
|
|
50
|
+
ICON_SLOTS = {
|
|
51
|
+
default: 'icon',
|
|
52
|
+
slots: %w[icon],
|
|
53
|
+
slot_map: { 'icon' => 'icon' }
|
|
54
|
+
}.freeze
|
|
55
|
+
|
|
56
|
+
PRIMARY_REGEX = /^>{6}([^\n]*)\n(.*?)\n>{6}[ \t]*$/m
|
|
57
|
+
ALTERNATIVE_REGEX = /^:::wa-stepper[ \t]*([^\n]*)\n(.*?)\n:::[ \t]*$/m
|
|
58
|
+
LABEL_REGEX = /label:(?:"([^"]*)"|'([^']*)')/
|
|
59
|
+
STEP_LINE_REGEX = /\A[-*+][ \t]+(.*)\z/
|
|
60
|
+
CONTINUATION_REGEX = /\A[ \t]{2,}(\S.*)\z/
|
|
61
|
+
CHECKBOX_REGEX = /\A\[([ xX])\](?:[ \t]+(.*))?\z/
|
|
62
|
+
|
|
63
|
+
def self.transform(content)
|
|
64
|
+
transform_proc = proc do |params_string, body|
|
|
65
|
+
label, params_string = extract_label(params_string.to_s)
|
|
66
|
+
attributes = AttributeParser.parse(params_string, STEPPER_ATTRIBUTES)
|
|
67
|
+
steps = parse_steps(body)
|
|
68
|
+
active = active_index(steps)
|
|
69
|
+
|
|
70
|
+
attr_parts = []
|
|
71
|
+
attr_parts << "orientation=\"#{attributes[:orientation]}\"" if attributes[:orientation]
|
|
72
|
+
attr_parts << "label=\"#{escape_html(label)}\"" unless label.empty?
|
|
73
|
+
attr_parts << "active=\"step-#{active + 1}\"" if active
|
|
74
|
+
attrs_string = attr_parts.empty? ? '' : " #{attr_parts.join(' ')}"
|
|
75
|
+
|
|
76
|
+
steps_html = steps.each_with_index.map { |step, index| build_step(step, index) }.join
|
|
77
|
+
"<wa-stepper#{attrs_string}>#{steps_html}</wa-stepper>"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
patterns = dual_syntax_patterns(PRIMARY_REGEX, ALTERNATIVE_REGEX, transform_proc)
|
|
81
|
+
apply_multiple_patterns(content, patterns)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Degrade to an ordered task list: completed steps keep [x], the current
|
|
85
|
+
# step is bold, and a description becomes an indented continuation line.
|
|
86
|
+
def self.render_as_markdown(content, _options = {})
|
|
87
|
+
transform_proc = proc do |_params_string, body|
|
|
88
|
+
steps = parse_steps(body)
|
|
89
|
+
active = active_index(steps)
|
|
90
|
+
|
|
91
|
+
steps.each_with_index.map do |step, index|
|
|
92
|
+
marker = "#{index + 1}. "
|
|
93
|
+
label = index == active && !step[:label].empty? ? "**#{step[:label]}**" : step[:label]
|
|
94
|
+
line = "#{marker}[#{step[:completed] ? 'x' : ' '}]"
|
|
95
|
+
line += " #{label}" unless label.empty?
|
|
96
|
+
line += "\n#{' ' * marker.length}#{step[:description]}" unless step[:description].empty?
|
|
97
|
+
line
|
|
98
|
+
end.join("\n")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
patterns = dual_syntax_patterns(PRIMARY_REGEX, ALTERNATIVE_REGEX, transform_proc)
|
|
102
|
+
apply_multiple_patterns(content, patterns)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class << self
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
# Pull out label:"…" (or label:'…') before the bare tokens are parsed, so
|
|
109
|
+
# words inside the label can't be read as orientation tokens.
|
|
110
|
+
def extract_label(params_string)
|
|
111
|
+
match = LABEL_REGEX.match(params_string)
|
|
112
|
+
return ['', params_string] unless match
|
|
113
|
+
|
|
114
|
+
[(match[1] || match[2]).strip, params_string.sub(LABEL_REGEX, ' ')]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def parse_steps(body)
|
|
118
|
+
steps = []
|
|
119
|
+
body.to_s.each_line do |line|
|
|
120
|
+
line = line.chomp
|
|
121
|
+
if (match = STEP_LINE_REGEX.match(line))
|
|
122
|
+
steps << parse_step(match[1])
|
|
123
|
+
elsif (match = CONTINUATION_REGEX.match(line)) && steps.any?
|
|
124
|
+
steps.last[:description_lines] << match[1].strip
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
steps.each { |step| step[:description] = step.delete(:description_lines).join(' ') }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def parse_step(raw)
|
|
131
|
+
completed = false
|
|
132
|
+
if (match = CHECKBOX_REGEX.match(raw.strip))
|
|
133
|
+
completed = match[1] != ' '
|
|
134
|
+
raw = match[2].to_s
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
tokens = raw.strip.split(/\s+/)
|
|
138
|
+
lead = []
|
|
139
|
+
lead << tokens.shift while tokens.any? && leading_token?(tokens.first)
|
|
140
|
+
lead_string = lead.join(' ')
|
|
141
|
+
|
|
142
|
+
{
|
|
143
|
+
completed: completed,
|
|
144
|
+
flags: lead & STEP_FLAGS,
|
|
145
|
+
attributes: AttributeParser.parse(lead_string, STEP_ATTRIBUTES),
|
|
146
|
+
icons: IconSlotParser.parse(lead_string, ICON_SLOTS)[:icons],
|
|
147
|
+
label: tokens.join(' '),
|
|
148
|
+
description_lines: []
|
|
149
|
+
}
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def leading_token?(token)
|
|
153
|
+
token.start_with?('icon:') ||
|
|
154
|
+
STEP_FLAGS.include?(token) ||
|
|
155
|
+
STEP_ATTRIBUTES.values.any? { |values| values.include?(token) }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# The explicitly active step (last wins), else the first incomplete one,
|
|
159
|
+
# else the last step; nil when there are no steps.
|
|
160
|
+
def active_index(steps)
|
|
161
|
+
return nil if steps.empty?
|
|
162
|
+
|
|
163
|
+
explicit = steps.rindex { |step| step[:flags].include?('active') }
|
|
164
|
+
return explicit if explicit
|
|
165
|
+
|
|
166
|
+
steps.index { |step| !step[:completed] } || (steps.length - 1)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def build_step(step, index)
|
|
170
|
+
attr_parts = ["name=\"step-#{index + 1}\""]
|
|
171
|
+
attr_parts << 'completed' if step[:completed]
|
|
172
|
+
attr_parts << 'loading' if step[:flags].include?('loading')
|
|
173
|
+
attr_parts << 'disabled' if step[:flags].include?('disabled')
|
|
174
|
+
attr_parts << "variant=\"#{step[:attributes][:variant]}\"" if step[:attributes][:variant]
|
|
175
|
+
attr_parts << "attention=\"#{step[:attributes][:attention]}\"" if step[:attributes][:attention]
|
|
176
|
+
|
|
177
|
+
icon_html = IconSlotParser.to_html(step[:icons], ICON_SLOTS[:slot_map])
|
|
178
|
+
description = render_inline(step[:description])
|
|
179
|
+
description_html = description.empty? ? '' : "<span slot=\"description\">#{description}</span>"
|
|
180
|
+
|
|
181
|
+
"<wa-step #{attr_parts.join(' ')}>#{icon_html}#{render_inline(step[:label])}#{description_html}</wa-step>"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Inline Markdown: render, then drop the wrapping <p> (the badge idiom).
|
|
185
|
+
def render_inline(text)
|
|
186
|
+
return '' if text.empty?
|
|
187
|
+
|
|
188
|
+
markdown_to_html(text).strip.sub(%r{\A<p>(.*)</p>\z}m, '\1')
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def escape_html(text)
|
|
192
|
+
text.to_s
|
|
193
|
+
.gsub('&', '&')
|
|
194
|
+
.gsub('<', '<')
|
|
195
|
+
.gsub('>', '>')
|
|
196
|
+
.gsub('"', '"')
|
|
197
|
+
.gsub("'", ''')
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -15,11 +15,13 @@ require_relative 'transformers/copy_button_transformer'
|
|
|
15
15
|
require_relative 'transformers/date_transformer'
|
|
16
16
|
require_relative 'transformers/details_transformer'
|
|
17
17
|
require_relative 'transformers/dialog_transformer'
|
|
18
|
+
require_relative 'transformers/divider_transformer'
|
|
18
19
|
require_relative 'transformers/icon_transformer'
|
|
19
20
|
require_relative 'transformers/image_dialog_transformer'
|
|
20
21
|
require_relative 'transformers/layout_transformer'
|
|
21
22
|
require_relative 'transformers/popover_transformer'
|
|
22
23
|
require_relative 'transformers/random_content_transformer'
|
|
24
|
+
require_relative 'transformers/stepper_transformer'
|
|
23
25
|
require_relative 'transformers/tabs_transformer'
|
|
24
26
|
require_relative 'transformers/tag_transformer'
|
|
25
27
|
require_relative 'transformers/tooltip_transformer'
|
data/lib/markawesome/version.rb
CHANGED
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.20.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Janne Waren
|
|
@@ -96,11 +96,13 @@ files:
|
|
|
96
96
|
- lib/markawesome/transformers/date_transformer.rb
|
|
97
97
|
- lib/markawesome/transformers/details_transformer.rb
|
|
98
98
|
- lib/markawesome/transformers/dialog_transformer.rb
|
|
99
|
+
- lib/markawesome/transformers/divider_transformer.rb
|
|
99
100
|
- lib/markawesome/transformers/icon_transformer.rb
|
|
100
101
|
- lib/markawesome/transformers/image_dialog_transformer.rb
|
|
101
102
|
- lib/markawesome/transformers/layout_transformer.rb
|
|
102
103
|
- lib/markawesome/transformers/popover_transformer.rb
|
|
103
104
|
- lib/markawesome/transformers/random_content_transformer.rb
|
|
105
|
+
- lib/markawesome/transformers/stepper_transformer.rb
|
|
104
106
|
- lib/markawesome/transformers/tabs_transformer.rb
|
|
105
107
|
- lib/markawesome/transformers/tag_transformer.rb
|
|
106
108
|
- lib/markawesome/transformers/tooltip_transformer.rb
|
|
@@ -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: 4.0.
|
|
135
|
+
rubygems_version: 4.0.20
|
|
134
136
|
specification_version: 4
|
|
135
137
|
summary: Framework-agnostic Markdown to Web Awesome component transformer
|
|
136
138
|
test_files: []
|