primer_view_components 0.0.69 → 0.0.72
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 +96 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/components/primer/alpha/{tooltip.d.ts → tool-tip-element.d.ts} +2 -3
- data/app/components/primer/alpha/{tooltip.js → tool-tip-element.js} +112 -145
- data/app/components/primer/alpha/{tooltip.ts → tool-tip-element.ts} +37 -67
- data/app/components/primer/alpha/tooltip.rb +8 -0
- data/app/components/primer/beta/auto_complete/auto_complete.html.erb +16 -6
- data/app/components/primer/beta/auto_complete.rb +82 -20
- data/app/components/primer/button_component.html.erb +1 -0
- data/app/components/primer/button_component.rb +29 -0
- data/app/components/primer/component.rb +9 -2
- data/app/components/primer/link_component.erb +1 -0
- data/app/components/primer/link_component.rb +29 -4
- data/app/components/primer/primer.d.ts +1 -1
- data/app/components/primer/primer.js +1 -1
- data/app/components/primer/primer.ts +1 -1
- data/lib/primer/classify/utilities.yml +20 -0
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/rubocop/config/default.yml +3 -0
- data/lib/rubocop/cop/primer/deprecated_components.rb +80 -0
- data/lib/tasks/custom_utilities.yml +12 -0
- data/lib/tasks/docs.rake +3 -1
- data/static/arguments.yml +18 -0
- data/static/classes.yml +4 -0
- metadata +12 -10
@@ -1,5 +1,5 @@
|
|
1
1
|
declare type Direction = 'n' | 's' | 'e' | 'w' | 'ne' | 'se' | 'nw' | 'sw';
|
2
|
-
declare class
|
2
|
+
declare class ToolTipElement extends HTMLElement {
|
3
3
|
#private;
|
4
4
|
styles(): string;
|
5
5
|
get htmlFor(): string;
|
@@ -9,7 +9,6 @@ declare class TooltipElement extends HTMLElement {
|
|
9
9
|
get direction(): Direction;
|
10
10
|
set direction(value: Direction);
|
11
11
|
get control(): HTMLElement | null;
|
12
|
-
constructor();
|
13
12
|
connectedCallback(): void;
|
14
13
|
disconnectedCallback(): void;
|
15
14
|
handleEvent(event: Event): void;
|
@@ -18,7 +17,7 @@ declare class TooltipElement extends HTMLElement {
|
|
18
17
|
}
|
19
18
|
declare global {
|
20
19
|
interface Window {
|
21
|
-
|
20
|
+
ToolTipElement: typeof ToolTipElement;
|
22
21
|
}
|
23
22
|
}
|
24
23
|
export {};
|
@@ -1,19 +1,18 @@
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver,
|
2
|
-
if (
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
return value;
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
7
6
|
};
|
8
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver,
|
9
|
-
if (!
|
10
|
-
|
11
|
-
|
12
|
-
return privateMap.get(receiver);
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
13
11
|
};
|
14
|
-
var
|
12
|
+
var _ToolTipElement_instances, _ToolTipElement_abortController, _ToolTipElement_align, _ToolTipElement_side, _ToolTipElement_allowUpdatePosition, _ToolTipElement_update, _ToolTipElement_updatePosition;
|
15
13
|
import { getAnchoredPosition } from '@primer/behaviors';
|
16
14
|
const TOOLTIP_OPEN_CLASS = 'tooltip-open';
|
15
|
+
const TOOLTIP_ARROW_EDGE_OFFSET = 6;
|
17
16
|
const DIRECTION_CLASSES = [
|
18
17
|
'tooltip-n',
|
19
18
|
'tooltip-s',
|
@@ -24,20 +23,14 @@ const DIRECTION_CLASSES = [
|
|
24
23
|
'tooltip-nw',
|
25
24
|
'tooltip-sw'
|
26
25
|
];
|
27
|
-
class
|
26
|
+
class ToolTipElement extends HTMLElement {
|
28
27
|
constructor() {
|
29
|
-
super();
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
shadow.innerHTML = `
|
36
|
-
<style>
|
37
|
-
${this.styles()}
|
38
|
-
</style>
|
39
|
-
<slot></slot>
|
40
|
-
`;
|
28
|
+
super(...arguments);
|
29
|
+
_ToolTipElement_instances.add(this);
|
30
|
+
_ToolTipElement_abortController.set(this, void 0);
|
31
|
+
_ToolTipElement_align.set(this, 'center');
|
32
|
+
_ToolTipElement_side.set(this, 'outside-bottom');
|
33
|
+
_ToolTipElement_allowUpdatePosition.set(this, false);
|
41
34
|
}
|
42
35
|
styles() {
|
43
36
|
return `
|
@@ -60,7 +53,8 @@ class TooltipElement extends HTMLElement {
|
|
60
53
|
opacity: 0;
|
61
54
|
max-width: 250px;
|
62
55
|
word-wrap: break-word;
|
63
|
-
white-space: normal
|
56
|
+
white-space: normal;
|
57
|
+
width: max-content;
|
64
58
|
}
|
65
59
|
|
66
60
|
:host:before{
|
@@ -99,12 +93,16 @@ class TooltipElement extends HTMLElement {
|
|
99
93
|
animation-delay: .4s
|
100
94
|
}
|
101
95
|
|
96
|
+
:host(.tooltip-s):before,
|
97
|
+
:host(.tooltip-n):before {
|
98
|
+
right: 50%;
|
99
|
+
margin-right: -${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
100
|
+
}
|
101
|
+
|
102
102
|
:host(.tooltip-s):before,
|
103
103
|
:host(.tooltip-se):before,
|
104
104
|
:host(.tooltip-sw):before {
|
105
|
-
right: 50%;
|
106
105
|
bottom: 100%;
|
107
|
-
margin-right: -6px;
|
108
106
|
border-bottom-color: var(--color-neutral-emphasis-plus)
|
109
107
|
}
|
110
108
|
|
@@ -118,8 +116,6 @@ class TooltipElement extends HTMLElement {
|
|
118
116
|
:host(.tooltip-ne):before,
|
119
117
|
:host(.tooltip-nw):before {
|
120
118
|
top: 100%;
|
121
|
-
right: 50%;
|
122
|
-
margin-right: -6px;
|
123
119
|
border-top-color: var(--color-neutral-emphasis-plus)
|
124
120
|
}
|
125
121
|
|
@@ -131,13 +127,14 @@ class TooltipElement extends HTMLElement {
|
|
131
127
|
|
132
128
|
:host(.tooltip-se):before,
|
133
129
|
:host(.tooltip-ne):before {
|
134
|
-
|
130
|
+
left: 0;
|
131
|
+
margin-left: ${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
135
132
|
}
|
136
133
|
|
137
134
|
:host(.tooltip-sw):before,
|
138
135
|
:host(.tooltip-nw):before {
|
139
136
|
right: 0;
|
140
|
-
margin-right:
|
137
|
+
margin-right: ${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
141
138
|
}
|
142
139
|
|
143
140
|
:host(.tooltip-w):before {
|
@@ -181,27 +178,35 @@ class TooltipElement extends HTMLElement {
|
|
181
178
|
}
|
182
179
|
connectedCallback() {
|
183
180
|
var _a;
|
181
|
+
const shadow = this.attachShadow({ mode: 'open' });
|
182
|
+
shadow.innerHTML = `
|
183
|
+
<style>
|
184
|
+
${this.styles()}
|
185
|
+
</style>
|
186
|
+
<slot></slot>
|
187
|
+
`;
|
184
188
|
this.hidden = true;
|
185
|
-
__classPrivateFieldSet(this,
|
189
|
+
__classPrivateFieldSet(this, _ToolTipElement_allowUpdatePosition, true, "f");
|
186
190
|
if (!this.id) {
|
187
191
|
this.id = `tooltip-${Date.now()}-${(Math.random() * 10000).toFixed(0)}`;
|
188
192
|
}
|
189
193
|
if (!this.control)
|
190
194
|
return;
|
191
195
|
this.setAttribute('role', 'tooltip');
|
192
|
-
(_a = __classPrivateFieldGet(this,
|
193
|
-
__classPrivateFieldSet(this,
|
194
|
-
const { signal } = __classPrivateFieldGet(this,
|
196
|
+
(_a = __classPrivateFieldGet(this, _ToolTipElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
|
197
|
+
__classPrivateFieldSet(this, _ToolTipElement_abortController, new AbortController(), "f");
|
198
|
+
const { signal } = __classPrivateFieldGet(this, _ToolTipElement_abortController, "f");
|
195
199
|
this.addEventListener('mouseleave', this, { signal });
|
196
200
|
this.control.addEventListener('mouseenter', this, { signal });
|
197
201
|
this.control.addEventListener('mouseleave', this, { signal });
|
198
202
|
this.control.addEventListener('focus', this, { signal });
|
199
203
|
this.control.addEventListener('blur', this, { signal });
|
200
204
|
this.ownerDocument.addEventListener('keydown', this, { signal });
|
205
|
+
__classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_update).call(this);
|
201
206
|
}
|
202
207
|
disconnectedCallback() {
|
203
208
|
var _a;
|
204
|
-
(_a = __classPrivateFieldGet(this,
|
209
|
+
(_a = __classPrivateFieldGet(this, _ToolTipElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
|
205
210
|
}
|
206
211
|
handleEvent(event) {
|
207
212
|
if (!this.control)
|
@@ -236,146 +241,108 @@ class TooltipElement extends HTMLElement {
|
|
236
241
|
this.control.setAttribute('aria-describedby', describedBy);
|
237
242
|
}
|
238
243
|
}
|
239
|
-
else if (name === 'hidden') {
|
240
|
-
|
241
|
-
this.classList.remove(TOOLTIP_OPEN_CLASS, ...DIRECTION_CLASSES);
|
242
|
-
}
|
243
|
-
else {
|
244
|
-
this.classList.add(TOOLTIP_OPEN_CLASS);
|
245
|
-
for (const tooltip of this.ownerDocument.querySelectorAll(this.tagName)) {
|
246
|
-
if (tooltip !== this)
|
247
|
-
tooltip.hidden = true;
|
248
|
-
}
|
249
|
-
this..call(this);
|
250
|
-
}
|
244
|
+
else if (this.isConnected && name === 'hidden') {
|
245
|
+
__classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_update).call(this);
|
251
246
|
}
|
252
247
|
else if (name === 'data-direction') {
|
253
248
|
this.classList.remove(...DIRECTION_CLASSES);
|
254
249
|
const direction = this.direction;
|
255
250
|
if (direction === 'n') {
|
256
|
-
__classPrivateFieldSet(this,
|
257
|
-
__classPrivateFieldSet(this,
|
251
|
+
__classPrivateFieldSet(this, _ToolTipElement_align, 'center', "f");
|
252
|
+
__classPrivateFieldSet(this, _ToolTipElement_side, 'outside-top', "f");
|
258
253
|
}
|
259
254
|
else if (direction === 'ne') {
|
260
|
-
__classPrivateFieldSet(this,
|
261
|
-
__classPrivateFieldSet(this,
|
255
|
+
__classPrivateFieldSet(this, _ToolTipElement_align, 'start', "f");
|
256
|
+
__classPrivateFieldSet(this, _ToolTipElement_side, 'outside-top', "f");
|
262
257
|
}
|
263
258
|
else if (direction === 'e') {
|
264
|
-
__classPrivateFieldSet(this,
|
265
|
-
__classPrivateFieldSet(this,
|
259
|
+
__classPrivateFieldSet(this, _ToolTipElement_align, 'center', "f");
|
260
|
+
__classPrivateFieldSet(this, _ToolTipElement_side, 'outside-right', "f");
|
266
261
|
}
|
267
262
|
else if (direction === 'se') {
|
268
|
-
__classPrivateFieldSet(this,
|
269
|
-
__classPrivateFieldSet(this,
|
263
|
+
__classPrivateFieldSet(this, _ToolTipElement_align, 'start', "f");
|
264
|
+
__classPrivateFieldSet(this, _ToolTipElement_side, 'outside-bottom', "f");
|
270
265
|
}
|
271
266
|
else if (direction === 's') {
|
272
|
-
__classPrivateFieldSet(this,
|
273
|
-
__classPrivateFieldSet(this,
|
267
|
+
__classPrivateFieldSet(this, _ToolTipElement_align, 'center', "f");
|
268
|
+
__classPrivateFieldSet(this, _ToolTipElement_side, 'outside-bottom', "f");
|
274
269
|
}
|
275
270
|
else if (direction === 'sw') {
|
276
|
-
__classPrivateFieldSet(this,
|
277
|
-
__classPrivateFieldSet(this,
|
271
|
+
__classPrivateFieldSet(this, _ToolTipElement_align, 'end', "f");
|
272
|
+
__classPrivateFieldSet(this, _ToolTipElement_side, 'outside-bottom', "f");
|
278
273
|
}
|
279
274
|
else if (direction === 'w') {
|
280
|
-
__classPrivateFieldSet(this,
|
281
|
-
__classPrivateFieldSet(this,
|
275
|
+
__classPrivateFieldSet(this, _ToolTipElement_align, 'center', "f");
|
276
|
+
__classPrivateFieldSet(this, _ToolTipElement_side, 'outside-left', "f");
|
282
277
|
}
|
283
278
|
else if (direction === 'nw') {
|
284
|
-
__classPrivateFieldSet(this,
|
285
|
-
__classPrivateFieldSet(this,
|
279
|
+
__classPrivateFieldSet(this, _ToolTipElement_align, 'end', "f");
|
280
|
+
__classPrivateFieldSet(this, _ToolTipElement_side, 'outside-top', "f");
|
286
281
|
}
|
287
282
|
}
|
288
283
|
}
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
const
|
297
|
-
|
298
|
-
|
299
|
-
const targetCenter = targetPosition.x + targetPosition.width / 2;
|
300
|
-
if (Math.abs(tooltipCenter - targetCenter) < 2 || anchorSide === 'outside-left' || anchorSide === 'outside-right') {
|
301
|
-
return 'center';
|
302
|
-
}
|
303
|
-
else if (tooltipPosition.left === targetPosition.left) {
|
304
|
-
return 'start';
|
284
|
+
}
|
285
|
+
_ToolTipElement_abortController = new WeakMap(), _ToolTipElement_align = new WeakMap(), _ToolTipElement_side = new WeakMap(), _ToolTipElement_allowUpdatePosition = new WeakMap(), _ToolTipElement_instances = new WeakSet(), _ToolTipElement_update = function _ToolTipElement_update() {
|
286
|
+
if (this.hidden) {
|
287
|
+
this.classList.remove(TOOLTIP_OPEN_CLASS, ...DIRECTION_CLASSES);
|
288
|
+
}
|
289
|
+
else {
|
290
|
+
this.classList.add(TOOLTIP_OPEN_CLASS);
|
291
|
+
for (const tooltip of this.ownerDocument.querySelectorAll(this.tagName)) {
|
292
|
+
if (tooltip !== this)
|
293
|
+
tooltip.hidden = true;
|
305
294
|
}
|
306
|
-
|
307
|
-
|
295
|
+
__classPrivateFieldGet(this, _ToolTipElement_instances, "m", _ToolTipElement_updatePosition).call(this);
|
296
|
+
}
|
297
|
+
}, _ToolTipElement_updatePosition = function _ToolTipElement_updatePosition() {
|
298
|
+
if (!this.control)
|
299
|
+
return;
|
300
|
+
if (!__classPrivateFieldGet(this, _ToolTipElement_allowUpdatePosition, "f") || this.hidden)
|
301
|
+
return;
|
302
|
+
const TOOLTIP_OFFSET = 10;
|
303
|
+
this.style.left = `0px`; // Ensures we have reliable tooltip width in `getAnchoredPosition`
|
304
|
+
const position = getAnchoredPosition(this, this.control, {
|
305
|
+
side: __classPrivateFieldGet(this, _ToolTipElement_side, "f"),
|
306
|
+
align: __classPrivateFieldGet(this, _ToolTipElement_align, "f"),
|
307
|
+
anchorOffset: TOOLTIP_OFFSET
|
308
|
+
});
|
309
|
+
const anchorSide = position.anchorSide;
|
310
|
+
const align = position.anchorAlign;
|
311
|
+
this.style.top = `${position.top}px`;
|
312
|
+
this.style.left = `${position.left}px`;
|
313
|
+
let direction = 's';
|
314
|
+
if (anchorSide === 'outside-left') {
|
315
|
+
direction = 'w';
|
316
|
+
}
|
317
|
+
else if (anchorSide === 'outside-right') {
|
318
|
+
direction = 'e';
|
319
|
+
}
|
320
|
+
else if (anchorSide === 'outside-top') {
|
321
|
+
if (align === 'center') {
|
322
|
+
direction = 'n';
|
308
323
|
}
|
309
|
-
else if (
|
310
|
-
|
311
|
-
return 'start';
|
312
|
-
return 'end';
|
324
|
+
else if (align === 'start') {
|
325
|
+
direction = 'ne';
|
313
326
|
}
|
314
327
|
else {
|
315
|
-
|
316
|
-
return 'end';
|
317
|
-
return 'start';
|
328
|
+
direction = 'nw';
|
318
329
|
}
|
319
330
|
}
|
320
|
-
|
321
|
-
if (
|
322
|
-
|
323
|
-
if (!__classPrivateFieldGet(this, _allowUpdatePosition) || this.hidden)
|
324
|
-
return;
|
325
|
-
const TOOLTIP_OFFSET = 10;
|
326
|
-
this.style.left = `0px`; // Ensures we have reliable tooltip width in `getAnchoredPosition`
|
327
|
-
let position = getAnchoredPosition(this, this.control, {
|
328
|
-
side: __classPrivateFieldGet(this, _side),
|
329
|
-
align: __classPrivateFieldGet(this, _align),
|
330
|
-
anchorOffset: TOOLTIP_OFFSET
|
331
|
-
});
|
332
|
-
let anchorSide = position.anchorSide;
|
333
|
-
// We need to set tooltip position in order to determine ideal align.
|
334
|
-
this.style.top = `${position.top}px`;
|
335
|
-
this.style.left = `${position.left}px`;
|
336
|
-
let direction = 's';
|
337
|
-
const align = this..call(this, anchorSide);
|
338
|
-
if (!align)
|
339
|
-
return;
|
340
|
-
this.style.left = `0px`; // Reset tooltip position again to ensure accurate width in `getAnchoredPosition`
|
341
|
-
position = getAnchoredPosition(this, this.control, { side: anchorSide, align, anchorOffset: TOOLTIP_OFFSET });
|
342
|
-
anchorSide = position.anchorSide;
|
343
|
-
this.style.top = `${position.top}px`;
|
344
|
-
this.style.left = `${position.left}px`;
|
345
|
-
if (anchorSide === 'outside-left') {
|
346
|
-
direction = 'w';
|
347
|
-
}
|
348
|
-
else if (anchorSide === 'outside-right') {
|
349
|
-
direction = 'e';
|
331
|
+
else {
|
332
|
+
if (align === 'center') {
|
333
|
+
direction = 's';
|
350
334
|
}
|
351
|
-
else if (
|
352
|
-
|
353
|
-
direction = 'n';
|
354
|
-
}
|
355
|
-
else if (align === 'start') {
|
356
|
-
direction = 'ne';
|
357
|
-
}
|
358
|
-
else {
|
359
|
-
direction = 'nw';
|
360
|
-
}
|
335
|
+
else if (align === 'start') {
|
336
|
+
direction = 'se';
|
361
337
|
}
|
362
338
|
else {
|
363
|
-
|
364
|
-
direction = 's';
|
365
|
-
}
|
366
|
-
else if (align === 'start') {
|
367
|
-
direction = 'se';
|
368
|
-
}
|
369
|
-
else {
|
370
|
-
direction = 'sw';
|
371
|
-
}
|
339
|
+
direction = 'sw';
|
372
340
|
}
|
373
|
-
this.classList.add(`tooltip-${direction}`);
|
374
341
|
}
|
375
|
-
}
|
376
|
-
|
377
|
-
|
342
|
+
this.classList.add(`tooltip-${direction}`);
|
343
|
+
};
|
344
|
+
ToolTipElement.observedAttributes = ['data-type', 'data-direction', 'id', 'hidden'];
|
378
345
|
if (!window.customElements.get('tool-tip')) {
|
379
|
-
window.
|
380
|
-
window.customElements.define('tool-tip',
|
346
|
+
window.ToolTipElement = ToolTipElement;
|
347
|
+
window.customElements.define('tool-tip', ToolTipElement);
|
381
348
|
}
|
@@ -2,6 +2,7 @@ import type {AnchorAlignment, AnchorSide} from '@primer/behaviors'
|
|
2
2
|
import {getAnchoredPosition} from '@primer/behaviors'
|
3
3
|
|
4
4
|
const TOOLTIP_OPEN_CLASS = 'tooltip-open'
|
5
|
+
const TOOLTIP_ARROW_EDGE_OFFSET = 6
|
5
6
|
|
6
7
|
type Direction = 'n' | 's' | 'e' | 'w' | 'ne' | 'se' | 'nw' | 'sw'
|
7
8
|
|
@@ -16,7 +17,7 @@ const DIRECTION_CLASSES = [
|
|
16
17
|
'tooltip-sw'
|
17
18
|
]
|
18
19
|
|
19
|
-
class
|
20
|
+
class ToolTipElement extends HTMLElement {
|
20
21
|
styles() {
|
21
22
|
return `
|
22
23
|
:host {
|
@@ -38,7 +39,8 @@ class TooltipElement extends HTMLElement {
|
|
38
39
|
opacity: 0;
|
39
40
|
max-width: 250px;
|
40
41
|
word-wrap: break-word;
|
41
|
-
white-space: normal
|
42
|
+
white-space: normal;
|
43
|
+
width: max-content;
|
42
44
|
}
|
43
45
|
|
44
46
|
:host:before{
|
@@ -77,12 +79,16 @@ class TooltipElement extends HTMLElement {
|
|
77
79
|
animation-delay: .4s
|
78
80
|
}
|
79
81
|
|
82
|
+
:host(.tooltip-s):before,
|
83
|
+
:host(.tooltip-n):before {
|
84
|
+
right: 50%;
|
85
|
+
margin-right: -${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
86
|
+
}
|
87
|
+
|
80
88
|
:host(.tooltip-s):before,
|
81
89
|
:host(.tooltip-se):before,
|
82
90
|
:host(.tooltip-sw):before {
|
83
|
-
right: 50%;
|
84
91
|
bottom: 100%;
|
85
|
-
margin-right: -6px;
|
86
92
|
border-bottom-color: var(--color-neutral-emphasis-plus)
|
87
93
|
}
|
88
94
|
|
@@ -96,8 +102,6 @@ class TooltipElement extends HTMLElement {
|
|
96
102
|
:host(.tooltip-ne):before,
|
97
103
|
:host(.tooltip-nw):before {
|
98
104
|
top: 100%;
|
99
|
-
right: 50%;
|
100
|
-
margin-right: -6px;
|
101
105
|
border-top-color: var(--color-neutral-emphasis-plus)
|
102
106
|
}
|
103
107
|
|
@@ -109,13 +113,14 @@ class TooltipElement extends HTMLElement {
|
|
109
113
|
|
110
114
|
:host(.tooltip-se):before,
|
111
115
|
:host(.tooltip-ne):before {
|
112
|
-
|
116
|
+
left: 0;
|
117
|
+
margin-left: ${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
113
118
|
}
|
114
119
|
|
115
120
|
:host(.tooltip-sw):before,
|
116
121
|
:host(.tooltip-nw):before {
|
117
122
|
right: 0;
|
118
|
-
margin-right:
|
123
|
+
margin-right: ${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
119
124
|
}
|
120
125
|
|
121
126
|
:host(.tooltip-w):before {
|
@@ -170,8 +175,7 @@ class TooltipElement extends HTMLElement {
|
|
170
175
|
return this.ownerDocument.getElementById(this.htmlFor)
|
171
176
|
}
|
172
177
|
|
173
|
-
|
174
|
-
super()
|
178
|
+
connectedCallback() {
|
175
179
|
const shadow = this.attachShadow({mode: 'open'})
|
176
180
|
shadow.innerHTML = `
|
177
181
|
<style>
|
@@ -179,9 +183,6 @@ class TooltipElement extends HTMLElement {
|
|
179
183
|
</style>
|
180
184
|
<slot></slot>
|
181
185
|
`
|
182
|
-
}
|
183
|
-
|
184
|
-
connectedCallback() {
|
185
186
|
this.hidden = true
|
186
187
|
this.#allowUpdatePosition = true
|
187
188
|
|
@@ -203,6 +204,7 @@ class TooltipElement extends HTMLElement {
|
|
203
204
|
this.control.addEventListener('focus', this, {signal})
|
204
205
|
this.control.addEventListener('blur', this, {signal})
|
205
206
|
this.ownerDocument.addEventListener('keydown', this, {signal})
|
207
|
+
this.#update()
|
206
208
|
}
|
207
209
|
|
208
210
|
disconnectedCallback() {
|
@@ -231,6 +233,18 @@ class TooltipElement extends HTMLElement {
|
|
231
233
|
|
232
234
|
static observedAttributes = ['data-type', 'data-direction', 'id', 'hidden']
|
233
235
|
|
236
|
+
#update() {
|
237
|
+
if (this.hidden) {
|
238
|
+
this.classList.remove(TOOLTIP_OPEN_CLASS, ...DIRECTION_CLASSES)
|
239
|
+
} else {
|
240
|
+
this.classList.add(TOOLTIP_OPEN_CLASS)
|
241
|
+
for (const tooltip of this.ownerDocument.querySelectorAll<HTMLElement>(this.tagName)) {
|
242
|
+
if (tooltip !== this) tooltip.hidden = true
|
243
|
+
}
|
244
|
+
this.#updatePosition()
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
234
248
|
attributeChangedCallback(name: string) {
|
235
249
|
if (name === 'id' || name === 'data-type') {
|
236
250
|
if (!this.id || !this.control) return
|
@@ -241,16 +255,8 @@ class TooltipElement extends HTMLElement {
|
|
241
255
|
describedBy ? (describedBy = `${describedBy} ${this.id}`) : (describedBy = this.id)
|
242
256
|
this.control.setAttribute('aria-describedby', describedBy)
|
243
257
|
}
|
244
|
-
} else if (name === 'hidden') {
|
245
|
-
|
246
|
-
this.classList.remove(TOOLTIP_OPEN_CLASS, ...DIRECTION_CLASSES)
|
247
|
-
} else {
|
248
|
-
this.classList.add(TOOLTIP_OPEN_CLASS)
|
249
|
-
for (const tooltip of this.ownerDocument.querySelectorAll<HTMLElement>(this.tagName)) {
|
250
|
-
if (tooltip !== this) tooltip.hidden = true
|
251
|
-
}
|
252
|
-
this.#updatePosition()
|
253
|
-
}
|
258
|
+
} else if (this.isConnected && name === 'hidden') {
|
259
|
+
this.#update()
|
254
260
|
} else if (name === 'data-direction') {
|
255
261
|
this.classList.remove(...DIRECTION_CLASSES)
|
256
262
|
const direction = this.direction
|
@@ -282,34 +288,6 @@ class TooltipElement extends HTMLElement {
|
|
282
288
|
}
|
283
289
|
}
|
284
290
|
|
285
|
-
// `getAnchoredPosition` may calibrate `anchoredSide` but does not recalibrate `align`.
|
286
|
-
// Therefore, we need to determine which `align` is best based on the initial `getAnchoredPosition` calcluation.
|
287
|
-
// Related: https://github.com/primer/behaviors/issues/63
|
288
|
-
#adjustedAnchorAlignment(anchorSide: AnchorSide): AnchorAlignment | undefined {
|
289
|
-
if (!this.control) return
|
290
|
-
|
291
|
-
const tooltipPosition = this.getBoundingClientRect()
|
292
|
-
const targetPosition = this.control.getBoundingClientRect()
|
293
|
-
const tooltipWidth = tooltipPosition.width
|
294
|
-
|
295
|
-
const tooltipCenter = tooltipPosition.left + tooltipWidth / 2
|
296
|
-
const targetCenter = targetPosition.x + targetPosition.width / 2
|
297
|
-
|
298
|
-
if (Math.abs(tooltipCenter - targetCenter) < 2 || anchorSide === 'outside-left' || anchorSide === 'outside-right') {
|
299
|
-
return 'center'
|
300
|
-
} else if (tooltipPosition.left === targetPosition.left) {
|
301
|
-
return 'start'
|
302
|
-
} else if (tooltipPosition.right === targetPosition.right) {
|
303
|
-
return 'end'
|
304
|
-
} else if (tooltipCenter < targetCenter) {
|
305
|
-
if (tooltipPosition.left === 0) return 'start'
|
306
|
-
return 'end'
|
307
|
-
} else {
|
308
|
-
if (tooltipPosition.right === 0) return 'end'
|
309
|
-
return 'start'
|
310
|
-
}
|
311
|
-
}
|
312
|
-
|
313
291
|
#updatePosition() {
|
314
292
|
if (!this.control) return
|
315
293
|
if (!this.#allowUpdatePosition || this.hidden) return
|
@@ -317,27 +295,19 @@ class TooltipElement extends HTMLElement {
|
|
317
295
|
const TOOLTIP_OFFSET = 10
|
318
296
|
|
319
297
|
this.style.left = `0px` // Ensures we have reliable tooltip width in `getAnchoredPosition`
|
320
|
-
|
298
|
+
|
299
|
+
const position = getAnchoredPosition(this, this.control, {
|
321
300
|
side: this.#side,
|
322
301
|
align: this.#align,
|
323
302
|
anchorOffset: TOOLTIP_OFFSET
|
324
303
|
})
|
325
|
-
|
304
|
+
const anchorSide = position.anchorSide
|
305
|
+
const align = position.anchorAlign
|
326
306
|
|
327
|
-
// We need to set tooltip position in order to determine ideal align.
|
328
307
|
this.style.top = `${position.top}px`
|
329
308
|
this.style.left = `${position.left}px`
|
330
|
-
let direction: Direction = 's'
|
331
|
-
|
332
|
-
const align = this.#adjustedAnchorAlignment(anchorSide)
|
333
|
-
if (!align) return
|
334
309
|
|
335
|
-
|
336
|
-
position = getAnchoredPosition(this, this.control, {side: anchorSide, align, anchorOffset: TOOLTIP_OFFSET})
|
337
|
-
anchorSide = position.anchorSide
|
338
|
-
|
339
|
-
this.style.top = `${position.top}px`
|
340
|
-
this.style.left = `${position.left}px`
|
310
|
+
let direction: Direction = 's'
|
341
311
|
|
342
312
|
if (anchorSide === 'outside-left') {
|
343
313
|
direction = 'w'
|
@@ -366,12 +336,12 @@ class TooltipElement extends HTMLElement {
|
|
366
336
|
}
|
367
337
|
|
368
338
|
if (!window.customElements.get('tool-tip')) {
|
369
|
-
window.
|
370
|
-
window.customElements.define('tool-tip',
|
339
|
+
window.ToolTipElement = ToolTipElement
|
340
|
+
window.customElements.define('tool-tip', ToolTipElement)
|
371
341
|
}
|
372
342
|
|
373
343
|
declare global {
|
374
344
|
interface Window {
|
375
|
-
|
345
|
+
ToolTipElement: typeof ToolTipElement
|
376
346
|
}
|
377
347
|
}
|
@@ -70,6 +70,14 @@ module Primer
|
|
70
70
|
# <%= render(Primer::Alpha::Tooltip.new(for_id: "Northwest", type: :description, text: "This is a Northwest-facing tooltip and is responsive.", direction: :nw)) %>
|
71
71
|
# <%= render(Primer::ButtonComponent.new(id: "Southwest", m: 2)) { "Southwest" } %>
|
72
72
|
# <%= render(Primer::Alpha::Tooltip.new(for_id: "Southwest", type: :description, text: "This is a Southwest-facing tooltip and is responsive.", direction: :sw)) %>
|
73
|
+
# @example With relative parent
|
74
|
+
# @description
|
75
|
+
# When the tooltip and trigger element have a parent container with `relative: position`, it should not affect width of the tooltip.
|
76
|
+
# @code
|
77
|
+
# <span style="position: relative;">
|
78
|
+
# <%= render(Primer::ButtonComponent.new(id: "test-button", scheme: :primary)) { "Test" } %>
|
79
|
+
# <%= render(Primer::Alpha::Tooltip.new(for_id: "test-button", type: :description, text: "This tooltip should take up the full width", direction: :ne)) %>
|
80
|
+
# </span>
|
73
81
|
# @param for_id [String] The ID of the element that the tooltip should be attached to.
|
74
82
|
# @param type [Symbol] <%= one_of(Primer::Alpha::Tooltip::TYPE_OPTIONS) %>
|
75
83
|
# @param direction [Symbol] <%= one_of(Primer::Alpha::Tooltip::DIRECTION_OPTIONS) %>
|
@@ -1,14 +1,24 @@
|
|
1
1
|
<%= render Primer::BaseComponent.new(**@system_arguments) do %>
|
2
|
-
<label for="<%= @input_id %>">
|
2
|
+
<label for="<%= @input_id %>" class="<%= @label_classes %>">
|
3
3
|
<% if @is_label_visible %>
|
4
4
|
<%= @label_text %>
|
5
5
|
<% else %>
|
6
6
|
<span class="sr-only"><%= @label_text %></span>
|
7
7
|
<% end %>
|
8
|
-
<% if icon.present? %>
|
9
|
-
<%= icon %>
|
10
|
-
<% end %>
|
11
8
|
</label>
|
12
|
-
<
|
13
|
-
|
9
|
+
<span class="autocomplete-body">
|
10
|
+
<% if @with_icon %>
|
11
|
+
<div class="form-control autocomplete-embedded-icon-wrap">
|
12
|
+
<%= render Primer::OcticonComponent.new(:search) %>
|
13
|
+
<% end %>
|
14
|
+
<%= input %>
|
15
|
+
<% if @is_clearable %>
|
16
|
+
<button id="<%= @input_id %>-clear" class="btn-octicon" aria-label="Clear"><%= primer_octicon "x" %></button>
|
17
|
+
<% end %>
|
18
|
+
<% if @with_icon %>
|
19
|
+
</div>
|
20
|
+
<% end %>
|
21
|
+
<%= results %>
|
22
|
+
</span>
|
23
|
+
<div id="<%= @list_id %>-feedback" class="sr-only"></div>
|
14
24
|
<% end %>
|