primer_view_components 0.0.69 → 0.0.70

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,19 +1,18 @@
1
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {
2
- if (!privateMap.has(receiver)) {
3
- throw new TypeError("attempted to set private field on non-instance");
4
- }
5
- privateMap.set(receiver, value);
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, privateMap) {
9
- if (!privateMap.has(receiver)) {
10
- throw new TypeError("attempted to get private field on non-instance");
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 _abortController, _align, _side, _allowUpdatePosition;
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
- _abortController.set(this, void 0);
31
- _align.set(this, 'center');
32
- _side.set(this, 'outside-bottom');
33
- _allowUpdatePosition.set(this, false);
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: -6px;
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: 50%;
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
- right: auto
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: 6px
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, _allowUpdatePosition, true);
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, _abortController)) === null || _a === void 0 ? void 0 : _a.abort();
193
- __classPrivateFieldSet(this, _abortController, new AbortController());
194
- const { signal } = __classPrivateFieldGet(this, _abortController);
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, _abortController)) === null || _a === void 0 ? void 0 : _a.abort();
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..call(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, _align, 'center');
257
- __classPrivateFieldSet(this, _side, 'outside-top');
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, _align, 'start');
261
- __classPrivateFieldSet(this, _side, 'outside-top');
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, _align, 'center');
265
- __classPrivateFieldSet(this, _side, 'outside-right');
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, _align, 'start');
269
- __classPrivateFieldSet(this, _side, 'outside-bottom');
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, _align, 'center');
273
- __classPrivateFieldSet(this, _side, 'outside-bottom');
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, _align, 'end');
277
- __classPrivateFieldSet(this, _side, 'outside-bottom');
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, _align, 'center');
281
- __classPrivateFieldSet(this, _side, 'outside-left');
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, _align, 'end');
285
- __classPrivateFieldSet(this, _side, 'outside-top');
289
+ __classPrivateFieldSet(this, _TooltipElement_align, 'end', "f");
290
+ __classPrivateFieldSet(this, _TooltipElement_side, 'outside-top', "f");
286
291
  }
287
292
  }
288
293
  }
289
- // `getAnchoredPosition` may calibrate `anchoredSide` but does not recalibrate `align`.
290
- // Therefore, we need to determine which `align` is best based on the initial `getAnchoredPosition` calcluation.
291
- // Related: https://github.com/primer/behaviors/issues/63
292
- (anchorSide) {
293
- if (!this.control)
294
- return;
295
- const tooltipPosition = this.getBoundingClientRect();
296
- const targetPosition = this.control.getBoundingClientRect();
297
- const tooltipWidth = tooltipPosition.width;
298
- const tooltipCenter = tooltipPosition.left + tooltipWidth / 2;
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) {
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
- else if (tooltipPosition.right === targetPosition.right) {
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 (tooltipCenter < targetCenter) {
310
- if (tooltipPosition.left === 0)
311
- return 'start';
312
- return 'end';
357
+ else if (align === 'start') {
358
+ direction = 'ne';
313
359
  }
314
360
  else {
315
- if (tooltipPosition.right === 0)
316
- return 'end';
317
- return 'start';
361
+ direction = 'nw';
318
362
  }
319
363
  }
320
- () {
321
- if (!this.control)
322
- return;
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 (anchorSide === 'outside-right') {
349
- direction = 'e';
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
- if (align === 'center') {
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
- _abortController = new WeakMap(), _align = new WeakMap(), _side = new WeakMap(), _allowUpdatePosition = new WeakMap();
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: -6px;
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: 50%;
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
- right: auto
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: 6px
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
- <input id="<%= @input_id %>" name="<%= @input_id %>" type="text" class="form-control" autocomplete="off">
13
- <%= results %>
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
- # <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input-1", list_id: "fruits-popup-1", position: :relative)) %>
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 Non-Visible Label
39
- # <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input-2", list_id: "fruits-popup-2", is_label_visible: false, position: :relative)) %>
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 Custom Classes for the Results
42
- # <%= render(Primer::Beta::AutoComplete.new(label_text: "Fruits", src: "/auto_complete", input_id: "fruits-input-3", list_id: "fruits-popup-3", position: :relative)) do |c| %>
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
@@ -1,3 +1,4 @@
1
1
  <%= render Primer::BaseButton.new(**@system_arguments) do -%>
2
2
  <%= leading_visual %><%= trimmed_content %><%= trailing_visual %><%= primer_octicon("triangle-down", ml: 2, mr: -1) if @dropdown %>
3
+ <%= tooltip %>
3
4
  <% end -%>