@diplodoc/transform 4.27.1-beta → 4.28.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.
@@ -112,19 +112,19 @@ export function setDefinitionPosition(
112
112
  }
113
113
 
114
114
  function termOnResize() {
115
- const openDefinition = document.getElementsByClassName(openDefinitionClass)[0] as HTMLElement;
115
+ const openedDefinition = document.getElementsByClassName(openDefinitionClass)[0] as HTMLElement;
116
116
 
117
- if (!openDefinition) {
117
+ if (!openedDefinition) {
118
118
  return;
119
119
  }
120
- const termId = openDefinition.getAttribute('term-id') || '';
120
+ const termId = openedDefinition.getAttribute('term-id') || '';
121
121
  const termElement = document.getElementById(termId);
122
122
 
123
123
  if (!termElement) {
124
124
  return;
125
125
  }
126
126
 
127
- setDefinitionPosition(openDefinition, termElement);
127
+ setDefinitionPosition(openedDefinition, termElement);
128
128
  }
129
129
 
130
130
  function termParentElement(term: HTMLElement | null) {
@@ -137,16 +137,57 @@ function termParentElement(term: HTMLElement | null) {
137
137
  return closestScrollableParent || term.parentElement;
138
138
  }
139
139
 
140
+ export function openDefinition(target: HTMLElement) {
141
+ const openedDefinition = document.getElementsByClassName(openDefinitionClass)[0] as HTMLElement;
142
+
143
+ const termId = target.getAttribute('id');
144
+ const termKey = target.getAttribute('term-key');
145
+ let definitionElement = document.getElementById(termKey + '_element');
146
+
147
+ if (termKey && !definitionElement) {
148
+ definitionElement = createDefinitionElement(target);
149
+ }
150
+
151
+ const isSameTerm = openedDefinition && termId === openedDefinition.getAttribute('term-id');
152
+ if (isSameTerm) {
153
+ closeDefinition(openedDefinition);
154
+ return;
155
+ }
156
+
157
+ const isTargetDefinitionContent = target.closest(
158
+ [Selector.CONTENT.replace(' ', ''), openClass].join('.'),
159
+ );
160
+
161
+ if (openedDefinition && !isTargetDefinitionContent) {
162
+ closeDefinition(openedDefinition);
163
+ }
164
+
165
+ if (!target.matches(Selector.TITLE) || !definitionElement) {
166
+ return;
167
+ }
168
+
169
+ setDefinitionId(definitionElement, target);
170
+ setDefinitonAriaLive(definitionElement, target);
171
+ setDefinitionPosition(definitionElement, target);
172
+
173
+ definitionElement.classList.toggle(openClass);
174
+
175
+ trapFocus(definitionElement);
176
+ }
177
+
140
178
  export function closeDefinition(definition: HTMLElement) {
141
179
  definition.classList.remove(openClass);
142
180
  const termId = definition.getAttribute('term-id') || '';
143
- const termParent = termParentElement(document.getElementById(termId));
181
+ const term = document.getElementById(termId);
182
+ const termParent = termParentElement(term);
144
183
 
145
184
  if (!termParent) {
146
185
  return;
147
186
  }
148
187
 
149
188
  termParent.removeEventListener('scroll', termOnResize);
189
+ term?.focus(); // Set focus back to open button after closing popup
190
+
150
191
  isListenerNeeded = true;
151
192
  }
152
193
 
@@ -167,3 +208,32 @@ function getCoords(elem: HTMLElement) {
167
208
 
168
209
  return {top: Math.round(top), left: Math.round(left)};
169
210
  }
211
+
212
+ export function trapFocus(element: HTMLElement) {
213
+ const focusableElements = element.querySelectorAll(
214
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
215
+ );
216
+ const firstFocusableElement = focusableElements[0] as HTMLElement;
217
+ const lastFocusableElement = focusableElements[focusableElements.length - 1] as HTMLElement;
218
+
219
+ if (firstFocusableElement) {
220
+ firstFocusableElement.focus();
221
+ }
222
+
223
+ element.addEventListener('keydown', function (e) {
224
+ const isTabPressed = e.key === 'Tab' || e.keyCode === 9;
225
+ if (!isTabPressed) {
226
+ return;
227
+ }
228
+
229
+ if (e.shiftKey) {
230
+ if (document.activeElement === firstFocusableElement) {
231
+ lastFocusableElement.focus();
232
+ e.preventDefault();
233
+ }
234
+ } else if (document.activeElement === lastFocusableElement) {
235
+ firstFocusableElement.focus();
236
+ e.preventDefault();
237
+ }
238
+ });
239
+ }
@@ -9,11 +9,17 @@
9
9
 
10
10
  text-align: center;
11
11
  font-size: 18px;
12
- }
13
12
 
14
- .yfm-anchor::before {
15
- content: '#';
16
- opacity: 0;
13
+ &:focus {
14
+ &::before {
15
+ opacity: 1;
16
+ }
17
+ }
18
+
19
+ &::before {
20
+ content: '#';
21
+ opacity: 0;
22
+ }
17
23
  }
18
24
 
19
25
  &:hover .yfm-anchor::before {
@@ -2,22 +2,35 @@
2
2
  &-clipboard {
3
3
  position: relative;
4
4
 
5
- &:hover &-button {
6
- display: block;
5
+ &-button {
6
+ &:hover, &:focus {
7
+ opacity: 1;
8
+ }
7
9
  }
8
10
 
9
11
  & > pre {
10
12
  border-radius: 10px;
11
13
  overflow: hidden;
12
14
  }
13
- }
14
15
 
15
- &-clipboard-button {
16
- display: none;
17
- position: absolute;
18
- cursor: pointer;
19
- top: 16px;
20
- right: 16px;
21
- z-index: 1;
16
+ &-button {
17
+ position: absolute;
18
+ top: 16px;
19
+ right: 16px;
20
+ z-index: 1;
21
+ opacity: 0;
22
+
23
+ //reset default button style
24
+ background: none;
25
+ color: inherit;
26
+ border: none;
27
+ padding: 0;
28
+ font: inherit;
29
+ cursor: pointer;
30
+ }
31
+
32
+ &-icon {
33
+ pointer-events: none;
34
+ }
22
35
  }
23
36
  }
@@ -6,39 +6,41 @@ import {generateID} from './utils';
6
6
  const wrapInClipboard = (element: string | undefined, id: number) => {
7
7
  return `
8
8
  <div class="yfm-clipboard">
9
- ${element}
10
- <svg width="16" height="16" viewBox="0 0 24 24" class="yfm-clipboard-button" data-animation="${id}">
11
- <path
12
- fill="currentColor"
13
- d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"
14
- />
15
- <path
16
- stroke="currentColor"
17
- fill="transparent"
18
- stroke-width="1.5"
19
- d="M9.5 13l3 3l5 -5"
20
- visibility="hidden"
21
- >
22
- <animate
23
- id="visibileAnimation-${id}"
24
- attributeName="visibility"
25
- from="hidden"
26
- to="visible"
27
- dur="0.2s"
28
- fill="freeze"
29
- begin=""
30
- />
31
- <animate
32
- id="hideAnimation-${id}"
33
- attributeName="visibility"
34
- from="visible"
35
- to="hidden"
36
- dur="1s"
37
- begin="visibileAnimation-${id}.end+1"
38
- fill="freeze"
39
- />
40
- </path>
41
- </svg>
9
+ ${element}
10
+ <button class="yfm-clipboard-button">
11
+ <svg width="16" height="16" viewBox="0 0 24 24" class="yfm-clipboard-icon" data-animation="${id}">
12
+ <path
13
+ fill="currentColor"
14
+ d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"
15
+ />
16
+ <path
17
+ stroke="currentColor"
18
+ fill="transparent"
19
+ stroke-width="1.5"
20
+ d="M9.5 13l3 3l5 -5"
21
+ visibility="hidden"
22
+ >
23
+ <animate
24
+ id="visibileAnimation-${id}"
25
+ attributeName="visibility"
26
+ from="hidden"
27
+ to="visible"
28
+ dur="0.2s"
29
+ fill="freeze"
30
+ begin=""
31
+ />
32
+ <animate
33
+ id="hideAnimation-${id}"
34
+ attributeName="visibility"
35
+ from="visible"
36
+ to="hidden"
37
+ dur="1s"
38
+ begin="visibileAnimation-${id}.end+1"
39
+ fill="freeze"
40
+ />
41
+ </path>
42
+ </svg>
43
+ </button>
42
44
  </div>
43
45
  `;
44
46
  };
@@ -100,6 +100,7 @@ const term: MarkdownItPluginCb = (md, options) => {
100
100
  token.attrSet('class', 'yfm yfm-term_title');
101
101
  token.attrSet('term-key', ':' + termKey);
102
102
  token.attrSet('aria-describedby', ':' + termKey + '_element');
103
+ token.attrSet('tabindex', '0');
103
104
  token.attrSet('id', generateID());
104
105
  nodes.push(token);
105
106
 
@@ -485,7 +485,7 @@ const defaultCssWhitelist = {
485
485
  '--method': true,
486
486
  };
487
487
 
488
- const yfmHtmlAttrs = ['note-type', 'term-key'];
488
+ const yfmHtmlAttrs = ['note-type', 'yfm2xliff-explicit', 'term-key'];
489
489
 
490
490
  const allowedTags = Array.from(
491
491
  new Set([...htmlTags, ...svgTags, ...sanitizeHtml.defaults.allowedTags]),
@@ -518,7 +518,7 @@ export const defaultOptions: SanitizeOptions = {
518
518
  function sanitizeStyleTags(dom: cheerio.CheerioAPI, cssWhiteList: CssWhiteList) {
519
519
  const styleTags = dom('style');
520
520
 
521
- styleTags.each((_index, element) => {
521
+ styleTags.each((_index: number, element: cheerio.Element) => {
522
522
  const styleText = dom(element).text();
523
523
 
524
524
  try {