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.
@@ -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 -%>