@undrr/undrr-mangrove 2.0.1 → 2.1.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.
- package/README.md +1 -1
- package/components/BookCard.js +1 -1
- package/components/Breadcrumbs.js +1 -1
- package/components/Checkbox.js +1 -1
- package/components/Chips.js +1 -1
- package/components/CopyButton.js +1 -1
- package/components/CtaButton.js +1 -1
- package/components/Drawer.js +2 -2
- package/components/EmbedContainer.js +1 -1
- package/components/Footer.js +1 -1
- package/components/FormAction.js +1 -1
- package/components/FormErrorSummary.js +1 -1
- package/components/FormGroup.js +1 -1
- package/components/FullWidth.js +1 -1
- package/components/Gallery.js +2 -2
- package/components/Hero.js +2 -2
- package/components/HighlightBox.js +1 -1
- package/components/HorizontalBookCard.js +1 -1
- package/components/HorizontalCard.js +1 -1
- package/components/IconCard.js +1 -1
- package/components/Legend.js +1 -1
- package/components/Loader.js +2 -2
- package/components/MegaMenu.js +2 -2
- package/components/Notice.js +1 -1
- package/components/PageHeader.js +1 -1
- package/components/Pager.js +1 -1
- package/components/ProgressBar.js +2 -2
- package/components/QuoteHighlight.js +1 -1
- package/components/Radio.js +1 -1
- package/components/Range.js +1 -1
- package/components/ScrollContainer.js +1 -1
- package/components/SegmentedControl.js +1 -1
- package/components/Select.js +1 -1
- package/components/ServiceNotice.js +1 -1
- package/components/ShareButtons.js +1 -1
- package/components/ShowMore.js +2 -2
- package/components/SkipLink.js +1 -1
- package/components/StatsCard.js +1 -1
- package/components/SyndicationSearchWidget.js +2 -2
- package/components/Tab.js +1 -1
- package/components/TextCta.js +1 -1
- package/components/TextInput.js +1 -1
- package/components/Textarea.js +1 -1
- package/components/Tree.js +1 -1
- package/components/UserFeedback.js +1 -1
- package/components/VerticalCard.js +1 -1
- package/components/hydrate.js +2 -2
- package/css/style-all.css +41 -42
- package/css/style-all.css.map +1 -1
- package/css/style-delta.css +41 -42
- package/css/style-delta.css.map +1 -1
- package/css/style-gutenberg.css +4 -4
- package/css/style-gutenberg.css.map +1 -1
- package/css/style-irp.css +41 -42
- package/css/style-irp.css.map +1 -1
- package/css/style-mcr.css +41 -42
- package/css/style-mcr.css.map +1 -1
- package/css/style-preventionweb.css +41 -42
- package/css/style-preventionweb.css.map +1 -1
- package/css/style.css +41 -42
- package/css/style.css.map +1 -1
- package/docs/EDITORIAL-MANUAL.md +1 -1
- package/docs/llms-motion.txt +125 -0
- package/error-pages/401.html +1 -1
- package/error-pages/403.html +1 -1
- package/error-pages/404.html +1 -1
- package/error-pages/429.html +1 -1
- package/error-pages/500.html +1 -1
- package/error-pages/502.html +1 -1
- package/error-pages/503.html +1 -1
- package/error-pages/504.html +1 -1
- package/error-pages/5xx.html +1 -1
- package/error-pages/challenge.html +1 -1
- package/error-pages/managed-challenge.html +1 -1
- package/js/cite-this.js +685 -43
- package/js/on-this-page-nav.js +80 -1
- package/js/show-more.js +149 -21
- package/package.json +1 -1
- package/scss/Atom/Images/AuthorImage/author-image.scss +9 -2
- package/scss/Atom/ReachElement/Details/details.scss +4 -2
- package/scss/Components/Cards/Card/card.scss +8 -2
- package/scss/Components/Cards/StatsCard/stats-card.scss +8 -1
- package/scss/Components/CiteThis/cite-this.scss +162 -9
- package/scss/Components/Gallery/gallery.scss +40 -15
- package/scss/Components/Hero/hero.scss +165 -0
- package/scss/Components/MegaMenu/mega-menu.scss +42 -121
- package/scss/Components/Navigation/Drawer/drawer.scss +10 -0
- package/scss/Components/OnThisPageNav/on-this-page-nav.scss +108 -3
- package/scss/Components/SyndicationSearchWidget/syndication-search-widget.scss +252 -127
- package/scss/Components/Tab/tab.scss +2 -1
- package/scss/Patterns/RichContent/rich-content.scss +177 -0
- package/scss/Patterns/_shared/pattern-demo.scss +5 -0
- package/scss/Utilities/Loader/loader.scss +89 -11
- package/scss/Utilities/ShowMore/show-more.scss +6 -0
- package/scss/assets/scss/_components.scss +3 -0
- package/scss/assets/scss/_foundational.scss +23 -8
- package/scss/assets/scss/_mixins.scss +5 -0
- package/scss/assets/scss/_variables.scss +1 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rich content
|
|
3
|
+
*
|
|
4
|
+
* Opt-in semantic defaults for trusted editorial HTML. Classed descendants
|
|
5
|
+
* keep their own presentation; classless structural wrappers pass through.
|
|
6
|
+
*
|
|
7
|
+
* Full documentation:
|
|
8
|
+
* https://github.com/PreventionWeb/undrr-mangrove/blob/main/stories/Patterns/RichContent/RichContent.mdx
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// The pattern's scroll wrapper is editorial structure, not a component
|
|
12
|
+
// island. Root classes never exclude the root's own content.
|
|
13
|
+
@mixin mg-editorial-element($element) {
|
|
14
|
+
.mg-content
|
|
15
|
+
#{$element}:not(
|
|
16
|
+
:where(
|
|
17
|
+
.mg-content [class]:not(.mg-content__scroll),
|
|
18
|
+
.mg-content [class]:not(.mg-content__scroll) *,
|
|
19
|
+
.mg-content
|
|
20
|
+
:is(
|
|
21
|
+
nav,
|
|
22
|
+
form,
|
|
23
|
+
fieldset,
|
|
24
|
+
button,
|
|
25
|
+
[role="navigation"],
|
|
26
|
+
[role="menu"],
|
|
27
|
+
[role="menubar"],
|
|
28
|
+
[role="tablist"],
|
|
29
|
+
[role="dialog"],
|
|
30
|
+
[role="toolbar"],
|
|
31
|
+
[role="listbox"],
|
|
32
|
+
[role="tree"]
|
|
33
|
+
),
|
|
34
|
+
.mg-content
|
|
35
|
+
:is(
|
|
36
|
+
nav,
|
|
37
|
+
form,
|
|
38
|
+
fieldset,
|
|
39
|
+
button,
|
|
40
|
+
[role="navigation"],
|
|
41
|
+
[role="menu"],
|
|
42
|
+
[role="menubar"],
|
|
43
|
+
[role="tablist"],
|
|
44
|
+
[role="dialog"],
|
|
45
|
+
[role="toolbar"],
|
|
46
|
+
[role="listbox"],
|
|
47
|
+
[role="tree"]
|
|
48
|
+
)
|
|
49
|
+
*
|
|
50
|
+
)
|
|
51
|
+
) {
|
|
52
|
+
@content;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@include mg-editorial-element(":is(p, ul, ol, dl)") {
|
|
57
|
+
margin-block: 0 var(--mg-spacing-150);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@include mg-editorial-element(":is(h1, h2, h3, h4, h5, h6)") {
|
|
61
|
+
margin-block: var(--mg-spacing-200) var(--mg-spacing-100);
|
|
62
|
+
line-height: var(--mg-font-line-height-500);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@include mg-editorial-element(":is(ul, ol)") {
|
|
66
|
+
padding-inline: 0;
|
|
67
|
+
padding-inline-start: var(--mg-spacing-250);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@include mg-editorial-element("li") {
|
|
71
|
+
padding-inline: 0;
|
|
72
|
+
margin-block-end: var(--mg-spacing-25);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@include mg-editorial-element("li > :is(ul, ol)") {
|
|
76
|
+
margin-block: var(--mg-spacing-50) 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@include mg-editorial-element(
|
|
80
|
+
"blockquote:not(:where(:has(.mg-highlight-box)))"
|
|
81
|
+
) {
|
|
82
|
+
margin-block: var(--mg-spacing-200);
|
|
83
|
+
margin-inline: 0;
|
|
84
|
+
padding-inline-start: var(--mg-spacing-150);
|
|
85
|
+
border-inline-start: 3px solid rgb(var(--mg-color-neutral-400));
|
|
86
|
+
font-size: var(--mg-font-size-300);
|
|
87
|
+
font-weight: 400;
|
|
88
|
+
line-height: var(--mg-font-line-height-700);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@include mg-editorial-element("cite") {
|
|
92
|
+
display: inline;
|
|
93
|
+
margin: 0;
|
|
94
|
+
font-style: italic;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@include mg-editorial-element("a:not(:where([role=button]))") {
|
|
98
|
+
text-decoration: underline;
|
|
99
|
+
text-underline-offset: 0.15em;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@include mg-editorial-element("hr") {
|
|
103
|
+
margin-block: var(--mg-spacing-200);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@include mg-editorial-element("figure") {
|
|
107
|
+
margin-block: var(--mg-spacing-200);
|
|
108
|
+
margin-inline: 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@include mg-editorial-element("figure > img") {
|
|
112
|
+
display: block;
|
|
113
|
+
max-width: 100%;
|
|
114
|
+
height: auto;
|
|
115
|
+
padding: 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@include mg-editorial-element(":is(figcaption, caption)") {
|
|
119
|
+
font-size: var(--mg-font-size-250);
|
|
120
|
+
line-height: var(--mg-font-line-height-700);
|
|
121
|
+
text-align: start;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@include mg-editorial-element("figcaption") {
|
|
125
|
+
margin-block-start: var(--mg-spacing-50);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@include mg-editorial-element("table") {
|
|
129
|
+
width: 100%;
|
|
130
|
+
border-collapse: collapse;
|
|
131
|
+
margin-block: var(--mg-spacing-150);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@include mg-editorial-element(":is(th, td)") {
|
|
135
|
+
padding: var(--mg-spacing-75) var(--mg-spacing-100);
|
|
136
|
+
border: 1px solid rgb(var(--mg-color-neutral-400));
|
|
137
|
+
text-align: start;
|
|
138
|
+
vertical-align: top;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@include mg-editorial-element("caption") {
|
|
142
|
+
margin-block-end: var(--mg-spacing-50);
|
|
143
|
+
font-weight: 600;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@include mg-editorial-element("code:not(:where(pre code))") {
|
|
147
|
+
// Code is prose, not a link: use the readable text/surface pairing rather
|
|
148
|
+
// than interactive ink on its tinted global background in every theme.
|
|
149
|
+
color: rgb(var(--mg-color-text));
|
|
150
|
+
background: rgb(var(--mg-color-neutral-50));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@include mg-editorial-element("pre") {
|
|
154
|
+
max-width: 100%;
|
|
155
|
+
margin-block: var(--mg-spacing-150);
|
|
156
|
+
overflow-x: auto;
|
|
157
|
+
|
|
158
|
+
// Match the existing LTR code child so RTL pages can reach both ends.
|
|
159
|
+
direction: ltr;
|
|
160
|
+
unicode-bidi: isolate;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@include mg-editorial-element("pre:focus-visible") {
|
|
164
|
+
@include mg-focus-ring-inset;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Only authored overflow regions become keyboard stops. Native table
|
|
168
|
+
// semantics remain on the table; the wrapper supplies scrolling and focus.
|
|
169
|
+
.mg-content .mg-content__scroll {
|
|
170
|
+
max-width: 100%;
|
|
171
|
+
overflow-x: auto;
|
|
172
|
+
margin-block: var(--mg-spacing-150);
|
|
173
|
+
|
|
174
|
+
&:focus-visible {
|
|
175
|
+
@include mg-focus-ring-inset;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Loader / Spinner
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Loading indicator for asynchronous states, in a default size for
|
|
5
5
|
* pages and regions and a 1em inline size for text, buttons and form fields.
|
|
6
6
|
*
|
|
7
7
|
* Full documentation:
|
|
@@ -59,16 +59,6 @@
|
|
|
59
59
|
width: 1em;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
// Under reduced motion the site-wide reset cuts every animation to 0.01ms
|
|
63
|
-
// and one iteration, which would freeze the spinner and make it look as if
|
|
64
|
-
// loading has stopped. A slow, steady rotation still says "working" without
|
|
65
|
-
// the speed. The !important is needed to outrank the reset's own
|
|
66
|
-
// !important, and is scoped to the loader.
|
|
67
|
-
@media (prefers-reduced-motion: reduce) {
|
|
68
|
-
animation-duration: calc(var(--mg-loader-duration) * 3) !important;
|
|
69
|
-
animation-iteration-count: infinite !important;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
62
|
// Forced colours would paint every side of the border in one system colour,
|
|
73
63
|
// a full ring whose rotation cannot be seen. Opt out and use system colours
|
|
74
64
|
// for the arc and the track.
|
|
@@ -77,6 +67,94 @@
|
|
|
77
67
|
border-top-color: CanvasText;
|
|
78
68
|
forced-color-adjust: none;
|
|
79
69
|
}
|
|
70
|
+
|
|
71
|
+
@media (prefers-reduced-motion: reduce) {
|
|
72
|
+
// A complete dashed ring is a deliberate pending marker, not a frozen
|
|
73
|
+
// frame of the asymmetric spinner. Its label carries the state meaning.
|
|
74
|
+
animation: none !important;
|
|
75
|
+
transform: none;
|
|
76
|
+
border-style: dashed;
|
|
77
|
+
border-color: rgb(var(--mg-color-interactive));
|
|
78
|
+
|
|
79
|
+
&--inline {
|
|
80
|
+
border-color: currentColor;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// The same existing markup works for React and CSS-only consumers. The
|
|
84
|
+
// status label joins normal flow; only the marker keeps the old size.
|
|
85
|
+
&:not([aria-hidden="true"]) {
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
flex-direction: column;
|
|
88
|
+
align-items: center;
|
|
89
|
+
gap: var(--mg-spacing-50);
|
|
90
|
+
width: fit-content;
|
|
91
|
+
max-width: 100%;
|
|
92
|
+
height: auto;
|
|
93
|
+
border: 0;
|
|
94
|
+
border-radius: 0;
|
|
95
|
+
vertical-align: middle;
|
|
96
|
+
text-align: center;
|
|
97
|
+
|
|
98
|
+
&::before {
|
|
99
|
+
content: "";
|
|
100
|
+
display: block;
|
|
101
|
+
flex-shrink: 0;
|
|
102
|
+
box-sizing: border-box;
|
|
103
|
+
width: 40px;
|
|
104
|
+
max-width: 100%;
|
|
105
|
+
aspect-ratio: 1;
|
|
106
|
+
border: 8px dashed rgb(var(--mg-color-interactive));
|
|
107
|
+
border-radius: 50%;
|
|
108
|
+
|
|
109
|
+
@include devicebreak(medium) {
|
|
110
|
+
width: 96px;
|
|
111
|
+
border-width: 14px;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
> .mg-u-sr-only {
|
|
116
|
+
position: static;
|
|
117
|
+
width: auto;
|
|
118
|
+
min-width: 0;
|
|
119
|
+
max-width: 100%;
|
|
120
|
+
height: auto;
|
|
121
|
+
margin: 0;
|
|
122
|
+
overflow: visible;
|
|
123
|
+
clip-path: none;
|
|
124
|
+
white-space: normal;
|
|
125
|
+
overflow-wrap: anywhere;
|
|
126
|
+
line-height: var(--mg-font-line-height-700);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&--inline:not([aria-hidden="true"]) {
|
|
131
|
+
flex-direction: row;
|
|
132
|
+
align-items: baseline;
|
|
133
|
+
text-align: start;
|
|
134
|
+
|
|
135
|
+
&::before {
|
|
136
|
+
width: 1em;
|
|
137
|
+
height: 1em;
|
|
138
|
+
border-width: max(2px, 0.125em);
|
|
139
|
+
border-color: currentColor;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@media (forced-colors: active) {
|
|
144
|
+
border-color: CanvasText;
|
|
145
|
+
|
|
146
|
+
&:not([aria-hidden="true"]) {
|
|
147
|
+
// Let the visible label use the user's text colours. Only the
|
|
148
|
+
// decorative marker opts out to keep its dashed outline distinct.
|
|
149
|
+
forced-color-adjust: auto;
|
|
150
|
+
|
|
151
|
+
&::before {
|
|
152
|
+
border-color: CanvasText;
|
|
153
|
+
forced-color-adjust: none;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
80
158
|
}
|
|
81
159
|
|
|
82
160
|
// * loader end *//
|
|
@@ -32,3 +32,9 @@
|
|
|
32
32
|
z-index: 2; // button sits above the gradient fade-out overlay — local to show-more context
|
|
33
33
|
padding: mg-rem(10) mg-rem(40);
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
// `.mg-button` sets `display`, which beats the `hidden` attribute the
|
|
37
|
+
// threshold option puts on a toggle with nothing to reveal.
|
|
38
|
+
.mg-show-more--button[hidden] {
|
|
39
|
+
display: none;
|
|
40
|
+
}
|
|
@@ -97,3 +97,6 @@
|
|
|
97
97
|
@import "../../Components/DataViz/Legend/legend";
|
|
98
98
|
@import "../../Components/Navigation/Drawer/drawer";
|
|
99
99
|
@import "../../Components/Navigation/Tree/tree";
|
|
100
|
+
|
|
101
|
+
// Opt-in editorial defaults follow component styles; classed islands opt out.
|
|
102
|
+
@import "../../Patterns/RichContent/rich-content";
|
|
@@ -191,7 +191,10 @@ a {
|
|
|
191
191
|
// Long-form and WYSIWYG content opt into this boundary. Navigation, cards and
|
|
192
192
|
// button-like links carry their purpose through their component treatment;
|
|
193
193
|
// prose links do not, so colour alone is insufficient (WCAG 1.4.1).
|
|
194
|
-
|
|
194
|
+
// Retain the reach through CMS classes, with low specificity so component
|
|
195
|
+
// links (including paragraph-wrapped buttons) can own their treatment.
|
|
196
|
+
:where(.mg-content p)
|
|
197
|
+
a:not(:where(.mg-button, .mg-icon-button, [role="button"])) {
|
|
195
198
|
text-decoration: underline;
|
|
196
199
|
}
|
|
197
200
|
|
|
@@ -206,9 +209,22 @@ a {
|
|
|
206
209
|
}
|
|
207
210
|
}
|
|
208
211
|
|
|
209
|
-
|
|
212
|
+
// RTL list indentation. The direction is wrapped in :where() so these rules
|
|
213
|
+
// carry exactly the specificity of the LTR `ul`, `ol` and `ul li` rules they
|
|
214
|
+
// mirror. A bare [dir="rtl"] made them outrank a component's own list reset
|
|
215
|
+
// (a class rule), so in RTL every unmarked component list (On this page nav,
|
|
216
|
+
// hero tags, breadcrumbs, pager, tabs and more) picked up the prose indent and
|
|
217
|
+
// sat 9-22px inside its content edge. LTR is untouched: these rules only match
|
|
218
|
+
// under an RTL ancestor, and the custom property is only read here.
|
|
219
|
+
:where([dir="rtl"]) {
|
|
210
220
|
ul,
|
|
211
221
|
ol {
|
|
222
|
+
// The item indent below aligns Arabic text with its bullets. A list
|
|
223
|
+
// with no bullets (mg-list-unmarked) sets this to 0 on itself; each
|
|
224
|
+
// list declares its own default, so a bulleted list nested inside an
|
|
225
|
+
// unmarked one keeps its indent.
|
|
226
|
+
--mg-list-item-indent-rtl: #{mg-rem(9)};
|
|
227
|
+
|
|
212
228
|
direction: rtl;
|
|
213
229
|
padding-left: 0;
|
|
214
230
|
padding-right: mg-rem(11);
|
|
@@ -219,16 +235,15 @@ a {
|
|
|
219
235
|
|
|
220
236
|
li {
|
|
221
237
|
padding-left: 0;
|
|
222
|
-
padding-right: mg-rem(9);
|
|
223
238
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
padding-right: mg-rem(9);
|
|
228
|
-
}
|
|
239
|
+
// Keep RTL list item text aligned with bullets
|
|
240
|
+
// https://gitlab.com/undrr/web-backlog/-/issues/2444
|
|
241
|
+
padding-right: var(--mg-list-item-indent-rtl);
|
|
229
242
|
}
|
|
230
243
|
}
|
|
244
|
+
}
|
|
231
245
|
|
|
246
|
+
[dir="rtl"] {
|
|
232
247
|
// 900 weight Roboto does not work well with Arabic
|
|
233
248
|
// https://gitlab.com/undrr/web-backlog/-/issues/1711
|
|
234
249
|
h1,
|
|
@@ -176,6 +176,11 @@
|
|
|
176
176
|
// (`tree`, `group`, `menu`, `listbox`, `tablist`): that role already replaces
|
|
177
177
|
// list semantics, and there is nothing to preserve.
|
|
178
178
|
@mixin mg-list-unmarked {
|
|
179
|
+
// A list with no bullets has nothing to align its items with, so it opts
|
|
180
|
+
// out of the RTL item indent in _foundational.scss. Read only under
|
|
181
|
+
// [dir="rtl"]; no effect in LTR.
|
|
182
|
+
--mg-list-item-indent-rtl: 0;
|
|
183
|
+
|
|
179
184
|
list-style: none;
|
|
180
185
|
list-style-type: "";
|
|
181
186
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Storybook: https://mangrove.undrr.org/
|
|
5
5
|
* LLMs context: https://mangrove.undrr.org/llms.txt
|
|
6
6
|
* Repository: https://github.com/PreventionWeb/undrr-mangrove
|
|
7
|
-
* Version: 2.0
|
|
7
|
+
* Version: 2.1.0
|
|
8
8
|
*
|
|
9
9
|
* ██╗ ██╗███╗ ██╗██████╗ ██████╗ ██████╗
|
|
10
10
|
* ██║ ██║████╗ ██║██╔══██╗██╔══██╗██╔══██╗
|