diff --git a/app/javascript/application.js b/app/javascript/application.js index 7dee5edf9..f32bf9968 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -35,6 +35,8 @@ import PromptPassword from './elements/prompt_password' import EmailsTextarea from './elements/emails_textarea' import ToggleSubmit from './elements/toggle_submit' import ToggleOnSubmit from './elements/toggle_on_submit' +import ToggleOnChecked from './elements/toggle_on_checked' +import AppendTemplate from './elements/append_template' import CheckOnClick from './elements/check_on_click' import PasswordInput from './elements/password_input' import SearchInput from './elements/search_input' @@ -159,6 +161,8 @@ safeRegisterElement('emails-textarea', EmailsTextarea) safeRegisterElement('toggle-cookies', ToggleCookies) safeRegisterElement('toggle-submit', ToggleSubmit) safeRegisterElement('toggle-on-submit', ToggleOnSubmit) +safeRegisterElement('toggle-on-checked', ToggleOnChecked) +safeRegisterElement('append-template', AppendTemplate) safeRegisterElement('password-input', PasswordInput) safeRegisterElement('search-input', SearchInput) safeRegisterElement('toggle-attribute', ToggleAttribute) diff --git a/app/javascript/elements/append_template.js b/app/javascript/elements/append_template.js new file mode 100644 index 000000000..18012bcf4 --- /dev/null +++ b/app/javascript/elements/append_template.js @@ -0,0 +1,29 @@ +export default class extends HTMLElement { + connectedCallback () { + this.addEventListener('click', this.onClick) + } + + disconnectedCallback () { + this.removeEventListener('click', this.onClick) + } + + onClick = (e) => { + e.preventDefault() + + const target = document.getElementById(this.dataset.targetId) + const item = this.querySelector('template').content.firstElementChild.cloneNode(true) + + item.querySelector('a').addEventListener('click', (e) => { + e.preventDefault() + + item.remove() + + target.classList.toggle('hidden', !target.childElementCount) + }) + + target.append(item) + target.classList.remove('hidden') + + item.querySelector('input').focus() + } +} diff --git a/app/javascript/elements/dynamic_list.js b/app/javascript/elements/dynamic_list.js index 57c9ccf18..a0ad26c0b 100644 --- a/app/javascript/elements/dynamic_list.js +++ b/app/javascript/elements/dynamic_list.js @@ -2,7 +2,7 @@ import { actionable } from '@github/catalyst/lib/actionable' import { targets, targetable } from '@github/catalyst/lib/targetable' export default actionable(targetable(class extends HTMLElement { - static [targets.static] = ['items'] + static [targets.static] = ['items', 'groups'] addItem (e) { e.preventDefault() @@ -24,11 +24,28 @@ export default actionable(targetable(class extends HTMLElement { duplicateItem.querySelectorAll('a.hidden').forEach((button) => button.classList.toggle('hidden')) originalItem.parentNode.append(duplicateItem) + + this.toggleMultipleClasses() + + duplicateItem.querySelector("input:not([type='hidden'])")?.focus() } removeItem (e) { e.preventDefault() this.items.find((item) => item.contains(e.target))?.remove() + + this.toggleMultipleClasses() + } + + toggleMultipleClasses () { + const isMultiple = this.items.length > 1 + const elements = [...this.items, ...this.groups] + + elements.forEach((element) => { + const classes = element.dataset.multipleClasses || this.dataset.multipleClasses + + classes?.split(' ').forEach((cls) => element.classList.toggle(cls, isMultiple)) + }) } })) diff --git a/app/javascript/elements/toggle_on_checked.js b/app/javascript/elements/toggle_on_checked.js new file mode 100644 index 000000000..85ba9b0df --- /dev/null +++ b/app/javascript/elements/toggle_on_checked.js @@ -0,0 +1,23 @@ +export default class extends HTMLElement { + connectedCallback () { + this.form.addEventListener('change', this.toggle) + + this.toggle() + } + + disconnectedCallback () { + this.form.removeEventListener('change', this.toggle) + } + + toggle = () => { + const selector = this.dataset.names.split(' ').map((name) => `[name="${name}"]:checked:enabled`).join(', ') + const isChecked = !!this.form.querySelector(selector) + + this.firstElementChild.classList.toggle('hidden', !isChecked) + this.lastElementChild.classList.toggle('hidden', isChecked) + } + + get form () { + return this.closest('form') + } +} diff --git a/app/javascript/native.js b/app/javascript/native.js index 7de1f76a1..5ddd44580 100644 --- a/app/javascript/native.js +++ b/app/javascript/native.js @@ -24,6 +24,9 @@ const ICONS = { user: 'person', users: 'person.2', users_plus: 'person.badge.plus', + user_plus: 'person.badge.plus', + eye_plus: 'eye', + mail_plus: 'envelope', photo: 'photo', settings: 'gearshape', logout: 'rectangle.portrait.and.arrow.right', diff --git a/app/views/icons/_eye_plus.html.erb b/app/views/icons/_eye_plus.html.erb new file mode 100644 index 000000000..ad4a2aef7 --- /dev/null +++ b/app/views/icons/_eye_plus.html.erb @@ -0,0 +1,7 @@ + diff --git a/app/views/icons/_mail_plus.html.erb b/app/views/icons/_mail_plus.html.erb new file mode 100644 index 000000000..5b4ecab38 --- /dev/null +++ b/app/views/icons/_mail_plus.html.erb @@ -0,0 +1,7 @@ + diff --git a/app/views/icons/_user_plus.html.erb b/app/views/icons/_user_plus.html.erb index 935d26d05..0a83ce86e 100644 --- a/app/views/icons/_user_plus.html.erb +++ b/app/views/icons/_user_plus.html.erb @@ -1,8 +1,7 @@ diff --git a/app/views/submissions/_add_new.html.erb b/app/views/submissions/_add_new.html.erb new file mode 100644 index 000000000..3a283f7fb --- /dev/null +++ b/app/views/submissions/_add_new.html.erb @@ -0,0 +1,70 @@ +<% fields_id = "add_new_fields_#{SecureRandom.hex(3)}" %> +
+