markawesome 0.13.0 → 0.15.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/plain_markdown_renderer.rb +8 -4
- data/lib/markawesome/transformer.rb +5 -0
- data/lib/markawesome/transformers/accordion_transformer.rb +138 -0
- data/lib/markawesome/transformers/tooltip_transformer.rb +184 -0
- data/lib/markawesome/transformers.rb +2 -0
- data/lib/markawesome/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a3f542a573c1e649456cd84852aec0549b8a4029c23953a947d1966e86f6730c
|
|
4
|
+
data.tar.gz: 2db687f1c67cfa97ebab7b29eecf7152e5396d98bd614eb04c99e115ad59b267
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58e6b4bcd8f1ae4a82059def1b169b8cd553cdc23ebc3d83299533ddb3be3ee153f0eb23ec104deea2740b4cdcf5d3e581f3f2aeb47d1ce1322bed875e4f5412
|
|
7
|
+
data.tar.gz: 34c077b57d27f96b8d8dae36a387044fe9dc263d74f2260455fb5bcc9ed8e25ae92ecd23aedeaaa03e88edd9e5a98138ea0d4d6ec15798b8a53badbc1eb267aa
|
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.15.0] - 2026-06-24
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- New `TooltipTransformer` producing Web Awesome `<wa-tooltip>` — inline contextual help on hover/focus for glossary terms and inline definitions. Declarative, zero-JS, and static-site-safe: the tooltip is attached to a focusable anchor `<span>` via an auto-generated `for`/`id` pair, so authors write only the trigger term and the tip text.
|
|
12
|
+
- **Inline syntax** (primary): `(((anchor term >>> tip text)))`. The `(((` delimiter is unused by any other transformer, is not Markdown- or Liquid-special, and never appears in normal prose; the `>>>` separator matches the popover/dialog/details convention (anchor first, tip after `>>>`).
|
|
13
|
+
- **Block alternative**: `:::wa-tooltip placement? distance:N?` / anchor / `>>>` / tip / `:::`, for consistency with every other component's `:::wa-*` form.
|
|
14
|
+
- **Attributes** (order-independent leading tokens): `placement` (`top` default, `bottom`, `left`, `right`) and `distance:N`. Mirrors `PopoverTransformer`'s surface minus `link`/`without-arrow` (WA `<wa-tooltip>` has no `without-arrow` boolean — arrow size is CSS-only).
|
|
15
|
+
- **Tip content** is plain text (HTML-escaped), with literal `\n` rendered as `<br>` — the same surface as the popover's *inline* form. Tooltips hold brief text, so there is no Markdown body.
|
|
16
|
+
- **Emitted markup**: a focusable `<span id="tooltip-…" tabindex="0" class="ma-tooltip-anchor" style="text-decoration: underline dotted; cursor: help;">` anchor followed by `<wa-tooltip for="tooltip-…" placement="…" [distance="…"]>`. The anchor is focusable so keyboard/AT users get the tip (tooltips fire on focus too); `ma-tooltip-anchor` is a styling hook (mirrors popover's `ma-popover-trigger`).
|
|
17
|
+
- **IDs** are auto-wired via `tooltip-<first 8 of MD5(anchor+tip)>`, with `-2`/`-3` dedup suffixes for repeated identical tooltips — the same scheme as `PopoverTransformer`. Runs immediately after `PopoverTransformer` (early, after `LayoutTransformer`) so inline tooltips inside cards/callouts/details are transformed before Kramdown escapes `(((`/`>>>`.
|
|
18
|
+
- **Plain-markdown degradation** (`render_as_markdown`, used for `.md` endpoints / llms.txt): both forms degrade to `**anchor** (tip)`, mirroring the popover inline degradation.
|
|
19
|
+
- Aligned placements (`top-start`, …), `show-delay`/`hide-delay`, and a rich-content block form are explicit future follow-ups, not part of v1.
|
|
20
|
+
|
|
21
|
+
## [0.14.0] - 2026-06-24
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- New `AccordionTransformer` producing Web Awesome `<wa-accordion>` / `<wa-accordion-item>` — a multi-section collapsible container for FAQs, docs sections, and "show more" content (the grouped, mutually-exclusive-capable sibling of `details`). The `/` delimiter is the container/item marker: container fence `//////` (6×), item fence `///` (3×) with an explicit `///` close, mirroring tabs (`++++++`/`+++`). An alternative `:::wa-accordion … :::` form is also accepted.
|
|
26
|
+
- **Container line** (bare tokens, order-independent, rightmost-wins): `appearance` (`outlined` default, `filled`, `filled-outlined`, `plain`), `mode` (`multiple`, `single`, `single-collapsible`), `icon-placement` (`start`, `end`), and a value-form `heading:N` → `heading-level="N"` where `N` is `1`–`6` or `none` (out-of-range values fall back to omitting the attribute). `appearance` is always emitted (defaulting to `outlined`); `mode`, `icon-placement`, and `heading-level` are emitted only when specified.
|
|
27
|
+
- **Item header** (`/// [expanded] [disabled] [icon:name] Label`): leading `expanded`/`disabled` flags become the matching boolean attributes; `icon:name` becomes a `<wa-icon slot="icon" name="…">` first child; the remaining text becomes the HTML-escaped `label`. Item bodies are full markdown and may contain other components.
|
|
28
|
+
- **Plain-markdown degradation** (`render_as_markdown`, used for `.md` endpoints / llms.txt): each item degrades to a `### Label` heading followed by its body, mirroring tabs.
|
|
29
|
+
- `wa-accordion` is **experimental** in Web Awesome (added in 3.7), but `expanded`, `disabled`, and the `mode`/`appearance`/`icon-placement`/`heading-level` attributes are all declarative and static-site-safe — no JavaScript required. The interactive `expandAll()`/`collapseAll()` controls and rich/HTML `label` slot are out of scope for v1; item labels are plain text. The `--wa-accordion-divider-color` custom property removed in WA 3.9.0 is intentionally not exposed.
|
|
30
|
+
|
|
7
31
|
## [0.13.0] - 2026-06-20
|
|
8
32
|
|
|
9
33
|
### Added
|
|
@@ -24,9 +24,9 @@ module Markawesome
|
|
|
24
24
|
# plugin during boot to override the default rendering for a single
|
|
25
25
|
# component without forking the gem.
|
|
26
26
|
#
|
|
27
|
-
# @param component [Symbol] one of :callout, :badge, :button,
|
|
28
|
-
# :carousel, :comparison, :copy_button, :details, :dialog, :icon,
|
|
29
|
-
# :image_dialog, :layout, :popover, :tabs, :tag.
|
|
27
|
+
# @param component [Symbol] one of :accordion, :callout, :badge, :button,
|
|
28
|
+
# :card, :carousel, :comparison, :copy_button, :details, :dialog, :icon,
|
|
29
|
+
# :image_dialog, :layout, :popover, :tabs, :tag, :tooltip.
|
|
30
30
|
# @yield [content, options] Proc that receives the full source content
|
|
31
31
|
# and the renderer options; returns the content with that component
|
|
32
32
|
# syntax replaced.
|
|
@@ -45,6 +45,7 @@ module Markawesome
|
|
|
45
45
|
PIPELINE = %i[
|
|
46
46
|
layout
|
|
47
47
|
popover
|
|
48
|
+
tooltip
|
|
48
49
|
badge
|
|
49
50
|
button
|
|
50
51
|
callout
|
|
@@ -58,11 +59,13 @@ module Markawesome
|
|
|
58
59
|
icon
|
|
59
60
|
tag
|
|
60
61
|
tabs
|
|
62
|
+
accordion
|
|
61
63
|
].freeze
|
|
62
64
|
|
|
63
65
|
TRANSFORMER_MAP = {
|
|
64
66
|
layout: LayoutTransformer,
|
|
65
67
|
popover: PopoverTransformer,
|
|
68
|
+
tooltip: TooltipTransformer,
|
|
66
69
|
badge: BadgeTransformer,
|
|
67
70
|
button: ButtonTransformer,
|
|
68
71
|
callout: CalloutTransformer,
|
|
@@ -75,7 +78,8 @@ module Markawesome
|
|
|
75
78
|
dialog: DialogTransformer,
|
|
76
79
|
icon: IconTransformer,
|
|
77
80
|
tag: TagTransformer,
|
|
78
|
-
tabs: TabsTransformer
|
|
81
|
+
tabs: TabsTransformer,
|
|
82
|
+
accordion: AccordionTransformer
|
|
79
83
|
}.freeze
|
|
80
84
|
|
|
81
85
|
def self.process(content, options = {})
|
|
@@ -12,6 +12,7 @@ module Markawesome
|
|
|
12
12
|
|
|
13
13
|
content = LayoutTransformer.transform(content)
|
|
14
14
|
content = PopoverTransformer.transform(content)
|
|
15
|
+
content = TooltipTransformer.transform(content)
|
|
15
16
|
content = BadgeTransformer.transform(content)
|
|
16
17
|
content = ButtonTransformer.transform(content)
|
|
17
18
|
content = CalloutTransformer.transform(content)
|
|
@@ -32,6 +33,10 @@ module Markawesome
|
|
|
32
33
|
content = TagTransformer.transform(content)
|
|
33
34
|
content = TabsTransformer.transform(content)
|
|
34
35
|
|
|
36
|
+
# Accordion runs last so item bodies may contain other already-transformed
|
|
37
|
+
# components (same reason tabs runs near the end).
|
|
38
|
+
content = AccordionTransformer.transform(content)
|
|
39
|
+
|
|
35
40
|
CodeBlockProtector.restore(content, tokens)
|
|
36
41
|
end
|
|
37
42
|
end
|
|
@@ -0,0 +1,138 @@
|
|
|
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 accordion syntax into wa-accordion / wa-accordion-item elements
|
|
9
|
+
# Primary syntax:
|
|
10
|
+
# //////appearance? mode? icon-placement? heading:N?
|
|
11
|
+
# /// [expanded] [disabled] [icon:name] Label text
|
|
12
|
+
# item body markdown
|
|
13
|
+
# ///
|
|
14
|
+
# //////
|
|
15
|
+
# Alternative syntax: :::wa-accordion ...same items... :::
|
|
16
|
+
#
|
|
17
|
+
# Container attributes (bare, order-independent, rightmost-wins):
|
|
18
|
+
# - appearance: outlined (default), filled, filled-outlined, plain
|
|
19
|
+
# - mode: multiple (default), single, single-collapsible
|
|
20
|
+
# - icon-placement: start, end (default emitted only when given)
|
|
21
|
+
# - heading:N -> heading-level="N" where N is 1-6 or "none"
|
|
22
|
+
# Item tokens (leading flags, then the rest of the line is the label):
|
|
23
|
+
# - expanded -> expanded; disabled -> disabled
|
|
24
|
+
# - icon:name -> <wa-icon slot="icon" name="name"></wa-icon> as first child
|
|
25
|
+
#
|
|
26
|
+
# wa-accordion is experimental in Web Awesome (added 3.7). expanded/disabled
|
|
27
|
+
# and the mode/appearance/icon-placement attributes are all static-safe.
|
|
28
|
+
class AccordionTransformer < BaseTransformer
|
|
29
|
+
CONTAINER_ATTRIBUTES = {
|
|
30
|
+
appearance: %w[outlined filled filled-outlined plain],
|
|
31
|
+
mode: %w[multiple single single-collapsible],
|
|
32
|
+
icon_placement: %w[start end]
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
ICON_SLOTS = {
|
|
36
|
+
default: 'icon',
|
|
37
|
+
slots: %w[icon],
|
|
38
|
+
slot_map: { 'icon' => 'icon' }
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
ITEM_FLAGS = %w[expanded disabled].freeze
|
|
42
|
+
|
|
43
|
+
PRIMARY_REGEX = %r{^/{6}([^\n]*)\n((?:/{3} [^\n]+\n.*?\n/{3}\n?)+)/{6}}m
|
|
44
|
+
ALTERNATIVE_REGEX = %r{^:::wa-accordion\s*([^\n]*)\n((?:/{3} [^\n]+\n.*?\n/{3}\n?)+):::}m
|
|
45
|
+
ITEM_REGEX = %r{^/{3} ([^\n]+)\n(.*?)\n/{3}}m
|
|
46
|
+
|
|
47
|
+
def self.transform(content)
|
|
48
|
+
transform_proc = proc do |params_string, items_block, _third|
|
|
49
|
+
attributes = AttributeParser.parse(params_string.to_s.strip, CONTAINER_ATTRIBUTES)
|
|
50
|
+
heading_level = extract_heading_level(params_string)
|
|
51
|
+
|
|
52
|
+
attr_parts = ["appearance=\"#{normalize_appearance(attributes[:appearance])}\""]
|
|
53
|
+
attr_parts << "mode=\"#{attributes[:mode]}\"" if attributes[:mode]
|
|
54
|
+
attr_parts << "icon-placement=\"#{attributes[:icon_placement]}\"" if attributes[:icon_placement]
|
|
55
|
+
attr_parts << "heading-level=\"#{heading_level}\"" if heading_level
|
|
56
|
+
|
|
57
|
+
"<wa-accordion #{attr_parts.join(' ')}>#{build_items(items_block)}</wa-accordion>"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
patterns = dual_syntax_patterns(PRIMARY_REGEX, ALTERNATIVE_REGEX, transform_proc)
|
|
61
|
+
apply_multiple_patterns(content, patterns)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.render_as_markdown(content, _options = {})
|
|
65
|
+
transform_proc = proc do |_params_string, items_block, _third|
|
|
66
|
+
items_block.scan(ITEM_REGEX).map do |header, body|
|
|
67
|
+
_flags, label = parse_item_flags_and_label(IconSlotParser.parse(header, ICON_SLOTS)[:remaining])
|
|
68
|
+
"### #{label}\n\n#{body.strip}"
|
|
69
|
+
end.join("\n\n")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
patterns = dual_syntax_patterns(PRIMARY_REGEX, ALTERNATIVE_REGEX, transform_proc)
|
|
73
|
+
apply_multiple_patterns(content, patterns)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class << self
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def build_items(items_block)
|
|
80
|
+
items_block.scan(ITEM_REGEX).map do |header, body|
|
|
81
|
+
icon_result = IconSlotParser.parse(header, ICON_SLOTS)
|
|
82
|
+
flags, label = parse_item_flags_and_label(icon_result[:remaining])
|
|
83
|
+
|
|
84
|
+
item_attrs = ["label=\"#{escape_html(label)}\""]
|
|
85
|
+
item_attrs << 'expanded' if flags.include?('expanded')
|
|
86
|
+
item_attrs << 'disabled' if flags.include?('disabled')
|
|
87
|
+
|
|
88
|
+
icon_html = IconSlotParser.to_html(icon_result[:icons], ICON_SLOTS[:slot_map])
|
|
89
|
+
body_html = markdown_to_html(body.strip)
|
|
90
|
+
|
|
91
|
+
"<wa-accordion-item #{item_attrs.join(' ')}>#{icon_html}#{body_html}</wa-accordion-item>"
|
|
92
|
+
end.join
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Consume leading expanded/disabled tokens; the remainder is the label.
|
|
96
|
+
def parse_item_flags_and_label(remaining)
|
|
97
|
+
tokens = remaining.to_s.strip.split(/\s+/)
|
|
98
|
+
flags = []
|
|
99
|
+
flags << tokens.shift while tokens.any? && ITEM_FLAGS.include?(tokens.first)
|
|
100
|
+
[flags, tokens.join(' ')]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def extract_heading_level(params_string)
|
|
104
|
+
return nil if params_string.nil? || params_string.strip.empty?
|
|
105
|
+
|
|
106
|
+
token = params_string.strip.split(/\s+/).find { |t| t.start_with?('heading:') }
|
|
107
|
+
return nil unless token
|
|
108
|
+
|
|
109
|
+
value = token.sub(/^heading:/, '')
|
|
110
|
+
return value if value == 'none' || value.match?(/\A[1-6]\z/)
|
|
111
|
+
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def normalize_appearance(appearance_param)
|
|
116
|
+
case appearance_param
|
|
117
|
+
when 'filled'
|
|
118
|
+
'filled'
|
|
119
|
+
when 'filled-outlined'
|
|
120
|
+
'filled-outlined'
|
|
121
|
+
when 'plain'
|
|
122
|
+
'plain'
|
|
123
|
+
else
|
|
124
|
+
'outlined'
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def escape_html(text)
|
|
129
|
+
text.to_s
|
|
130
|
+
.gsub('&', '&')
|
|
131
|
+
.gsub('<', '<')
|
|
132
|
+
.gsub('>', '>')
|
|
133
|
+
.gsub('"', '"')
|
|
134
|
+
.gsub("'", ''')
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'digest'
|
|
4
|
+
require_relative 'base_transformer'
|
|
5
|
+
require_relative '../attribute_parser'
|
|
6
|
+
|
|
7
|
+
module Markawesome
|
|
8
|
+
# Transforms tooltip syntax into wa-tooltip elements attached to a focusable
|
|
9
|
+
# anchor span via an auto-generated for/id pair. Declarative, zero-JS, and
|
|
10
|
+
# static-site-safe — great for glossary terms and inline definitions.
|
|
11
|
+
#
|
|
12
|
+
# Inline syntax (primary): (((anchor term >>> tip text)))
|
|
13
|
+
# Alternative block syntax: :::wa-tooltip params\nanchor\n>>>\ntip\n:::
|
|
14
|
+
#
|
|
15
|
+
# Params: space-separated tokens (order doesn't matter)
|
|
16
|
+
# Placement: top (default), bottom, left, right
|
|
17
|
+
# Distance: distance:N (e.g., distance:10)
|
|
18
|
+
#
|
|
19
|
+
# Tip content is plain text (HTML-escaped), with literal `\n` rendered as
|
|
20
|
+
# <br> — the same surface as the popover's inline form. Tooltips hold brief
|
|
21
|
+
# text, so there is no markdown body.
|
|
22
|
+
class TooltipTransformer < BaseTransformer
|
|
23
|
+
TOOLTIP_ATTRIBUTES = {
|
|
24
|
+
placement: %w[top bottom left right]
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
# Inline regex (single-line, no newlines allowed): capture 1 = params+anchor,
|
|
28
|
+
# capture 2 = tip text.
|
|
29
|
+
INLINE_REGEX = /\(\(\([ \t]*([^\r\n]*?)[ \t]*>>>[ \t]*([^\r\n]+?)[ \t]*\)\)\)/
|
|
30
|
+
# Block alternative regex (multiline): capture 1 = params, 2 = anchor, 3 = tip.
|
|
31
|
+
ALTERNATIVE_REGEX = /^:::wa-tooltip([^\n]*)$\n(.*?)\n^>>>$\n(.*?)\n^:::$/m
|
|
32
|
+
|
|
33
|
+
def self.transform(content)
|
|
34
|
+
# Tracks ID base usage within this transform call so repeated tooltips
|
|
35
|
+
# (same anchor + tip) get disambiguated suffixes instead of colliding on
|
|
36
|
+
# the page.
|
|
37
|
+
seen_ids = Hash.new(0)
|
|
38
|
+
|
|
39
|
+
inline_transform = {
|
|
40
|
+
regex: INLINE_REGEX,
|
|
41
|
+
block: proc do |_match, matchdata|
|
|
42
|
+
combined = matchdata[1]
|
|
43
|
+
tip_text = matchdata[2].strip
|
|
44
|
+
|
|
45
|
+
params_string, anchor_text = parse_inline_anchor_and_params(combined)
|
|
46
|
+
placement, distance = parse_parameters(params_string)
|
|
47
|
+
|
|
48
|
+
tooltip_id = generate_tooltip_id(anchor_text, tip_text, seen_ids)
|
|
49
|
+
|
|
50
|
+
build_tooltip_html(tooltip_id, anchor_text, tip_text,
|
|
51
|
+
{ placement: placement, distance: distance })
|
|
52
|
+
end
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
alternative_transform = {
|
|
56
|
+
regex: ALTERNATIVE_REGEX,
|
|
57
|
+
block: proc do |_match, matchdata|
|
|
58
|
+
params_string = matchdata[1]
|
|
59
|
+
anchor_text = matchdata[2].strip
|
|
60
|
+
tip_text = matchdata[3].strip
|
|
61
|
+
|
|
62
|
+
placement, distance = parse_parameters(params_string)
|
|
63
|
+
|
|
64
|
+
tooltip_id = generate_tooltip_id(anchor_text, tip_text, seen_ids)
|
|
65
|
+
|
|
66
|
+
build_tooltip_html(tooltip_id, anchor_text, tip_text,
|
|
67
|
+
{ placement: placement, distance: distance })
|
|
68
|
+
end
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Inline pattern first to avoid conflicts with the block pattern.
|
|
72
|
+
apply_multiple_patterns(content, [inline_transform, alternative_transform])
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.render_as_markdown(content, _options = {})
|
|
76
|
+
inline_transform = {
|
|
77
|
+
regex: INLINE_REGEX,
|
|
78
|
+
block: proc do |_match, matchdata|
|
|
79
|
+
combined = matchdata[1]
|
|
80
|
+
tip_text = matchdata[2].strip
|
|
81
|
+
_params, anchor_text = parse_inline_anchor_and_params(combined)
|
|
82
|
+
"**#{anchor_text}** (#{tip_text})"
|
|
83
|
+
end
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
alternative_transform = {
|
|
87
|
+
regex: ALTERNATIVE_REGEX,
|
|
88
|
+
block: proc do |_match, matchdata|
|
|
89
|
+
anchor_text = matchdata[2].strip
|
|
90
|
+
tip_text = matchdata[3].strip
|
|
91
|
+
"**#{anchor_text}** (#{tip_text})"
|
|
92
|
+
end
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
apply_multiple_patterns(content, [inline_transform, alternative_transform])
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class << self
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def parse_parameters(params_string)
|
|
102
|
+
return ['top', nil] if params_string.nil? || params_string.strip.empty?
|
|
103
|
+
|
|
104
|
+
attributes = AttributeParser.parse(params_string, TOOLTIP_ATTRIBUTES)
|
|
105
|
+
placement = attributes[:placement] || 'top'
|
|
106
|
+
|
|
107
|
+
# Look for distance:N parameter (rightmost-wins)
|
|
108
|
+
tokens = params_string.strip.split(/\s+/)
|
|
109
|
+
distance_token = tokens.reverse.find { |token| token.match?(/^distance:\d+$/) }
|
|
110
|
+
distance = distance_token&.sub('distance:', '')
|
|
111
|
+
|
|
112
|
+
[placement, distance]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def generate_tooltip_id(anchor_text, tip_text, seen_ids)
|
|
116
|
+
hash_input = "#{anchor_text}#{tip_text}"
|
|
117
|
+
hash = Digest::MD5.hexdigest(hash_input)
|
|
118
|
+
base = "tooltip-#{hash[0..7]}"
|
|
119
|
+
occurrence = seen_ids[base] += 1
|
|
120
|
+
occurrence == 1 ? base : "#{base}-#{occurrence}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def parse_inline_anchor_and_params(combined_string)
|
|
124
|
+
tokens = combined_string.strip.split(/\s+/)
|
|
125
|
+
param_tokens = []
|
|
126
|
+
anchor_tokens = []
|
|
127
|
+
found_anchor = false
|
|
128
|
+
|
|
129
|
+
tokens.each do |token|
|
|
130
|
+
if !found_anchor && tooltip_param?(token)
|
|
131
|
+
param_tokens << token
|
|
132
|
+
else
|
|
133
|
+
found_anchor = true
|
|
134
|
+
anchor_tokens << token
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
anchor_text = anchor_tokens.join(' ')
|
|
139
|
+
|
|
140
|
+
# If no anchor text remains, treat entire string as the anchor (no params)
|
|
141
|
+
if anchor_text.empty?
|
|
142
|
+
['', combined_string.strip]
|
|
143
|
+
else
|
|
144
|
+
[param_tokens.join(' '), anchor_text]
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def tooltip_param?(token)
|
|
149
|
+
TOOLTIP_ATTRIBUTES.any? { |_attr, values| values.include?(token) } ||
|
|
150
|
+
token.match?(/^distance:\d+$/)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def build_tooltip_html(tooltip_id, anchor_text, tip_text, options)
|
|
154
|
+
anchor_content = escape_html(anchor_text)
|
|
155
|
+
tip_escaped = escape_html(tip_text).gsub('\n', '<br>')
|
|
156
|
+
|
|
157
|
+
tooltip_attrs = ["for=\"#{tooltip_id}\""]
|
|
158
|
+
tooltip_attrs << "placement=\"#{options[:placement]}\""
|
|
159
|
+
tooltip_attrs << "distance=\"#{options[:distance]}\"" if options[:distance]
|
|
160
|
+
|
|
161
|
+
anchor = build_anchor(tooltip_id, anchor_content)
|
|
162
|
+
|
|
163
|
+
"#{anchor}<wa-tooltip #{tooltip_attrs.join(' ')}>#{tip_escaped}</wa-tooltip>"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Focusable span so keyboard/AT users get the tip (tooltips fire on focus
|
|
167
|
+
# too). The dotted underline + help cursor mirror the link-style popover
|
|
168
|
+
# trigger; the ma-tooltip-anchor class is a styling hook for consumers.
|
|
169
|
+
def build_anchor(tooltip_id, anchor_content)
|
|
170
|
+
style = 'text-decoration: underline dotted; cursor: help;'
|
|
171
|
+
"<span id=\"#{tooltip_id}\" tabindex=\"0\" class=\"ma-tooltip-anchor\" " \
|
|
172
|
+
"style=\"#{style}\">#{anchor_content}</span>"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def escape_html(text)
|
|
176
|
+
text.gsub('&', '&')
|
|
177
|
+
.gsub('<', '<')
|
|
178
|
+
.gsub('>', '>')
|
|
179
|
+
.gsub('"', '"')
|
|
180
|
+
.gsub("'", ''')
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# This file makes it easy to require all transformers at once
|
|
5
5
|
|
|
6
6
|
require_relative 'transformers/base_transformer'
|
|
7
|
+
require_relative 'transformers/accordion_transformer'
|
|
7
8
|
require_relative 'transformers/badge_transformer'
|
|
8
9
|
require_relative 'transformers/button_transformer'
|
|
9
10
|
require_relative 'transformers/callout_transformer'
|
|
@@ -19,3 +20,4 @@ require_relative 'transformers/layout_transformer'
|
|
|
19
20
|
require_relative 'transformers/popover_transformer'
|
|
20
21
|
require_relative 'transformers/tabs_transformer'
|
|
21
22
|
require_relative 'transformers/tag_transformer'
|
|
23
|
+
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.15.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Janne Waren
|
|
@@ -84,6 +84,7 @@ files:
|
|
|
84
84
|
- lib/markawesome/plain_markdown_renderer.rb
|
|
85
85
|
- lib/markawesome/transformer.rb
|
|
86
86
|
- lib/markawesome/transformers.rb
|
|
87
|
+
- lib/markawesome/transformers/accordion_transformer.rb
|
|
87
88
|
- lib/markawesome/transformers/badge_transformer.rb
|
|
88
89
|
- lib/markawesome/transformers/base_transformer.rb
|
|
89
90
|
- lib/markawesome/transformers/button_transformer.rb
|
|
@@ -100,6 +101,7 @@ files:
|
|
|
100
101
|
- lib/markawesome/transformers/popover_transformer.rb
|
|
101
102
|
- lib/markawesome/transformers/tabs_transformer.rb
|
|
102
103
|
- lib/markawesome/transformers/tag_transformer.rb
|
|
104
|
+
- lib/markawesome/transformers/tooltip_transformer.rb
|
|
103
105
|
- lib/markawesome/version.rb
|
|
104
106
|
- markawesome.gemspec
|
|
105
107
|
homepage: https://github.com/jannewaren/markawesome
|