primer_view_components 0.0.69 → 0.0.70
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 +36 -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.js +121 -121
- data/app/components/primer/alpha/tooltip.rb +8 -0
- data/app/components/primer/alpha/tooltip.ts +13 -7
- 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 +4 -0
- data/app/components/primer/link_component.rb +29 -4
- data/lib/primer/classify/utilities.yml +8 -0
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/tasks/docs.rake +3 -1
- data/static/arguments.yml +18 -0
- data/static/classes.yml +4 -0
- metadata +8 -7
@@ -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_adjustedAnchorAlignment, _TooltipElement_updatePosition;
|
15
13
|
import { getAnchoredPosition } from '@primer/behaviors';
|
16
14
|
const TOOLTIP_OPEN_CLASS = 'tooltip-open';
|
15
|
+
const TOOLTIP_ARROW_EDGE_OFFSET = 10;
|
17
16
|
const DIRECTION_CLASSES = [
|
18
17
|
'tooltip-n',
|
19
18
|
'tooltip-s',
|
@@ -27,10 +26,11 @@ const DIRECTION_CLASSES = [
|
|
27
26
|
class TooltipElement extends HTMLElement {
|
28
27
|
constructor() {
|
29
28
|
super();
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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);
|
34
34
|
const shadow = this.attachShadow({ mode: 'open' });
|
35
35
|
shadow.innerHTML = `
|
36
36
|
<style>
|
@@ -60,7 +60,8 @@ class TooltipElement extends HTMLElement {
|
|
60
60
|
opacity: 0;
|
61
61
|
max-width: 250px;
|
62
62
|
word-wrap: break-word;
|
63
|
-
white-space: normal
|
63
|
+
white-space: normal;
|
64
|
+
width: max-content;
|
64
65
|
}
|
65
66
|
|
66
67
|
:host:before{
|
@@ -99,12 +100,16 @@ class TooltipElement extends HTMLElement {
|
|
99
100
|
animation-delay: .4s
|
100
101
|
}
|
101
102
|
|
103
|
+
:host(.tooltip-s):before,
|
104
|
+
:host(.tooltip-n):before {
|
105
|
+
right: 50%;
|
106
|
+
}
|
107
|
+
|
102
108
|
:host(.tooltip-s):before,
|
103
109
|
:host(.tooltip-se):before,
|
104
110
|
:host(.tooltip-sw):before {
|
105
|
-
right: 50%;
|
106
111
|
bottom: 100%;
|
107
|
-
margin-right:
|
112
|
+
margin-right: -${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
108
113
|
border-bottom-color: var(--color-neutral-emphasis-plus)
|
109
114
|
}
|
110
115
|
|
@@ -118,8 +123,7 @@ class TooltipElement extends HTMLElement {
|
|
118
123
|
:host(.tooltip-ne):before,
|
119
124
|
:host(.tooltip-nw):before {
|
120
125
|
top: 100%;
|
121
|
-
right:
|
122
|
-
margin-right: -6px;
|
126
|
+
margin-right: -${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
123
127
|
border-top-color: var(--color-neutral-emphasis-plus)
|
124
128
|
}
|
125
129
|
|
@@ -131,13 +135,14 @@ class TooltipElement extends HTMLElement {
|
|
131
135
|
|
132
136
|
:host(.tooltip-se):before,
|
133
137
|
:host(.tooltip-ne):before {
|
134
|
-
|
138
|
+
left: 0;
|
139
|
+
margin-left: ${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
135
140
|
}
|
136
141
|
|
137
142
|
:host(.tooltip-sw):before,
|
138
143
|
:host(.tooltip-nw):before {
|
139
144
|
right: 0;
|
140
|
-
margin-right:
|
145
|
+
margin-right: ${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
141
146
|
}
|
142
147
|
|
143
148
|
:host(.tooltip-w):before {
|
@@ -182,16 +187,16 @@ class TooltipElement extends HTMLElement {
|
|
182
187
|
connectedCallback() {
|
183
188
|
var _a;
|
184
189
|
this.hidden = true;
|
185
|
-
__classPrivateFieldSet(this,
|
190
|
+
__classPrivateFieldSet(this, _TooltipElement_allowUpdatePosition, true, "f");
|
186
191
|
if (!this.id) {
|
187
192
|
this.id = `tooltip-${Date.now()}-${(Math.random() * 10000).toFixed(0)}`;
|
188
193
|
}
|
189
194
|
if (!this.control)
|
190
195
|
return;
|
191
196
|
this.setAttribute('role', 'tooltip');
|
192
|
-
(_a = __classPrivateFieldGet(this,
|
193
|
-
__classPrivateFieldSet(this,
|
194
|
-
const { signal } = __classPrivateFieldGet(this,
|
197
|
+
(_a = __classPrivateFieldGet(this, _TooltipElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
|
198
|
+
__classPrivateFieldSet(this, _TooltipElement_abortController, new AbortController(), "f");
|
199
|
+
const { signal } = __classPrivateFieldGet(this, _TooltipElement_abortController, "f");
|
195
200
|
this.addEventListener('mouseleave', this, { signal });
|
196
201
|
this.control.addEventListener('mouseenter', this, { signal });
|
197
202
|
this.control.addEventListener('mouseleave', this, { signal });
|
@@ -201,7 +206,7 @@ class TooltipElement extends HTMLElement {
|
|
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)
|
@@ -246,134 +251,129 @@ class TooltipElement extends HTMLElement {
|
|
246
251
|
if (tooltip !== this)
|
247
252
|
tooltip.hidden = true;
|
248
253
|
}
|
249
|
-
this
|
254
|
+
__classPrivateFieldGet(this, _TooltipElement_instances, "m", _TooltipElement_updatePosition).call(this);
|
250
255
|
}
|
251
256
|
}
|
252
257
|
else if (name === 'data-direction') {
|
253
258
|
this.classList.remove(...DIRECTION_CLASSES);
|
254
259
|
const direction = this.direction;
|
255
260
|
if (direction === 'n') {
|
256
|
-
__classPrivateFieldSet(this,
|
257
|
-
__classPrivateFieldSet(this,
|
261
|
+
__classPrivateFieldSet(this, _TooltipElement_align, 'center', "f");
|
262
|
+
__classPrivateFieldSet(this, _TooltipElement_side, 'outside-top', "f");
|
258
263
|
}
|
259
264
|
else if (direction === 'ne') {
|
260
|
-
__classPrivateFieldSet(this,
|
261
|
-
__classPrivateFieldSet(this,
|
265
|
+
__classPrivateFieldSet(this, _TooltipElement_align, 'start', "f");
|
266
|
+
__classPrivateFieldSet(this, _TooltipElement_side, 'outside-top', "f");
|
262
267
|
}
|
263
268
|
else if (direction === 'e') {
|
264
|
-
__classPrivateFieldSet(this,
|
265
|
-
__classPrivateFieldSet(this,
|
269
|
+
__classPrivateFieldSet(this, _TooltipElement_align, 'center', "f");
|
270
|
+
__classPrivateFieldSet(this, _TooltipElement_side, 'outside-right', "f");
|
266
271
|
}
|
267
272
|
else if (direction === 'se') {
|
268
|
-
__classPrivateFieldSet(this,
|
269
|
-
__classPrivateFieldSet(this,
|
273
|
+
__classPrivateFieldSet(this, _TooltipElement_align, 'start', "f");
|
274
|
+
__classPrivateFieldSet(this, _TooltipElement_side, 'outside-bottom', "f");
|
270
275
|
}
|
271
276
|
else if (direction === 's') {
|
272
|
-
__classPrivateFieldSet(this,
|
273
|
-
__classPrivateFieldSet(this,
|
277
|
+
__classPrivateFieldSet(this, _TooltipElement_align, 'center', "f");
|
278
|
+
__classPrivateFieldSet(this, _TooltipElement_side, 'outside-bottom', "f");
|
274
279
|
}
|
275
280
|
else if (direction === 'sw') {
|
276
|
-
__classPrivateFieldSet(this,
|
277
|
-
__classPrivateFieldSet(this,
|
281
|
+
__classPrivateFieldSet(this, _TooltipElement_align, 'end', "f");
|
282
|
+
__classPrivateFieldSet(this, _TooltipElement_side, 'outside-bottom', "f");
|
278
283
|
}
|
279
284
|
else if (direction === 'w') {
|
280
|
-
__classPrivateFieldSet(this,
|
281
|
-
__classPrivateFieldSet(this,
|
285
|
+
__classPrivateFieldSet(this, _TooltipElement_align, 'center', "f");
|
286
|
+
__classPrivateFieldSet(this, _TooltipElement_side, 'outside-left', "f");
|
282
287
|
}
|
283
288
|
else if (direction === 'nw') {
|
284
|
-
__classPrivateFieldSet(this,
|
285
|
-
__classPrivateFieldSet(this,
|
289
|
+
__classPrivateFieldSet(this, _TooltipElement_align, 'end', "f");
|
290
|
+
__classPrivateFieldSet(this, _TooltipElement_side, 'outside-top', "f");
|
286
291
|
}
|
287
292
|
}
|
288
293
|
}
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
294
|
+
}
|
295
|
+
_TooltipElement_abortController = new WeakMap(), _TooltipElement_align = new WeakMap(), _TooltipElement_side = new WeakMap(), _TooltipElement_allowUpdatePosition = new WeakMap(), _TooltipElement_instances = new WeakSet(), _TooltipElement_adjustedAnchorAlignment = function _TooltipElement_adjustedAnchorAlignment(anchorSide) {
|
296
|
+
if (!this.control)
|
297
|
+
return;
|
298
|
+
const tooltipPosition = this.getBoundingClientRect();
|
299
|
+
const targetPosition = this.control.getBoundingClientRect();
|
300
|
+
const tooltipWidth = tooltipPosition.width;
|
301
|
+
const tooltipCenter = tooltipPosition.left + tooltipWidth / 2;
|
302
|
+
const targetCenter = targetPosition.x + targetPosition.width / 2;
|
303
|
+
if (Math.abs(tooltipCenter - targetCenter) < 2 || anchorSide === 'outside-left' || anchorSide === 'outside-right') {
|
304
|
+
return 'center';
|
305
|
+
}
|
306
|
+
else if (tooltipPosition.left === targetPosition.left) {
|
307
|
+
return 'start';
|
308
|
+
}
|
309
|
+
else if (tooltipPosition.right === targetPosition.right) {
|
310
|
+
return 'end';
|
311
|
+
}
|
312
|
+
else if (tooltipCenter < targetCenter) {
|
313
|
+
if (tooltipPosition.left === 0)
|
304
314
|
return 'start';
|
305
|
-
|
306
|
-
|
315
|
+
return 'end';
|
316
|
+
}
|
317
|
+
else {
|
318
|
+
if (tooltipPosition.right === 0)
|
307
319
|
return 'end';
|
320
|
+
return 'start';
|
321
|
+
}
|
322
|
+
}, _TooltipElement_updatePosition = function _TooltipElement_updatePosition() {
|
323
|
+
if (!this.control)
|
324
|
+
return;
|
325
|
+
if (!__classPrivateFieldGet(this, _TooltipElement_allowUpdatePosition, "f") || this.hidden)
|
326
|
+
return;
|
327
|
+
const TOOLTIP_OFFSET = 10;
|
328
|
+
this.style.left = `0px`; // Ensures we have reliable tooltip width in `getAnchoredPosition`
|
329
|
+
let position = getAnchoredPosition(this, this.control, {
|
330
|
+
side: __classPrivateFieldGet(this, _TooltipElement_side, "f"),
|
331
|
+
align: __classPrivateFieldGet(this, _TooltipElement_align, "f"),
|
332
|
+
anchorOffset: TOOLTIP_OFFSET
|
333
|
+
});
|
334
|
+
let anchorSide = position.anchorSide;
|
335
|
+
// We need to set tooltip position in order to determine ideal align.
|
336
|
+
this.style.top = `${position.top}px`;
|
337
|
+
this.style.left = `${position.left}px`;
|
338
|
+
let direction = 's';
|
339
|
+
const align = __classPrivateFieldGet(this, _TooltipElement_instances, "m", _TooltipElement_adjustedAnchorAlignment).call(this, anchorSide);
|
340
|
+
if (!align)
|
341
|
+
return;
|
342
|
+
this.style.left = `0px`; // Reset tooltip position again to ensure accurate width in `getAnchoredPosition`
|
343
|
+
position = getAnchoredPosition(this, this.control, { side: anchorSide, align, anchorOffset: TOOLTIP_OFFSET });
|
344
|
+
anchorSide = position.anchorSide;
|
345
|
+
this.style.top = `${position.top}px`;
|
346
|
+
this.style.left = `${position.left}px`;
|
347
|
+
if (anchorSide === 'outside-left') {
|
348
|
+
direction = 'w';
|
349
|
+
}
|
350
|
+
else if (anchorSide === 'outside-right') {
|
351
|
+
direction = 'e';
|
352
|
+
}
|
353
|
+
else if (anchorSide === 'outside-top') {
|
354
|
+
if (align === 'center') {
|
355
|
+
direction = 'n';
|
308
356
|
}
|
309
|
-
else if (
|
310
|
-
|
311
|
-
return 'start';
|
312
|
-
return 'end';
|
357
|
+
else if (align === 'start') {
|
358
|
+
direction = 'ne';
|
313
359
|
}
|
314
360
|
else {
|
315
|
-
|
316
|
-
return 'end';
|
317
|
-
return 'start';
|
361
|
+
direction = 'nw';
|
318
362
|
}
|
319
363
|
}
|
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';
|
364
|
+
else {
|
365
|
+
if (align === 'center') {
|
366
|
+
direction = 's';
|
347
367
|
}
|
348
|
-
else if (
|
349
|
-
direction = '
|
350
|
-
}
|
351
|
-
else if (anchorSide === 'outside-top') {
|
352
|
-
if (align === 'center') {
|
353
|
-
direction = 'n';
|
354
|
-
}
|
355
|
-
else if (align === 'start') {
|
356
|
-
direction = 'ne';
|
357
|
-
}
|
358
|
-
else {
|
359
|
-
direction = 'nw';
|
360
|
-
}
|
368
|
+
else if (align === 'start') {
|
369
|
+
direction = 'se';
|
361
370
|
}
|
362
371
|
else {
|
363
|
-
|
364
|
-
direction = 's';
|
365
|
-
}
|
366
|
-
else if (align === 'start') {
|
367
|
-
direction = 'se';
|
368
|
-
}
|
369
|
-
else {
|
370
|
-
direction = 'sw';
|
371
|
-
}
|
372
|
+
direction = 'sw';
|
372
373
|
}
|
373
|
-
this.classList.add(`tooltip-${direction}`);
|
374
374
|
}
|
375
|
-
}
|
376
|
-
|
375
|
+
this.classList.add(`tooltip-${direction}`);
|
376
|
+
};
|
377
377
|
TooltipElement.observedAttributes = ['data-type', 'data-direction', 'id', 'hidden'];
|
378
378
|
if (!window.customElements.get('tool-tip')) {
|
379
379
|
window.TooltipElement = TooltipElement;
|
@@ -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) %>
|
@@ -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 = 10
|
5
6
|
|
6
7
|
type Direction = 'n' | 's' | 'e' | 'w' | 'ne' | 'se' | 'nw' | 'sw'
|
7
8
|
|
@@ -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
|
+
}
|
86
|
+
|
80
87
|
:host(.tooltip-s):before,
|
81
88
|
:host(.tooltip-se):before,
|
82
89
|
:host(.tooltip-sw):before {
|
83
|
-
right: 50%;
|
84
90
|
bottom: 100%;
|
85
|
-
margin-right:
|
91
|
+
margin-right: -${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
86
92
|
border-bottom-color: var(--color-neutral-emphasis-plus)
|
87
93
|
}
|
88
94
|
|
@@ -96,8 +102,7 @@ class TooltipElement extends HTMLElement {
|
|
96
102
|
:host(.tooltip-ne):before,
|
97
103
|
:host(.tooltip-nw):before {
|
98
104
|
top: 100%;
|
99
|
-
right:
|
100
|
-
margin-right: -6px;
|
105
|
+
margin-right: -${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
101
106
|
border-top-color: var(--color-neutral-emphasis-plus)
|
102
107
|
}
|
103
108
|
|
@@ -109,13 +114,14 @@ class TooltipElement extends HTMLElement {
|
|
109
114
|
|
110
115
|
:host(.tooltip-se):before,
|
111
116
|
:host(.tooltip-ne):before {
|
112
|
-
|
117
|
+
left: 0;
|
118
|
+
margin-left: ${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
113
119
|
}
|
114
120
|
|
115
121
|
:host(.tooltip-sw):before,
|
116
122
|
:host(.tooltip-nw):before {
|
117
123
|
right: 0;
|
118
|
-
margin-right:
|
124
|
+
margin-right: ${TOOLTIP_ARROW_EDGE_OFFSET}px;
|
119
125
|
}
|
120
126
|
|
121
127
|
:host(.tooltip-w):before {
|
@@ -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 %>
|
@@ -13,10 +13,7 @@ module Primer
|
|
13
13
|
# be used unless there is compelling reason not to. A placeholder is not a label.
|
14
14
|
class AutoComplete < Primer::Component
|
15
15
|
status :beta
|
16
|
-
|
17
|
-
# Optional icon to be rendered before the input. Has the same arguments as <%= link_to_component(Primer::OcticonComponent) %>.
|
18
|
-
renders_one :icon, Primer::OcticonComponent
|
19
|
-
|
16
|
+
#
|
20
17
|
# Customizable results list.
|
21
18
|
#
|
22
19
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
@@ -32,14 +29,79 @@ module Primer
|
|
32
29
|
Primer::BaseComponent.new(**system_arguments)
|
33
30
|
}
|
34
31
|
|
32
|
+
# Customizable input used to search for results.
|
33
|
+
# It is preferred to use this slot sparingly - it will be created by default if not explicity added.
|
34
|
+
#
|
35
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
36
|
+
renders_one :input, lambda { |**system_arguments|
|
37
|
+
sanitized_args = deny_tag_argument(**system_arguments)
|
38
|
+
sanitized_args = deny_single_argument(:autofocus, "autofocus is not allowed for accessibility reasons. See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus#accessibility_considerations for more information.", **sanitized_args)
|
39
|
+
deny_aria_key(
|
40
|
+
:label,
|
41
|
+
"instead of `aria-label`, include `label_text` and set `is_label_visible` to `false` on the component initializer.",
|
42
|
+
**sanitized_args
|
43
|
+
)
|
44
|
+
deny_single_argument(
|
45
|
+
:id,
|
46
|
+
"`id` will always be set to @input_id.",
|
47
|
+
**sanitized_args
|
48
|
+
)
|
49
|
+
deny_single_argument(
|
50
|
+
:name,
|
51
|
+
"Set @input_name on the component initializer instead with `input_name`.",
|
52
|
+
**sanitized_args
|
53
|
+
)
|
54
|
+
sanitized_args[:id] = @input_id
|
55
|
+
sanitized_args[:name] = @input_name
|
56
|
+
sanitized_args[:tag] = :input
|
57
|
+
sanitized_args[:autocomplete] = "off"
|
58
|
+
|
59
|
+
sanitized_args[:type] = :text
|
60
|
+
sanitized_args[:classes] = class_names(
|
61
|
+
"form-control",
|
62
|
+
sanitized_args[:classes]
|
63
|
+
)
|
64
|
+
|
65
|
+
Primer::BaseComponent.new(**sanitized_args)
|
66
|
+
}
|
67
|
+
|
35
68
|
# @example Default
|
36
|
-
#
|
69
|
+
# @description
|
70
|
+
# Labels are stacked by default.
|
71
|
+
# @code
|
72
|
+
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--default", list_id: "fruits-popup--default")) %>
|
73
|
+
#
|
74
|
+
# @example With inline label
|
75
|
+
# @description
|
76
|
+
# Labels can be inline by setting `is_label_inline: true`. However, labels will always become stacked on smaller screen sizes.
|
77
|
+
# @code
|
78
|
+
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", is_label_inline: true, input_id: "fruits-input--inline-label", list_id: "fruits-popup--inline-label")) %>
|
79
|
+
#
|
80
|
+
# @example With non-visible label
|
81
|
+
# @description
|
82
|
+
# A non-visible label may be rendered with `is_label_visible: false`, but it is highly discouraged. See <%= link_to_accessibility %>.
|
83
|
+
# @code
|
84
|
+
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--non-visible-label", list_id: "fruits-popup--non-visible-label", is_label_visible: false)) %>
|
85
|
+
#
|
86
|
+
# @example With icon
|
87
|
+
# @description
|
88
|
+
# To display a search icon, set `with_icon` to `true`.
|
89
|
+
# @code
|
90
|
+
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", list_id: "fruits-popup--icon", input_id: "fruits-input--icon", with_icon: true)) %>
|
91
|
+
#
|
92
|
+
# @example With icon and non-visible label
|
93
|
+
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", list_id: "fruits-popup--icon-no-label", input_id: "fruits-input--icon-no-label", with_icon: true, is_label_visible: false)) %>
|
37
94
|
#
|
38
|
-
# @example With
|
39
|
-
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input
|
95
|
+
# @example With clear button
|
96
|
+
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--clear", list_id: "fruits-popup--clear", is_clearable: true)) %>
|
40
97
|
#
|
41
|
-
# @example With
|
42
|
-
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input-
|
98
|
+
# @example With custom classes for the input
|
99
|
+
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--custom-input", list_id: "fruits-popup--custom-input")) do |c| %>
|
100
|
+
# <% c.input(classes: "custom-class") %>
|
101
|
+
# <% end %>
|
102
|
+
#
|
103
|
+
# @example With custom classes for the results
|
104
|
+
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input--custom-results", list_id: "fruits-popup--custom-results")) do |c| %>
|
43
105
|
# <% c.results(classes: "custom-class") do %>
|
44
106
|
# <%= render(Primer::Beta::AutoComplete::Item.new(selected: true, value: "apple")) do |c| %>
|
45
107
|
# Apple
|
@@ -50,36 +112,36 @@ module Primer
|
|
50
112
|
# <% end %>
|
51
113
|
# <% end %>
|
52
114
|
#
|
53
|
-
# @example With Icon
|
54
|
-
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", list_id: "fruits-popup-4", input_id: "fruits-input-4", position: :relative)) do |c| %>
|
55
|
-
# <% c.icon(icon: :search) %>
|
56
|
-
# <% end %>
|
57
|
-
#
|
58
|
-
# @example With Icon and Non-Visible Label
|
59
|
-
# <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", list_id: "fruits-popup-5", input_id: "fruits-input-5", is_label_visible: false, position: :relative)) do |c| %>
|
60
|
-
# <% c.icon(icon: :search) %>
|
61
|
-
# <% end %>
|
62
115
|
# @param label_text [String] The label of the input.
|
63
116
|
# @param src [String] The route to query.
|
64
117
|
# @param input_id [String] Id of the input element.
|
118
|
+
# @param input_name [String] Optional name of the input element, defaults to `input_id` when not set.
|
65
119
|
# @param list_id [String] Id of the list element.
|
120
|
+
# @param with_icon [Boolean] Controls if a search icon is visible, defaults to `false`.
|
66
121
|
# @param is_label_visible [Boolean] Controls if the label is visible. If `false`, screen reader only text will be added.
|
122
|
+
# @param is_clearable [Boolean] Adds optional clear button.
|
123
|
+
# @param is_label_inline [Boolean] Controls if the label is inline. On smaller screens, label will always become stacked.
|
67
124
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
68
|
-
def initialize(label_text:, src:, list_id:, input_id:, is_label_visible: true, **system_arguments)
|
125
|
+
def initialize(label_text:, src:, list_id:, input_id:, input_name: nil, is_label_visible: true, is_label_inline: false, with_icon: false, is_clearable: false, **system_arguments)
|
69
126
|
@label_text = label_text
|
70
127
|
@list_id = list_id
|
71
128
|
@input_id = input_id
|
129
|
+
@input_name = input_name || input_id
|
72
130
|
@is_label_visible = is_label_visible
|
131
|
+
@with_icon = with_icon
|
132
|
+
@is_clearable = is_clearable
|
73
133
|
|
134
|
+
@label_classes = is_label_inline ? "autocomplete-label-inline" : "autocomplete-label-stacked"
|
74
135
|
@system_arguments = deny_tag_argument(**system_arguments)
|
75
136
|
@system_arguments[:tag] = "auto-complete"
|
76
137
|
@system_arguments[:src] = src
|
77
138
|
@system_arguments[:for] = list_id
|
78
139
|
end
|
79
140
|
|
80
|
-
# add `results` without needing to explicitly call them in the view
|
141
|
+
# add `input` and `results` without needing to explicitly call them in the view
|
81
142
|
def before_render
|
82
143
|
results(classes: "") unless results
|
144
|
+
input(classes: "") unless input
|
83
145
|
end
|
84
146
|
end
|
85
147
|
end
|