hotdocs 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/app/assets/javascript/controllers/fetcher_controller.js +29 -0
- data/app/assets/javascript/controllers/search_controller.js +14 -7
- data/app/assets/stylesheets/hotdocs/application.css +57 -20
- data/app/helpers/hotdocs/application_helper.rb +12 -1
- data/app/views/layouts/hotdocs/application.html.erb +107 -82
- data/config/importmap.rb +1 -1
- data/lib/hotdocs/engine.rb +1 -2
- data/lib/hotdocs/kramdown_alerts.rb +92 -0
- data/lib/hotdocs/markdown.rb +21 -24
- data/lib/hotdocs/version.rb +1 -1
- data/lib/install/install.rb +163 -182
- data/lib/tasks/hotdocs_tasks.rake +3 -1
- metadata +47 -4
- data/lib/hotdocs/markdown.mjs +0 -177
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a04eac67f8afb072c5b6ef87e98bac8316659fe4da338d64b1fdb85b66334fd8
|
4
|
+
data.tar.gz: 542ea09084b1e53da0657368a3a8b4c559dbf9e0a6305be13f1ae2726ddf04f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b8fea60fa67ca77c4b4655e928429483b0a77779843c64863fce8e8f49dfaba102896e54c1b36151a19ad107bf5540fab6b5663727d791e71db27081acefedd
|
7
|
+
data.tar.gz: fcbfa4cf654c14cb2eb023c3905399479bf784c1dd349f99ff2357e3d7687f342a230b61eceb5b37c8f8ecf444b8b460608be5af07affeede0186c6707e3a95b
|
data/README.md
CHANGED
@@ -20,8 +20,8 @@ HotDocs is a set of optimized Rails components & tools for writing docs:
|
|
20
20
|
| Embed docs in an existing Rails app | ✅ | ❌ | ❌ |
|
21
21
|
| Standalone docs | ✅ | ✅ | ✅ |
|
22
22
|
| Styled components you can customize | ✅ | ✅ | ✅ |
|
23
|
-
| Markdown (with syntax highlight & themes) |
|
24
|
-
| Static export |
|
23
|
+
| Markdown (with syntax highlight & themes) | ✅ | ✅ | ✅ |
|
24
|
+
| Static export | ✅ | ✅ | ✅ |
|
25
25
|
| Search | ✅ | 🔌 | 🔌 |
|
26
26
|
| Light / Dark | 🔜 ✅ | 🔌 | ✅ |
|
27
27
|
| Open source | ✅ | ✅ | ✅ |
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static values = {
|
5
|
+
host: String,
|
6
|
+
path: String,
|
7
|
+
fallback: String,
|
8
|
+
};
|
9
|
+
|
10
|
+
connect() {
|
11
|
+
if (!this.element.id) {
|
12
|
+
throw new Error("Element must have an id.");
|
13
|
+
}
|
14
|
+
this.element.innerHTML = this.fallbackValue || "Loading...";
|
15
|
+
this.element.style.visibility = "";
|
16
|
+
this.fetchElement();
|
17
|
+
}
|
18
|
+
|
19
|
+
fetchElement() {
|
20
|
+
fetch(`${this.hostValue}${this.pathValue}`)
|
21
|
+
.then((response) => response.text())
|
22
|
+
.then((text) => {
|
23
|
+
const parser = new DOMParser();
|
24
|
+
const doc = parser.parseFromString(text, "text/html");
|
25
|
+
const fetchedElement = doc.querySelector(`#${this.element.id}`);
|
26
|
+
this.element.innerHTML = fetchedElement.innerHTML;
|
27
|
+
});
|
28
|
+
}
|
29
|
+
}
|
@@ -10,7 +10,7 @@ export default class extends Controller {
|
|
10
10
|
|
11
11
|
disconnect() {
|
12
12
|
document.removeEventListener("keydown", this.keydownOpen);
|
13
|
-
document.removeEventListener("click", this.
|
13
|
+
document.removeEventListener("click", this._clickClose);
|
14
14
|
}
|
15
15
|
|
16
16
|
open() {
|
@@ -20,6 +20,11 @@ export default class extends Controller {
|
|
20
20
|
this.searchTarget.showModal();
|
21
21
|
}
|
22
22
|
|
23
|
+
close() {
|
24
|
+
this.searchTarget.close();
|
25
|
+
document.removeEventListener("click", this._clickClose);
|
26
|
+
}
|
27
|
+
|
23
28
|
search = debounce(this._search, 200);
|
24
29
|
|
25
30
|
_allowOpening() {
|
@@ -32,13 +37,15 @@ export default class extends Controller {
|
|
32
37
|
document.addEventListener("keydown", this.keydownOpen);
|
33
38
|
}
|
34
39
|
|
35
|
-
|
36
|
-
this.
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
+
_clickClose = (event) => {
|
41
|
+
if (!this.searchTarget.open) return;
|
42
|
+
if (this.dialogTarget.contains(event.target)) return;
|
43
|
+
this.close();
|
44
|
+
};
|
40
45
|
|
41
|
-
|
46
|
+
_allowClosing() {
|
47
|
+
document.removeEventListener("click", this._clickClose);
|
48
|
+
document.addEventListener("click", this._clickClose);
|
42
49
|
}
|
43
50
|
|
44
51
|
_initSearch() {
|
@@ -73,7 +73,7 @@ body {
|
|
73
73
|
--nav-background-color: white;
|
74
74
|
--nav-link-active-color: var(--link-active-color);
|
75
75
|
--nav-link-color: var(--link-color);
|
76
|
-
--nav-padding-horizontal:
|
76
|
+
--nav-padding-horizontal: 1.5rem;
|
77
77
|
--nav-padding-vertical: 0.5rem;
|
78
78
|
--nav-shadow: 0 1px 2px 0 #0000001a;
|
79
79
|
--nav-title-color: #1c1e21;
|
@@ -304,14 +304,21 @@ body:has(.search:open), body:has(.search[open]) {
|
|
304
304
|
bottom: 0;
|
305
305
|
color: var(--search-text-color);
|
306
306
|
height: 100vh;
|
307
|
+
height: 100dvh;
|
307
308
|
left: 0;
|
308
309
|
max-height: 100vh;
|
310
|
+
height: 100dvh;
|
309
311
|
max-width: 100vw;
|
310
|
-
|
312
|
+
max-width: 100dvw;
|
311
313
|
position: fixed;
|
312
314
|
right: 0;
|
313
315
|
top: 0;
|
314
316
|
width: 100vw;
|
317
|
+
width: 100dvw;
|
318
|
+
|
319
|
+
@media (min-width: 64rem) {
|
320
|
+
padding-inline: 1rem;
|
321
|
+
}
|
315
322
|
}
|
316
323
|
|
317
324
|
::backdrop {
|
@@ -319,28 +326,57 @@ body:has(.search:open), body:has(.search[open]) {
|
|
319
326
|
}
|
320
327
|
|
321
328
|
.search__dialog {
|
322
|
-
overflow: auto;
|
323
329
|
background-color: var(--search-background-color);
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
max-
|
330
|
+
height: 100vh;
|
331
|
+
height: 100dvh;
|
332
|
+
max-height: 100vh;
|
333
|
+
max-height: 100dvh;
|
334
|
+
max-width: 100vw;
|
335
|
+
max-width: 100dvw;
|
336
|
+
overflow: auto;
|
328
337
|
padding: 1rem;
|
329
|
-
width:
|
338
|
+
width: 100vw;
|
339
|
+
width: 100dvw;
|
340
|
+
|
341
|
+
@media (min-width: 64rem) {
|
342
|
+
border-radius: 0.375rem;
|
343
|
+
height: auto;
|
344
|
+
margin: 60px auto auto;
|
345
|
+
max-height: calc(100vh - 120px);
|
346
|
+
max-height: calc(100dvh - 120px);
|
347
|
+
max-width: 800px;
|
348
|
+
width: auto;
|
349
|
+
}
|
350
|
+
}
|
351
|
+
|
352
|
+
.search__header {
|
353
|
+
align-items: center;
|
354
|
+
display: flex;
|
355
|
+
gap: 1rem;
|
330
356
|
}
|
331
357
|
|
332
358
|
.search__input {
|
333
359
|
background-color: var(--search-excerpt-background-color);
|
334
360
|
border: 1px solid #808080;
|
335
361
|
border-radius: 0.2rem;
|
362
|
+
flex: 1 0 auto;
|
336
363
|
padding: 0.3rem 0.5rem;
|
337
|
-
width: 100%;
|
338
364
|
|
339
365
|
&:focus-visible {
|
340
366
|
outline: solid 2px #0077ff;
|
341
367
|
}
|
342
368
|
}
|
343
369
|
|
370
|
+
.search__dismiss {
|
371
|
+
color: var(--search-text-color);
|
372
|
+
height: 1rem;
|
373
|
+
width: 1rem;
|
374
|
+
|
375
|
+
@media (min-width: 64rem) {
|
376
|
+
display: none;
|
377
|
+
}
|
378
|
+
}
|
379
|
+
|
344
380
|
.search__result {
|
345
381
|
margin-top: 1.5rem;
|
346
382
|
|
@@ -688,7 +724,7 @@ body:has(.search:open), body:has(.search[open]) {
|
|
688
724
|
.footer {
|
689
725
|
background-color: var(--footer-background-color);
|
690
726
|
flex: 0 0 auto;
|
691
|
-
padding: 4rem
|
727
|
+
padding: 4rem var(--nav-padding-horizontal);
|
692
728
|
}
|
693
729
|
|
694
730
|
.footer__sections {
|
@@ -726,6 +762,7 @@ body:has(.search:open), body:has(.search[open]) {
|
|
726
762
|
}
|
727
763
|
|
728
764
|
.credits {
|
765
|
+
padding-block: 1rem;
|
729
766
|
text-align: center;
|
730
767
|
}
|
731
768
|
|
@@ -767,9 +804,9 @@ body:has(.search:open), body:has(.search[open]) {
|
|
767
804
|
width: 1rem;
|
768
805
|
}
|
769
806
|
|
770
|
-
/* CSS:
|
807
|
+
/* CSS: ALERTS */
|
771
808
|
|
772
|
-
.
|
809
|
+
.alert {
|
773
810
|
border: 1px solid;
|
774
811
|
border-left: 0.5rem solid;
|
775
812
|
border-radius: 0.375rem;
|
@@ -777,44 +814,44 @@ body:has(.search:open), body:has(.search[open]) {
|
|
777
814
|
padding: 1rem;
|
778
815
|
}
|
779
816
|
|
780
|
-
.
|
817
|
+
.alert--tip {
|
781
818
|
background: #00940011;
|
782
819
|
border-color: #009400;
|
783
820
|
}
|
784
821
|
|
785
|
-
.
|
822
|
+
.alert--info {
|
786
823
|
background: #87cef911;
|
787
824
|
border-color: #87cef9;
|
788
825
|
}
|
789
826
|
|
790
|
-
.
|
827
|
+
.alert--warning {
|
791
828
|
background: #fea50011;
|
792
829
|
border-color: #fea500;
|
793
830
|
}
|
794
831
|
|
795
|
-
.
|
832
|
+
.alert--danger {
|
796
833
|
background: #db153b11;
|
797
834
|
border-color: #db153b;
|
798
835
|
}
|
799
836
|
|
800
|
-
.
|
837
|
+
.alert__header {
|
801
838
|
align-items: center;
|
802
839
|
display: flex;
|
803
840
|
gap: 0.5ch;
|
804
841
|
margin-bottom: 1rem;
|
805
842
|
}
|
806
843
|
|
807
|
-
.
|
844
|
+
.alert__icon {
|
808
845
|
height: 1rem;
|
809
846
|
width: 1rem;
|
810
847
|
}
|
811
848
|
|
812
|
-
.
|
849
|
+
.alert__label {
|
813
850
|
font-weight: bold;
|
814
851
|
margin: 0;
|
815
852
|
text-transform: uppercase;
|
816
853
|
}
|
817
854
|
|
818
|
-
.
|
855
|
+
.alert__content > :last-of-type {
|
819
856
|
margin-bottom: 0;
|
820
857
|
}
|
@@ -24,10 +24,11 @@ module Hotdocs
|
|
24
24
|
[ *Array(old), *Array(new) ].join(" ")
|
25
25
|
end
|
26
26
|
|
27
|
+
# Needs to be on one line otherwise kramdown chokes
|
27
28
|
link_to(options, html_options) do
|
28
29
|
concat(content_tag(:span, name))
|
29
30
|
|
30
|
-
concat(<<~SVG.html_safe)
|
31
|
+
concat(<<~SVG.gsub(/\n/, "").html_safe)
|
31
32
|
<svg aria-hidden="true" viewBox="0 0 24 24" class="external-link__icon">
|
32
33
|
<path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path>
|
33
34
|
</svg>
|
@@ -70,6 +71,16 @@ module Hotdocs
|
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
74
|
+
def fetcher(id:, path:, fallback: nil, &)
|
75
|
+
data = {
|
76
|
+
controller: "fetcher",
|
77
|
+
"fetcher-host-value": fetcher_host,
|
78
|
+
"fetcher-path-value": path,
|
79
|
+
"fetcher-fallback-value": fallback
|
80
|
+
}
|
81
|
+
content_tag(:div, id: id, data: data, style: "visibility: hidden;", &)
|
82
|
+
end
|
83
|
+
|
73
84
|
private
|
74
85
|
|
75
86
|
def active_link?(url)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html data-theme="light">
|
2
|
+
<html data-theme="light" lang="<%= I18n.locale %>">
|
3
3
|
<head>
|
4
4
|
<title><%= content_for(:title) %></title>
|
5
5
|
|
@@ -12,7 +12,16 @@
|
|
12
12
|
<body data-controller="search">
|
13
13
|
<dialog data-search-target="search" class="search">
|
14
14
|
<div data-search-target="dialog" class="search__dialog">
|
15
|
-
<
|
15
|
+
<div class="search__header">
|
16
|
+
<input autofocus data-action="input->search#search" type="text" class="search__input"></input>
|
17
|
+
<button aria-label="Close search dialog" type="button" class="search__dismiss" data-action="click->search#close">
|
18
|
+
<svg viewBox="0 0 15 15">
|
19
|
+
<g stroke="currentColor" stroke-width="1.2">
|
20
|
+
<path d="M.75.75l13.5 13.5M14.25.75L.75 14.25"></path>
|
21
|
+
</g>
|
22
|
+
</svg>
|
23
|
+
</button>
|
24
|
+
</div>
|
16
25
|
|
17
26
|
<template data-search-target="resultTemplate">
|
18
27
|
<li class="search__result">
|
@@ -33,88 +42,97 @@
|
|
33
42
|
</script>
|
34
43
|
</dialog>
|
35
44
|
|
36
|
-
|
37
|
-
|
38
|
-
<button class="nav__toggle" type="button" aria-label="Toggle navigation" aria-expanded="false" data-action="click->sidenav#open">
|
39
|
-
<svg viewBox="0 0 30 30" aria-hidden="true">
|
40
|
-
<path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path>
|
41
|
-
</svg>
|
42
|
-
</button>
|
43
|
-
|
44
|
-
<%= link_to root_path, class: "nav__brand" do %>
|
45
|
-
<div class="nav__logo-wrapper">
|
46
|
-
<img class="nav__logo" src="<%= logo.src %>" alt="<%= logo.alt %>" height="32" width="32" />
|
47
|
-
</div>
|
45
|
+
<% if content_for?(:hotdocs_nav) %>
|
46
|
+
<%= content_for(:hotdocs_nav) %>
|
48
47
|
|
49
|
-
|
50
|
-
|
48
|
+
<% else %>
|
49
|
+
<nav class="nav" data-controller="sidenav" data-sidenav-open-class-value="sidenav--open" data-sidenav-main-menu-class-value="sidenav__sections--main">
|
50
|
+
<div class="nav__section">
|
51
|
+
<button class="nav__toggle" type="button" aria-label="Toggle navigation" aria-expanded="false" data-action="click->sidenav#open">
|
52
|
+
<svg viewBox="0 0 30 30" aria-hidden="true">
|
53
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path>
|
54
|
+
</svg>
|
55
|
+
</button>
|
51
56
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
57
|
+
<%= link_to root_path, class: "nav__brand" do %>
|
58
|
+
<% unless logo.nil? %>
|
59
|
+
<div class="nav__logo-wrapper">
|
60
|
+
<img class="nav__logo" src="<%= logo.src %>" alt="<%= logo.alt %>" height="32" width="32" />
|
61
|
+
</div>
|
62
|
+
<% end %>
|
58
63
|
|
59
|
-
|
60
|
-
<div class="nav__links">
|
61
|
-
<% nav_right_items("nav__link").each do |item| %>
|
62
|
-
<%= item %>
|
64
|
+
<span class="nav__title"><%= title %></span>
|
63
65
|
<% end %>
|
64
|
-
</div>
|
65
|
-
|
66
|
-
<button type="button" data-action="click->search#open:stop" class="search-button">
|
67
|
-
<svg class="search-button__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
68
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
|
69
|
-
</svg>
|
70
66
|
|
71
|
-
<
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
<div class="sidenav__header">
|
78
|
-
<%= link_to root_path, class: "nav__brand" do %>
|
79
|
-
<div class="nav__logo-wrapper">
|
80
|
-
<img class="nav__logo" src="<%= logo.src %>" alt="<%= logo.alt %>" height="32" width="32" />
|
81
|
-
</div>
|
67
|
+
<div class="nav__links">
|
68
|
+
<% nav_left_items("nav__link").each do |item| %>
|
69
|
+
<%= item %>
|
70
|
+
<% end %>
|
71
|
+
</div>
|
72
|
+
</div>
|
82
73
|
|
83
|
-
|
84
|
-
|
74
|
+
<div class="nav__section">
|
75
|
+
<div class="nav__links">
|
76
|
+
<% nav_right_items("nav__link").each do |item| %>
|
77
|
+
<%= item %>
|
78
|
+
<% end %>
|
79
|
+
</div>
|
85
80
|
|
86
|
-
<button
|
87
|
-
<svg viewBox="0 0
|
88
|
-
<
|
89
|
-
<path d="M.75.75l13.5 13.5M14.25.75L.75 14.25"></path>
|
90
|
-
</g>
|
81
|
+
<button type="button" data-action="click->search#open:stop" class="search-button" aria-label="Open search dialog">
|
82
|
+
<svg class="search-button__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
83
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
|
91
84
|
</svg>
|
85
|
+
|
86
|
+
<span class="search-button__label">Type / to search</span>
|
92
87
|
</button>
|
93
88
|
</div>
|
94
89
|
|
95
|
-
<div class="
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
</li>
|
90
|
+
<div class="sidenav-backdrop"></div>
|
91
|
+
<div class="sidenav">
|
92
|
+
<div class="sidenav__header">
|
93
|
+
<%= link_to root_path, class: "nav__brand" do %>
|
94
|
+
<% unless logo.nil? %>
|
95
|
+
<div class="nav__logo-wrapper">
|
96
|
+
<img class="nav__logo" src="<%= logo.src %>" alt="<%= logo.alt %>" height="32" width="32" />
|
97
|
+
</div>
|
104
98
|
<% end %>
|
105
|
-
|
99
|
+
|
100
|
+
<span class="nav__title"><%= title %></span>
|
101
|
+
<% end %>
|
102
|
+
|
103
|
+
<button aria-label="Close navigation" class="sidenav__toggle" type="button" data-action="click->sidenav#close">
|
104
|
+
<svg viewBox="0 0 15 15">
|
105
|
+
<g stroke="currentColor" stroke-width="1.2">
|
106
|
+
<path d="M.75.75l13.5 13.5M14.25.75L.75 14.25"></path>
|
107
|
+
</g>
|
108
|
+
</svg>
|
109
|
+
</button>
|
106
110
|
</div>
|
107
111
|
|
108
|
-
<div class="
|
109
|
-
<div class="
|
110
|
-
<
|
112
|
+
<div class="sidenav__sections" data-sidenav-target="sections">
|
113
|
+
<div class="sidenav__section">
|
114
|
+
<ul class="menu__section">
|
115
|
+
<% (nav_left_items("menu__link") + nav_right_items("menu__link")).each do |item| %>
|
116
|
+
<li>
|
117
|
+
<div class="menu__row">
|
118
|
+
<%= item %>
|
119
|
+
</div>
|
120
|
+
</li>
|
121
|
+
<% end %>
|
122
|
+
</ul>
|
111
123
|
</div>
|
112
124
|
|
113
|
-
|
125
|
+
<div class="sidenav__section">
|
126
|
+
<div class="menu__section">
|
127
|
+
<button type="button" class="sidenav__back-button" data-action="click->sidenav#back">← Back to main menu</button>
|
128
|
+
</div>
|
129
|
+
|
130
|
+
<%= menu %>
|
131
|
+
</div>
|
114
132
|
</div>
|
115
133
|
</div>
|
116
|
-
</
|
117
|
-
|
134
|
+
</nav>
|
135
|
+
<% end %>
|
118
136
|
|
119
137
|
<div class="content">
|
120
138
|
<aside class="menu">
|
@@ -152,24 +170,31 @@
|
|
152
170
|
</main>
|
153
171
|
</div>
|
154
172
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
173
|
+
<% if content_for?(:hotdocs_footer) %>
|
174
|
+
<%= content_for(:hotdocs_footer) %>
|
175
|
+
|
176
|
+
<% else %>
|
177
|
+
<footer class="footer">
|
178
|
+
<div class="footer__sections">
|
179
|
+
<% footer_items.each do |footer_item| %>
|
180
|
+
<div class="footer__section">
|
181
|
+
<p class="footer__heading"><%= footer_item.fetch(:heading) %></p>
|
182
|
+
|
183
|
+
<ul>
|
184
|
+
<% footer_item.fetch(:items).each do |item| %>
|
185
|
+
<li><%= item %></li>
|
186
|
+
<% end %>
|
187
|
+
</ul>
|
188
|
+
</div>
|
189
|
+
<% end %>
|
190
|
+
</div>
|
160
191
|
|
161
|
-
|
162
|
-
|
163
|
-
<li><%= item %></li>
|
164
|
-
<% end %>
|
165
|
-
</ul>
|
166
|
-
</div>
|
192
|
+
<% unless logo.nil? %>
|
193
|
+
<img class="footer__logo" src="<%= logo.src %>" alt="<%= logo.alt %>" height="150" width="150" />
|
167
194
|
<% end %>
|
168
|
-
</
|
169
|
-
|
170
|
-
<img class="footer__logo" src="<%= logo.src %>" alt="<%= logo.alt %>" height="150" width="150" />
|
195
|
+
</footer>
|
196
|
+
<% end %>
|
171
197
|
|
172
|
-
|
173
|
-
</footer>
|
198
|
+
<p class="credits">Built with Rails & <a class="credits__link" href="https://hotdocsrails.com">HotDocs<img class="credits__logo" src="<%= asset_path "hotdocs/icon.svg" %>" alt="A humanized and happy hot dog" height="32" width="32" /></a></p>
|
174
199
|
</body>
|
175
200
|
</html>
|
data/config/importmap.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
# Pin npm packages by running ./bin/importmap
|
2
2
|
|
3
|
-
pin_all_from Hotdocs::Engine.root.join("app/assets/javascript/controllers"), under: "controllers"
|
3
|
+
pin_all_from Hotdocs::Engine.root.join("app/assets/javascript/controllers"), to: "controllers", under: "hotdocs/controllers", preload: "hotdocs"
|
data/lib/hotdocs/engine.rb
CHANGED
@@ -13,8 +13,7 @@ module Hotdocs
|
|
13
13
|
end
|
14
14
|
|
15
15
|
config.before_initialize do
|
16
|
-
MarkdownHandler.
|
17
|
-
ActionView::Template.register_template_handler :mderb, MarkdownHandler.new(self)
|
16
|
+
ActionView::Template.register_template_handler :mderb, MarkdownHandler.new
|
18
17
|
end
|
19
18
|
end
|
20
19
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Kramdown
|
2
|
+
class Element
|
3
|
+
def to_h
|
4
|
+
{
|
5
|
+
children: children.map(&:to_h),
|
6
|
+
type:,
|
7
|
+
value:
|
8
|
+
}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Alert
|
14
|
+
PATHS_BY_ALERT_TYPE = {
|
15
|
+
"danger" => [
|
16
|
+
"M15.362 5.214A8.252 8.252 0 0 1 12 21 8.25 8.25 0 0 1 6.038 7.047 8.287 8.287 0 0 0 9 9.601a8.983 8.983 0 0 1 3.361-6.867 8.21 8.21 0 0 0 3 2.48Z",
|
17
|
+
"M12 18a3.75 3.75 0 0 0 .495-7.468 5.99 5.99 0 0 0-1.925 3.547 5.975 5.975 0 0 1-2.133-1.001A3.75 3.75 0 0 0 12 18Z"
|
18
|
+
],
|
19
|
+
"warning" => [
|
20
|
+
"M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
|
21
|
+
],
|
22
|
+
"tip" => [
|
23
|
+
"M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18"
|
24
|
+
],
|
25
|
+
"info" => [
|
26
|
+
"m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"
|
27
|
+
]
|
28
|
+
}
|
29
|
+
|
30
|
+
def convert_blockquote(el, indent)
|
31
|
+
child = el.children[0]
|
32
|
+
|
33
|
+
case child.to_h
|
34
|
+
in {
|
35
|
+
type: :p,
|
36
|
+
children: [
|
37
|
+
{ type: :text, value: /\A\[!(INFO|TIP|WARNING|DANGER)\]\z/ },
|
38
|
+
{ type: :br },
|
39
|
+
*
|
40
|
+
]
|
41
|
+
}
|
42
|
+
alert_type = $+.downcase
|
43
|
+
child.children.slice!(0, 2) # remove :text & :br
|
44
|
+
|
45
|
+
svg = Kramdown::Element.new(
|
46
|
+
:html_element,
|
47
|
+
:svg,
|
48
|
+
{
|
49
|
+
class: "alert__icon",
|
50
|
+
xmlns: "http://www.w3.org/2000/svg",
|
51
|
+
fill: "none",
|
52
|
+
viewBox: "0 0 24 24",
|
53
|
+
"stroke-width": "1.5",
|
54
|
+
stroke: "currentColor"
|
55
|
+
}
|
56
|
+
)
|
57
|
+
|
58
|
+
PATHS_BY_ALERT_TYPE[alert_type].each do |path|
|
59
|
+
svg.children << Kramdown::Element.new(:html_element, :path, {
|
60
|
+
"stroke-linecap": "round",
|
61
|
+
"stroke-linejoin": "round",
|
62
|
+
d: path
|
63
|
+
})
|
64
|
+
end
|
65
|
+
|
66
|
+
label = Kramdown::Element.new(:html_element, :span, { class: "alert__label" })
|
67
|
+
label.children << Kramdown::Element.new(:text, alert_type.upcase)
|
68
|
+
|
69
|
+
header = Kramdown::Element.new(:html_element, :div, { class: "alert__header" })
|
70
|
+
header.children << svg
|
71
|
+
header.children << label
|
72
|
+
|
73
|
+
content = Kramdown::Element.new(:html_element, :div, { class: "alert__content" })
|
74
|
+
content.children.push(*el.children)
|
75
|
+
|
76
|
+
alert = Kramdown::Element.new(:html_element, :div, {})
|
77
|
+
alert.children << header
|
78
|
+
alert.children << content
|
79
|
+
|
80
|
+
format_as_block_html(
|
81
|
+
"div",
|
82
|
+
{ class: "alert alert--#{alert_type}" },
|
83
|
+
format_as_indented_block_html("div", {}, inner(alert, indent), indent),
|
84
|
+
indent
|
85
|
+
)
|
86
|
+
else
|
87
|
+
super
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
Kramdown::Converter::Html.prepend(Alert)
|