primer_view_components 0.19.0 → 0.20.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/app/assets/javascripts/app/components/primer/alpha/action_list.d.ts +16 -0
- data/app/assets/javascripts/app/components/primer/beta/nav_list.d.ts +3 -0
- data/app/assets/javascripts/app/components/primer/primer.d.ts +1 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/assets/styles/primer_view_components.css +1 -1
- data/app/assets/styles/primer_view_components.css.map +1 -1
- data/app/components/primer/alpha/action_list/item.rb +19 -6
- data/app/components/primer/alpha/action_list.css +1 -1
- data/app/components/primer/alpha/action_list.css.json +2 -0
- data/app/components/primer/alpha/action_list.css.map +1 -1
- data/app/components/primer/alpha/action_list.d.ts +16 -0
- data/app/components/primer/alpha/action_list.html.erb +19 -17
- data/app/components/primer/alpha/action_list.js +69 -0
- data/app/components/primer/alpha/action_list.pcss +8 -0
- data/app/components/primer/alpha/action_list.ts +58 -0
- data/app/components/primer/alpha/banner.css +1 -1
- data/app/components/primer/alpha/banner.css.map +1 -1
- data/app/components/primer/alpha/banner.pcss +4 -4
- data/app/components/primer/alpha/dialog.css +1 -1
- data/app/components/primer/alpha/dialog.css.json +2 -0
- data/app/components/primer/alpha/dialog.css.map +1 -1
- data/app/components/primer/alpha/dialog.pcss +9 -0
- data/app/components/primer/alpha/text_field.css +1 -1
- data/app/components/primer/alpha/text_field.css.json +2 -0
- data/app/components/primer/alpha/text_field.css.map +1 -1
- data/app/components/primer/alpha/text_field.pcss +10 -0
- data/app/components/primer/alpha/toggle_switch.rb +2 -2
- data/app/components/primer/beta/auto_complete/auto_complete.html.erb +9 -9
- data/app/components/primer/beta/breadcrumbs.css +1 -1
- data/app/components/primer/beta/breadcrumbs.css.map +1 -1
- data/app/components/primer/beta/breadcrumbs.pcss +3 -1
- data/app/components/primer/beta/button.css +1 -1
- data/app/components/primer/beta/button.css.json +1 -0
- data/app/components/primer/beta/button.css.map +1 -1
- data/app/components/primer/beta/button.pcss +4 -0
- data/app/components/primer/beta/flash.css +1 -1
- data/app/components/primer/beta/flash.css.map +1 -1
- data/app/components/primer/beta/flash.pcss +4 -4
- data/app/components/primer/beta/nav_list.d.ts +3 -0
- data/app/components/primer/beta/nav_list.html.erb +1 -1
- data/app/components/primer/beta/nav_list.js +25 -2
- data/app/components/primer/beta/nav_list.ts +18 -1
- data/app/components/primer/beta/nav_list_group_element.js +4 -1
- data/app/components/primer/beta/nav_list_group_element.ts +3 -0
- data/app/components/primer/primer.d.ts +1 -0
- data/app/components/primer/primer.js +1 -0
- data/app/components/primer/primer.ts +1 -0
- data/app/forms/auto_complete_form.rb +18 -0
- data/app/forms/select_form.rb +10 -0
- data/lib/primer/forms/auto_complete.html.erb +6 -0
- data/lib/primer/forms/auto_complete.rb +56 -0
- data/lib/primer/forms/builder.rb +19 -0
- data/lib/primer/forms/check_box_group.html.erb +4 -4
- data/lib/primer/forms/check_box_group.rb +0 -3
- data/lib/primer/forms/dsl/auto_complete_input.rb +33 -0
- data/lib/primer/forms/dsl/check_box_group_input.rb +8 -0
- data/lib/primer/forms/dsl/input.rb +8 -2
- data/lib/primer/forms/dsl/input_methods.rb +9 -0
- data/lib/primer/forms/dsl/radio_button_group_input.rb +8 -0
- data/lib/primer/forms/dsl/select_input.rb +5 -1
- data/lib/primer/forms/form_control.rb +1 -2
- data/lib/primer/forms/primer_text_field.js +2 -2
- data/lib/primer/forms/primer_text_field.ts +2 -2
- data/lib/primer/forms/radio_button_group.html.erb +4 -4
- data/lib/primer/forms/radio_button_group.rb +0 -3
- data/lib/primer/forms/select.html.erb +1 -0
- data/lib/primer/forms/select.rb +9 -5
- data/lib/primer/view_components/version.rb +2 -2
- data/previews/primer/alpha/action_list_preview.rb +42 -0
- data/previews/primer/alpha/select_preview.rb +12 -1
- data/previews/primer/alpha/text_area_preview.rb +7 -1
- data/previews/primer/alpha/text_field_preview.rb +7 -1
- data/previews/primer/beta/nav_list_preview.rb +43 -0
- data/previews/primer/forms_preview/auto_complete_form.html.erb +3 -0
- data/previews/primer/forms_preview/select_form.html.erb +1 -1
- data/previews/primer/forms_preview.rb +2 -0
- data/static/arguments.json +7 -7
- data/static/constants.json +15 -0
- data/static/info_arch.json +150 -7
- data/static/previews.json +143 -0
- metadata +11 -2
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["flash.pcss"],"names":[],"mappings":"AAGA,oBASE
|
1
|
+
{"version":3,"sources":["flash.pcss"],"names":[],"mappings":"AAGA,oBASE,uEAA6C,CAC7C,sEAA6C,CAL7C,gDAAyC,CAFzC,kBAAmB,CACnB,sDAAqC,CAIrC,oDAA6B,CAN7B,qFAAyE,CADzE,iBAmBF,CARE,6BACE,kDAA4B,CAC5B,uCACF,CAEA,iCACE,eACF,CAIF,gBACE,8CACF,CAGA,gCASE,uBAAgB,CAAhB,eAAgB,CAFhB,eAAgB,CAChB,QAAS,CAJT,cAAe,CAHf,WAAY,CACZ,6CAAyC,CACzC,iBAmBF,CAXE,sCACE,UACF,CAEA,uCACE,UACF,CAEA,yCACE,cACF,CAIF,mCAIE,2BAA4B,CAH5B,WAAY,CAEZ,4CAAsC,CADtC,eAgBF,CAZE,gDAEE,gDAA2B,CAD3B,4CAEF,CAEA,+CACE,uBAKF,CAHE,wDACE,aACF,CAMJ,yBAEE,6EAAgD,CAChD,4EAAgD,CAFhD,oDAOF,CAHE,kCACE,wDACF,CAGF,0BAEE,uEAA6C,CAC7C,sEAA6C,CAF7C,oDAOF,CAHE,mCACE,kDACF,CAGF,4BAEE,yEAA8C,CAC9C,wEAA8C,CAF9C,oDAOF,CAHE,qCACE,oDACF,CAKF,yBAGE,eAAgB,CADhB,wDAAuC,CADvC,8DAGF,CAGA,cAOE,aAAc,CACd,eAAgB,CAFhB,cAAe,CADf,YAAa,CAJb,cAAe,CACf,KAAM,CAEN,UAAW,CADX,UAMF,CAGA,0BAEE,mEACF,CAGA,SAIE,6EAAgD,CADhD,gDAA6C,CAD7C,kBAAoB,CADpB,YAIF","file":"flash.css","sourcesContent":["/* flash */\n\n/* Default flash */\n.flash:not(.Banner) {\n position: relative;\n padding: var(--base-size-20) var(--control-medium-paddingInline-spacious);\n border-style: solid;\n border-width: var(--borderWidth-thin);\n border-radius: var(--borderRadius-medium);\n\n /* Default color */\n color: var(--fgColor-default);\n background-color: var(--bgColor-accent-muted);\n border-color: var(--borderColor-accent-muted);\n\n & .octicon {\n color: var(--fgColor-accent);\n margin-right: var(--base-size-12);\n }\n\n & p:last-child {\n margin-bottom: 0;\n }\n}\n\n/* Contain the flash messages */\n.flash-messages {\n margin-bottom: var(--stack-gap-spacious);\n}\n\n/* Close button */\n.flash-close:not(.Banner-close) {\n float: right;\n margin-top: calc(var(--base-size-4) * -1);\n text-align: center;\n cursor: pointer;\n\n /* Undo `<button>` styles */\n background: none;\n border: 0;\n appearance: none;\n\n &:hover {\n opacity: 0.7;\n }\n\n &:active {\n opacity: 0.5;\n }\n\n & .octicon {\n margin-right: 0;\n }\n}\n\n/* Action button */\n.flash-action:not(.Banner-actions) {\n float: right;\n margin-top: -3px;\n margin-left: var(--stack-gap-spacious);\n background-clip: padding-box;\n\n &.btn .octicon {\n margin-right: var(--control-small-gap);\n color: var(--fgColor-muted);\n }\n\n &.btn-primary {\n background-clip: border-box;\n\n & .octicon {\n color: inherit;\n }\n }\n}\n\n/* Color variations */\n\n.flash-warn:not(.Banner) {\n color: var(--fgColor-default);\n background-color: var(--bgColor-attention-muted);\n border-color: var(--borderColor-attention-muted);\n\n & .octicon {\n color: var(--fgColor-attention);\n }\n}\n\n.flash-error:not(.Banner) {\n color: var(--fgColor-default);\n background-color: var(--bgColor-danger-muted);\n border-color: var(--borderColor-danger-muted);\n\n & .octicon {\n color: var(--fgColor-danger);\n }\n}\n\n.flash-success:not(.Banner) {\n color: var(--fgColor-default);\n background-color: var(--bgColor-success-muted);\n border-color: var(--borderColor-success-muted);\n\n & .octicon {\n color: var(--fgColor-success);\n }\n}\n\n/* Layout variations */\n\n.flash-full:not(.Banner) {\n margin-top: calc(var(--borderWidth-thin) * -1);\n border-width: var(--borderWidth-thin) 0;\n border-radius: 0;\n}\n\n/* A banner rendered at the top of the page. */\n.flash-banner {\n position: fixed;\n top: 0;\n z-index: 90;\n width: 100%;\n border-top: 0;\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n/* Makes sure the background is opaque to cover any content underneath */\n.flash-full,\n.flash-banner {\n background-color: var(--bgColor-default);\n}\n\n/* FIXME deprecate this */\n.warning {\n padding: 0.5em;\n margin-bottom: 0.8em;\n font-weight: var(--base-text-weight-semibold);\n background-color: var(--bgColor-attention-muted);\n}\n"]}
|
@@ -10,7 +10,7 @@
|
|
10
10
|
|
11
11
|
/* Default color */
|
12
12
|
color: var(--fgColor-default);
|
13
|
-
background-
|
13
|
+
background-color: var(--bgColor-accent-muted);
|
14
14
|
border-color: var(--borderColor-accent-muted);
|
15
15
|
|
16
16
|
& .octicon {
|
@@ -78,7 +78,7 @@
|
|
78
78
|
|
79
79
|
.flash-warn:not(.Banner) {
|
80
80
|
color: var(--fgColor-default);
|
81
|
-
background-
|
81
|
+
background-color: var(--bgColor-attention-muted);
|
82
82
|
border-color: var(--borderColor-attention-muted);
|
83
83
|
|
84
84
|
& .octicon {
|
@@ -88,7 +88,7 @@
|
|
88
88
|
|
89
89
|
.flash-error:not(.Banner) {
|
90
90
|
color: var(--fgColor-default);
|
91
|
-
background-
|
91
|
+
background-color: var(--bgColor-danger-muted);
|
92
92
|
border-color: var(--borderColor-danger-muted);
|
93
93
|
|
94
94
|
& .octicon {
|
@@ -98,7 +98,7 @@
|
|
98
98
|
|
99
99
|
.flash-success:not(.Banner) {
|
100
100
|
color: var(--fgColor-default);
|
101
|
-
background-
|
101
|
+
background-color: var(--bgColor-success-muted);
|
102
102
|
border-color: var(--borderColor-success-muted);
|
103
103
|
|
104
104
|
& .octicon {
|
@@ -1,6 +1,9 @@
|
|
1
1
|
export declare class NavListElement extends HTMLElement {
|
2
2
|
#private;
|
3
3
|
items: HTMLElement[];
|
4
|
+
topLevelList: HTMLElement;
|
5
|
+
connectedCallback(): void;
|
6
|
+
disconnectedCallback(): void;
|
4
7
|
selectItemById(itemId: string | null): boolean;
|
5
8
|
selectItemByHref(href: string | null): boolean;
|
6
9
|
selectItemByCurrentLocation(): boolean;
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<%= render(Primer::BaseComponent.new(tag: :nav, **@system_arguments)) do %>
|
2
2
|
<%= heading %>
|
3
3
|
<nav-list>
|
4
|
-
<%= render(Primer::ConditionalWrapper.new(condition: render_outer_list?, tag: :ul, classes: "ActionListWrap")) do %>
|
4
|
+
<%= render(Primer::ConditionalWrapper.new(condition: render_outer_list?, tag: :ul, classes: "ActionListWrap", data: { target: "nav-list.topLevelList" })) do %>
|
5
5
|
<% items.each_with_index do |item, index| %>
|
6
6
|
<% if index > 0 && render_divider_between?(item, items[index - 1]) %>
|
7
7
|
<%= render(Primer::Alpha::ActionList::Divider.new) %>
|
@@ -4,18 +4,37 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
6
6
|
};
|
7
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
8
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
10
|
+
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");
|
11
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
12
|
+
};
|
7
13
|
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
8
14
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
9
15
|
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
16
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
17
|
};
|
12
|
-
var _NavListElement_instances, _NavListElement_findSelectedNavItemById, _NavListElement_findSelectedNavItemByHref, _NavListElement_findSelectedNavItemByCurrentLocation, _NavListElement_select, _NavListElement_deselect, _NavListElement_findParentMenu;
|
18
|
+
var _NavListElement_instances, _NavListElement_truncationObserver, _NavListElement_findSelectedNavItemById, _NavListElement_findSelectedNavItemByHref, _NavListElement_findSelectedNavItemByCurrentLocation, _NavListElement_select, _NavListElement_deselect, _NavListElement_findParentMenu;
|
13
19
|
/* eslint-disable custom-elements/expose-class-on-global */
|
14
|
-
import { controller, targets } from '@github/catalyst';
|
20
|
+
import { controller, target, targets } from '@github/catalyst';
|
21
|
+
import { ActionListTruncationObserver } from '../alpha/action_list';
|
15
22
|
let NavListElement = class NavListElement extends HTMLElement {
|
16
23
|
constructor() {
|
17
24
|
super(...arguments);
|
18
25
|
_NavListElement_instances.add(this);
|
26
|
+
_NavListElement_truncationObserver.set(this, void 0);
|
27
|
+
}
|
28
|
+
connectedCallback() {
|
29
|
+
// groups are wrapped in <action-list>, which handles resizing on its own
|
30
|
+
if (this.topLevelList) {
|
31
|
+
__classPrivateFieldSet(this, _NavListElement_truncationObserver, new ActionListTruncationObserver(this.topLevelList), "f");
|
32
|
+
}
|
33
|
+
}
|
34
|
+
disconnectedCallback() {
|
35
|
+
if (this.topLevelList) {
|
36
|
+
__classPrivateFieldGet(this, _NavListElement_truncationObserver, "f").unobserve(this.topLevelList);
|
37
|
+
}
|
19
38
|
}
|
20
39
|
selectItemById(itemId) {
|
21
40
|
if (!itemId)
|
@@ -100,6 +119,7 @@ let NavListElement = class NavListElement extends HTMLElement {
|
|
100
119
|
e.stopPropagation();
|
101
120
|
}
|
102
121
|
};
|
122
|
+
_NavListElement_truncationObserver = new WeakMap();
|
103
123
|
_NavListElement_instances = new WeakSet();
|
104
124
|
_NavListElement_findSelectedNavItemById = function _NavListElement_findSelectedNavItemById(itemId) {
|
105
125
|
// First we compare the selected link to data-item-id for each nav item
|
@@ -164,6 +184,9 @@ _NavListElement_findParentMenu = function _NavListElement_findParentMenu(navItem
|
|
164
184
|
__decorate([
|
165
185
|
targets
|
166
186
|
], NavListElement.prototype, "items", void 0);
|
187
|
+
__decorate([
|
188
|
+
target
|
189
|
+
], NavListElement.prototype, "topLevelList", void 0);
|
167
190
|
NavListElement = __decorate([
|
168
191
|
controller
|
169
192
|
], NavListElement);
|
@@ -1,9 +1,26 @@
|
|
1
1
|
/* eslint-disable custom-elements/expose-class-on-global */
|
2
|
-
import {controller, targets} from '@github/catalyst'
|
2
|
+
import {controller, target, targets} from '@github/catalyst'
|
3
|
+
import {ActionListTruncationObserver} from '../alpha/action_list'
|
3
4
|
|
4
5
|
@controller
|
5
6
|
export class NavListElement extends HTMLElement {
|
6
7
|
@targets items: HTMLElement[]
|
8
|
+
@target topLevelList: HTMLElement
|
9
|
+
|
10
|
+
#truncationObserver: ActionListTruncationObserver
|
11
|
+
|
12
|
+
connectedCallback() {
|
13
|
+
// groups are wrapped in <action-list>, which handles resizing on its own
|
14
|
+
if (this.topLevelList) {
|
15
|
+
this.#truncationObserver = new ActionListTruncationObserver(this.topLevelList)
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
disconnectedCallback() {
|
20
|
+
if (this.topLevelList) {
|
21
|
+
this.#truncationObserver.unobserve(this.topLevelList)
|
22
|
+
}
|
23
|
+
}
|
7
24
|
|
8
25
|
selectItemById(itemId: string | null): boolean {
|
9
26
|
if (!itemId) return false
|
@@ -9,12 +9,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
9
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
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
11
|
};
|
12
|
-
var _NavListGroupElement_instances, _NavListGroupElement_parseHTML;
|
12
|
+
var _NavListGroupElement_instances, _NavListGroupElement_parseHTML, _NavListGroupElement_truncateObserver;
|
13
13
|
import { controller, target, targets } from '@github/catalyst';
|
14
|
+
import { ActionListTruncationObserver } from '../alpha/action_list';
|
14
15
|
let NavListGroupElement = class NavListGroupElement extends HTMLElement {
|
15
16
|
constructor() {
|
16
17
|
super(...arguments);
|
17
18
|
_NavListGroupElement_instances.add(this);
|
19
|
+
_NavListGroupElement_truncateObserver.set(this, new ActionListTruncationObserver(this));
|
18
20
|
}
|
19
21
|
connectedCallback() {
|
20
22
|
this.setShowMoreItemState();
|
@@ -87,6 +89,7 @@ let NavListGroupElement = class NavListGroupElement extends HTMLElement {
|
|
87
89
|
}
|
88
90
|
}
|
89
91
|
};
|
92
|
+
_NavListGroupElement_truncateObserver = new WeakMap();
|
90
93
|
_NavListGroupElement_instances = new WeakSet();
|
91
94
|
_NavListGroupElement_parseHTML = function _NavListGroupElement_parseHTML(document, html) {
|
92
95
|
const template = document.createElement('template');
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import {controller, target, targets} from '@github/catalyst'
|
2
|
+
import {ActionListTruncationObserver} from '../alpha/action_list'
|
2
3
|
|
3
4
|
@controller
|
4
5
|
export class NavListGroupElement extends HTMLElement {
|
@@ -86,6 +87,8 @@ export class NavListGroupElement extends HTMLElement {
|
|
86
87
|
template.innerHTML = html
|
87
88
|
return document.importNode(template.content, true)
|
88
89
|
}
|
90
|
+
|
91
|
+
#truncateObserver = new ActionListTruncationObserver(this)
|
89
92
|
}
|
90
93
|
|
91
94
|
declare global {
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../previews/primer/url_helpers"
|
4
|
+
|
5
|
+
# :nodoc:
|
6
|
+
class AutoCompleteForm < ApplicationForm
|
7
|
+
form do |auto_complete_form|
|
8
|
+
auto_complete_form.auto_complete(
|
9
|
+
name: :fruit,
|
10
|
+
label: "Fruit",
|
11
|
+
caption: "Please enter your favorite fruit",
|
12
|
+
src: Primer::UrlHelpers.autocomplete_index_path,
|
13
|
+
validation_message: "Something went wrong"
|
14
|
+
)
|
15
|
+
|
16
|
+
auto_complete_form.submit(label: "Submit", name: :submit)
|
17
|
+
end
|
18
|
+
end
|
data/app/forms/select_form.rb
CHANGED
@@ -8,5 +8,15 @@ class SelectForm < ApplicationForm
|
|
8
8
|
city_list.option(label: "Bellevue", value: "bellevue")
|
9
9
|
city_list.option(label: "Seattle", value: "seattle")
|
10
10
|
end
|
11
|
+
|
12
|
+
select_form.select_list(name: "been", label: "Places you've been", caption: "Select all that apply", multiple: true, include_hidden: false) do |been_list|
|
13
|
+
been_list.option(label: "Lima, Peru", value: "lima")
|
14
|
+
been_list.option(label: "Tokyo, Japan", value: "tokyo")
|
15
|
+
been_list.option(label: "Reykjavík, Iceland", value: "reykjavik")
|
16
|
+
been_list.option(label: "Chiang Mai, Thailand", value: "chiang_mai")
|
17
|
+
been_list.option(label: "Queenstown, New Zealand", value: "queenstown")
|
18
|
+
end
|
19
|
+
|
20
|
+
select_form.submit(name: :submit, label: "Submit")
|
11
21
|
end
|
12
22
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<%= render(Primer::Beta::AutoComplete.new(**auto_complete_arguments)) do |autocomplete| %>
|
2
|
+
<% @input.block.call(autocomplete) if @input.block %>
|
3
|
+
<% autocomplete.with_input(**input_arguments) %>
|
4
|
+
<% end %>
|
5
|
+
<%= render(ValidationMessage.new(input: @input)) %>
|
6
|
+
<%= render(Caption.new(input: @input)) %>
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Forms
|
5
|
+
# :nodoc:
|
6
|
+
class AutoComplete < BaseComponent
|
7
|
+
ARGUMENT_TYPES = %i(keyreq key).freeze
|
8
|
+
|
9
|
+
delegate :builder, :form, to: :@input
|
10
|
+
|
11
|
+
def initialize(input:)
|
12
|
+
@input = input
|
13
|
+
@input.merge_input_arguments!(text_field_attributes.deep_symbolize_keys)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.auto_complete_argument_names
|
17
|
+
@auto_complete_argument_names ||=
|
18
|
+
Primer::Beta::AutoComplete.instance_method(:initialize)
|
19
|
+
.parameters
|
20
|
+
.filter_map { |(type, param_name)| next param_name if ARGUMENT_TYPES.include?(type) }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def all_input_arguments
|
26
|
+
@all_input_arguments ||= @input.input_arguments.deep_dup.tap do |args|
|
27
|
+
# rails uses :class but PVC wants :classes
|
28
|
+
args[:classes] = class_names(
|
29
|
+
args[:classes],
|
30
|
+
args.delete(:class)
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def auto_complete_arguments
|
36
|
+
all_args = all_input_arguments
|
37
|
+
all_args
|
38
|
+
.slice(*self.class.auto_complete_argument_names)
|
39
|
+
.merge(
|
40
|
+
input_name: all_args[:name],
|
41
|
+
input_id: all_args[:id],
|
42
|
+
label_text: @input.label,
|
43
|
+
list_id: "#{all_args[:id]}-list"
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def input_arguments
|
48
|
+
all_input_arguments.except(*self.class.auto_complete_argument_names, :id, :name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def text_field_attributes
|
52
|
+
builder.text_field_attributes(@input.name).except("size", "value")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/primer/forms/builder.rb
CHANGED
@@ -27,6 +27,21 @@ ActionView::Helpers::Tags::Base.prepend(
|
|
27
27
|
|
28
28
|
module Primer
|
29
29
|
module Forms
|
30
|
+
module Tags
|
31
|
+
# :nodoc:
|
32
|
+
class TextField < ::ActionView::Helpers::Tags::TextField
|
33
|
+
def attributes
|
34
|
+
render
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def tag(_name, options)
|
40
|
+
options
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
30
45
|
# :nodoc:
|
31
46
|
class Builder < ActionView::Helpers::FormBuilder
|
32
47
|
alias primer_fields_for fields_for
|
@@ -57,6 +72,10 @@ module Primer
|
|
57
72
|
super(*args, classify(options).merge(generate_error_markup: false), &block)
|
58
73
|
end
|
59
74
|
|
75
|
+
def text_field_attributes(method, options = {})
|
76
|
+
Tags::TextField.new(@object_name, method, @template, options).attributes
|
77
|
+
end
|
78
|
+
|
60
79
|
def text_area(*args, **options, &block)
|
61
80
|
super(*args, classify(options).merge(generate_error_markup: false), &block)
|
62
81
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
<div class="FormControl-check-group-wrap">
|
2
|
+
<%= content_tag(:fieldset, **@input.input_arguments) do %>
|
3
3
|
<% if @input.label %>
|
4
4
|
<%= content_tag(:legend, **@input.label_arguments) do %>
|
5
5
|
<%= @input.label %>
|
@@ -10,11 +10,11 @@
|
|
10
10
|
<%= render(check_box.to_component) %>
|
11
11
|
<% end %>
|
12
12
|
<% end %>
|
13
|
-
|
13
|
+
<% end %>
|
14
14
|
<div class="mt-2">
|
15
15
|
<%= render(ValidationMessage.new(input: @input)) %>
|
16
16
|
</div>
|
17
17
|
<div class="mt-2">
|
18
18
|
<%= render(Caption.new(input: @input)) %>
|
19
19
|
</div>
|
20
|
-
|
20
|
+
</div>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Forms
|
5
|
+
module Dsl
|
6
|
+
# :nodoc:
|
7
|
+
class AutoCompleteInput < Input
|
8
|
+
attr_reader :name, :label, :block
|
9
|
+
|
10
|
+
def initialize(name:, label:, **system_arguments, &block)
|
11
|
+
@name = name
|
12
|
+
@label = label
|
13
|
+
@block = block
|
14
|
+
|
15
|
+
super(**system_arguments)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_component
|
19
|
+
AutoComplete.new(input: self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def type
|
23
|
+
:autocomplete
|
24
|
+
end
|
25
|
+
|
26
|
+
# The AutoComplete Primer component does not allow auto-focusing
|
27
|
+
def focusable?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -31,7 +31,7 @@ module Primer
|
|
31
31
|
# @param size [Symbol] The size of the field. <%= one_of(Primer::Forms::Dsl::Input::SIZE_OPTIONS) %>
|
32
32
|
|
33
33
|
# @!macro [new] form_full_width_arguments
|
34
|
-
# @param full_width [Boolean] When set to `true`, the field will take up all the horizontal space allowed by its container.
|
34
|
+
# @param full_width [Boolean] When set to `true`, the field will take up all the horizontal space allowed by its container. Defaults to `true`.
|
35
35
|
|
36
36
|
# @!macro [new] form_system_arguments
|
37
37
|
# @param system_arguments [Hash] A hash of attributes passed to the underlying Rails builder methods. These options may mean something special depending on the type of input, otherwise they are emitted as HTML attributes. See the [Rails documentation](https://guides.rubyonrails.org/form_helpers.html) for more information. In addition, the usual Primer utility arguments are accepted in system arguments. For example, passing `mt: 2` will add the `mt-2` class to the input. See the Primer system arguments docs for details.
|
@@ -56,7 +56,9 @@ module Primer
|
|
56
56
|
@form = form
|
57
57
|
|
58
58
|
@input_arguments = system_arguments
|
59
|
+
@input_arguments.delete(:id) unless @input_arguments[:id].present?
|
59
60
|
@label_arguments = @input_arguments.delete(:label_arguments) || {}
|
61
|
+
@label_arguments[:for] = id if id.present?
|
60
62
|
|
61
63
|
@label_arguments[:class] = class_names(
|
62
64
|
@label_arguments[:class],
|
@@ -71,7 +73,7 @@ module Primer
|
|
71
73
|
@caption = @input_arguments.delete(:caption)
|
72
74
|
@validation_message = @input_arguments.delete(:validation_message)
|
73
75
|
@invalid = @input_arguments.delete(:invalid)
|
74
|
-
@full_width = @input_arguments.delete(:full_width)
|
76
|
+
@full_width = @input_arguments.delete(:full_width) { true }
|
75
77
|
@size = @input_arguments.delete(:size)
|
76
78
|
|
77
79
|
# If scope_name_to_model is false, the name of the input for eg. `my_field`
|
@@ -240,6 +242,10 @@ module Primer
|
|
240
242
|
input_arguments[:autofocus] = true
|
241
243
|
end
|
242
244
|
|
245
|
+
def id
|
246
|
+
@input_arguments[:id]
|
247
|
+
end
|
248
|
+
|
243
249
|
# :nocov:
|
244
250
|
def name
|
245
251
|
raise_for_abstract_method!(__method__)
|
@@ -68,6 +68,15 @@ module Primer
|
|
68
68
|
add_input TextFieldInput.new(builder: builder, form: form, **options, &block)
|
69
69
|
end
|
70
70
|
|
71
|
+
# Adds an autocomplete text field to this form.
|
72
|
+
#
|
73
|
+
# @param options [Hash] The options accepted by the autocomplete input (see forms docs).
|
74
|
+
# @param block [Proc] A block that will be yielded a reference to the input object so it can be customized.
|
75
|
+
def auto_complete(**options, &block)
|
76
|
+
options = decorate_options(**options)
|
77
|
+
add_input AutoCompleteInput.new(builder: builder, form: form, **options, &block)
|
78
|
+
end
|
79
|
+
|
71
80
|
# Adds a text area to this form.
|
72
81
|
#
|
73
82
|
# @param options [Hash] The options accepted by the text area input (see forms docs).
|
@@ -25,6 +25,14 @@ module Primer
|
|
25
25
|
:radio_button_group
|
26
26
|
end
|
27
27
|
|
28
|
+
def autofocus!
|
29
|
+
@radio_buttons.first&.autofocus!
|
30
|
+
end
|
31
|
+
|
32
|
+
def focusable?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
28
36
|
def radio_button(**system_arguments, &block)
|
29
37
|
@radio_buttons << RadioButtonInput.new(
|
30
38
|
builder: @builder, form: @form, name: @name, disabled: disabled?,
|
@@ -5,7 +5,7 @@ module Primer
|
|
5
5
|
module Dsl
|
6
6
|
# :nodoc:
|
7
7
|
class SelectInput < Input
|
8
|
-
SELECT_ARGUMENTS = %i[multiple include_blank prompt].freeze
|
8
|
+
SELECT_ARGUMENTS = %i[multiple include_blank include_hidden prompt].freeze
|
9
9
|
|
10
10
|
# :nodoc:
|
11
11
|
class Option
|
@@ -38,6 +38,10 @@ module Primer
|
|
38
38
|
yield(self) if block_given?
|
39
39
|
end
|
40
40
|
|
41
|
+
def multiple?
|
42
|
+
@select_arguments.fetch(:multiple, false)
|
43
|
+
end
|
44
|
+
|
41
45
|
def option(**system_arguments)
|
42
46
|
@options << Option.new(**system_arguments)
|
43
47
|
end
|
@@ -29,7 +29,7 @@ class PrimerTextFieldElement extends HTMLElement {
|
|
29
29
|
connectedCallback() {
|
30
30
|
__classPrivateFieldGet(this, _PrimerTextFieldElement_abortController, "f")?.abort();
|
31
31
|
const { signal } = (__classPrivateFieldSet(this, _PrimerTextFieldElement_abortController, new AbortController(), "f"));
|
32
|
-
this.
|
32
|
+
this.addEventListener('auto-check-success', async (event) => {
|
33
33
|
const message = await event.detail.response.text();
|
34
34
|
if (message && message.length > 0) {
|
35
35
|
this.setSuccess(message);
|
@@ -38,7 +38,7 @@ class PrimerTextFieldElement extends HTMLElement {
|
|
38
38
|
this.clearError();
|
39
39
|
}
|
40
40
|
}, { signal });
|
41
|
-
this.
|
41
|
+
this.addEventListener('auto-check-error', async (event) => {
|
42
42
|
const errorMessage = await event.detail.response.text();
|
43
43
|
this.setError(errorMessage);
|
44
44
|
}, { signal });
|
@@ -17,7 +17,7 @@ class PrimerTextFieldElement extends HTMLElement {
|
|
17
17
|
this.#abortController?.abort()
|
18
18
|
const {signal} = (this.#abortController = new AbortController())
|
19
19
|
|
20
|
-
this.
|
20
|
+
this.addEventListener(
|
21
21
|
'auto-check-success',
|
22
22
|
async (event: any) => {
|
23
23
|
const message = await event.detail.response.text()
|
@@ -30,7 +30,7 @@ class PrimerTextFieldElement extends HTMLElement {
|
|
30
30
|
{signal},
|
31
31
|
)
|
32
32
|
|
33
|
-
this.
|
33
|
+
this.addEventListener(
|
34
34
|
'auto-check-error',
|
35
35
|
async (event: any) => {
|
36
36
|
const errorMessage = await event.detail.response.text()
|
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
<div class="FormControl-radio-group-wrap">
|
2
|
+
<%= content_tag(:fieldset, **@input.input_arguments) do %>
|
3
3
|
<% if @input.label %>
|
4
4
|
<%= content_tag(:legend, **@input.label_arguments) do %>
|
5
5
|
<%= @input.label %>
|
@@ -10,11 +10,11 @@
|
|
10
10
|
<%= render(radio_button.to_component) %>
|
11
11
|
<% end %>
|
12
12
|
<% end %>
|
13
|
-
|
13
|
+
<% end %>
|
14
14
|
<div class="mt-2">
|
15
15
|
<%= render(ValidationMessage.new(input: @input)) %>
|
16
16
|
</div>
|
17
17
|
<div class="mt-2">
|
18
18
|
<%= render(Caption.new(input: @input)) %>
|
19
19
|
</div>
|
20
|
-
|
20
|
+
</div>
|
data/lib/primer/forms/select.rb
CHANGED
@@ -8,14 +8,18 @@ module Primer
|
|
8
8
|
|
9
9
|
def initialize(input:)
|
10
10
|
@input = input
|
11
|
-
@input.add_input_classes(
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
@input.add_input_classes("FormControl-select")
|
12
|
+
|
13
|
+
if !input.multiple?
|
14
|
+
@input.add_input_classes(
|
15
|
+
Primer::Forms::Dsl::Input::SIZE_MAPPINGS[@input.size]
|
16
|
+
)
|
17
|
+
end
|
15
18
|
|
16
19
|
@field_wrap_arguments = {
|
17
20
|
class: "FormControl-select-wrap",
|
18
|
-
hidden: @input.hidden
|
21
|
+
hidden: @input.hidden?,
|
22
|
+
data: { multiple: input.multiple? }
|
19
23
|
}
|
20
24
|
end
|
21
25
|
|