diff --git a/resources/js/components/collapsible.js b/resources/js/components/collapsible.js index 544f91008..bb8ed477f 100644 --- a/resources/js/components/collapsible.js +++ b/resources/js/components/collapsible.js @@ -1,35 +1,37 @@ import {slideDown, slideUp} from "../services/animations"; +import {Component} from "./component"; /** * Collapsible * Provides some simple logic to allow collapsible sections. */ -class Collapsible { +export class Collapsible extends Component { - constructor(elem) { - this.elem = elem; - this.trigger = elem.querySelector('[collapsible-trigger]'); - this.content = elem.querySelector('[collapsible-content]'); + setup() { + this.container = this.$el; + this.trigger = this.$refs.trigger; + this.content = this.$refs.content; - if (!this.trigger) return; - this.trigger.addEventListener('click', this.toggle.bind(this)); - this.openIfContainsError(); + if (this.trigger) { + this.trigger.addEventListener('click', this.toggle.bind(this)); + this.openIfContainsError(); + } } open() { - this.elem.classList.add('open'); + this.container.classList.add('open'); this.trigger.setAttribute('aria-expanded', 'true'); slideDown(this.content, 300); } close() { - this.elem.classList.remove('open'); + this.container.classList.remove('open'); this.trigger.setAttribute('aria-expanded', 'false'); slideUp(this.content, 300); } toggle() { - if (this.elem.classList.contains('open')) { + if (this.container.classList.contains('open')) { this.close(); } else { this.open(); @@ -43,6 +45,4 @@ class Collapsible { } } -} - -export default Collapsible; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/dropdown-search.js b/resources/js/components/dropdown-search.js index 81fa940c2..30a2aadc1 100644 --- a/resources/js/components/dropdown-search.js +++ b/resources/js/components/dropdown-search.js @@ -1,7 +1,8 @@ import {debounce} from "../services/util"; import {transitionHeight} from "../services/animations"; +import {Component} from "./component"; -class DropdownSearch { +export class DropdownSearch extends Component { setup() { this.elem = this.$el; @@ -78,6 +79,4 @@ class DropdownSearch { this.loadingElem.style.display = show ? 'block' : 'none'; } -} - -export default DropdownSearch; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/expand-toggle.js b/resources/js/components/expand-toggle.js index cce1b215c..ab4d38ab1 100644 --- a/resources/js/components/expand-toggle.js +++ b/resources/js/components/expand-toggle.js @@ -1,17 +1,15 @@ import {slideUp, slideDown} from "../services/animations"; +import {Component} from "./component"; -class ExpandToggle { +export class ExpandToggle extends Component { - constructor(elem) { - this.elem = elem; - - // Component state - this.isOpen = elem.getAttribute('expand-toggle-is-open') === 'yes'; - this.updateEndpoint = elem.getAttribute('expand-toggle-update-endpoint'); - this.selector = elem.getAttribute('expand-toggle'); + setup(elem) { + this.targetSelector = this.$opts.targetSelector; + this.isOpen = this.$opts.isOpen === 'true'; + this.updateEndpoint = this.$opts.updateEndpoint; // Listener setup - elem.addEventListener('click', this.click.bind(this)); + this.$el.addEventListener('click', this.click.bind(this)); } open(elemToToggle) { @@ -25,7 +23,7 @@ class ExpandToggle { click(event) { event.preventDefault(); - const matchingElems = document.querySelectorAll(this.selector); + const matchingElems = document.querySelectorAll(this.targetSelector); for (let match of matchingElems) { this.isOpen ? this.close(match) : this.open(match); } @@ -40,6 +38,4 @@ class ExpandToggle { }); } -} - -export default ExpandToggle; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/header-mobile-toggle.js b/resources/js/components/header-mobile-toggle.js index 99737bfb8..11b23cca6 100644 --- a/resources/js/components/header-mobile-toggle.js +++ b/resources/js/components/header-mobile-toggle.js @@ -1,5 +1,6 @@ +import {Component} from "./component"; -class HeaderMobileToggle { +export class HeaderMobileToggle extends Component { setup() { this.elem = this.$el; @@ -36,6 +37,4 @@ class HeaderMobileToggle { this.onToggle(event); } -} - -export default HeaderMobileToggle; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/image-picker.js b/resources/js/components/image-picker.js index 7455fa622..03d9567d2 100644 --- a/resources/js/components/image-picker.js +++ b/resources/js/components/image-picker.js @@ -1,21 +1,25 @@ +import {Component} from "./component"; -class ImagePicker { +export class ImagePicker extends Component { - constructor(elem) { - this.elem = elem; - this.imageElem = elem.querySelector('img'); - this.imageInput = elem.querySelector('input[type=file]'); - this.resetInput = elem.querySelector('input[data-reset-input]'); - this.removeInput = elem.querySelector('input[data-remove-input]'); + setup() { + this.imageElem = this.$refs.image; + this.imageInput = this.$refs.imageInput; + this.resetInput = this.$refs.resetInput; + this.removeInput = this.$refs.removeInput; + this.resetButton = this.$refs.resetButton; + this.removeButton = this.$refs.removeButton || null; - this.defaultImage = elem.getAttribute('data-default-image'); + this.defaultImage = this.$opts.defaultImage; - const resetButton = elem.querySelector('button[data-action="reset-image"]'); - resetButton.addEventListener('click', this.reset.bind(this)); + this.setupListeners(); + } - const removeButton = elem.querySelector('button[data-action="remove-image"]'); - if (removeButton) { - removeButton.addEventListener('click', this.removeImage.bind(this)); + setupListeners() { + this.resetButton.addEventListener('click', this.reset.bind(this)); + + if (this.removeButton) { + this.removeButton.addEventListener('click', this.removeImage.bind(this)); } this.imageInput.addEventListener('change', this.fileInputChange.bind(this)); @@ -50,6 +54,4 @@ class ImagePicker { this.resetInput.setAttribute('disabled', 'disabled'); } -} - -export default ImagePicker; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/index.js b/resources/js/components/index.js index 6b5967031..0106e82d4 100644 --- a/resources/js/components/index.js +++ b/resources/js/components/index.js @@ -11,12 +11,12 @@ export {ChapterContents} from "./chapter-contents.js" // export {CodeEditor} from "./code-editor.js" export {CodeHighlighter} from "./code-highlighter.js" export {CodeTextarea} from "./code-textarea.js" -// export {Collapsible} from "./collapsible.js" +export {Collapsible} from "./collapsible.js" // export {ConfirmDialog} from "./confirm-dialog" export {CustomCheckbox} from "./custom-checkbox.js" export {DetailsHighlighter} from "./details-highlighter.js" export {Dropdown} from "./dropdown.js" -// export {DropdownSearch} from "./dropdown-search.js" +export {DropdownSearch} from "./dropdown-search.js" // export {Dropzone} from "./dropzone.js" // export {EditorToolbox} from "./editor-toolbox.js" export {EntityPermissions} from "./entity-permissions" @@ -24,17 +24,17 @@ export {EntityPermissions} from "./entity-permissions" export {EntitySelector} from "./entity-selector.js" export {EntitySelectorPopup} from "./entity-selector-popup.js" // export {EventEmitSelect} from "./event-emit-select.js" -// export {ExpandToggle} from "./expand-toggle.js" -// export {HeaderMobileToggle} from "./header-mobile-toggle.js" +export {ExpandToggle} from "./expand-toggle.js" +export {HeaderMobileToggle} from "./header-mobile-toggle.js" // export {ImageManager} from "./image-manager.js" -// export {ImagePicker} from "./image-picker.js" -// export {ListSortControl} from "./list-sort-control.js" +export {ImagePicker} from "./image-picker.js" +export {ListSortControl} from "./list-sort-control.js" // export {MarkdownEditor} from "./markdown-editor.js" -// export {NewUserPassword} from "./new-user-password.js" +export {NewUserPassword} from "./new-user-password.js" export {Notification} from "./notification.js" -// export {OptionalInput} from "./optional-input.js" +export {OptionalInput} from "./optional-input.js" export {PageComments} from "./page-comments.js" -// export {PageDisplay} from "./page-display.js" +export {PageDisplay} from "./page-display.js" // export {PageEditor} from "./page-editor.js" export {PagePicker} from "./page-picker.js" export {PermissionsTable} from "./permissions-table.js" @@ -43,17 +43,16 @@ export {Popup} from "./popup.js" export {SettingAppColorPicker} from "./setting-app-color-picker.js" export {SettingColorPicker} from "./setting-color-picker.js" export {SettingHomepageControl} from "./setting-homepage-control.js" -// export {ShelfSort} from "./shelf-sort.js" +export {ShelfSort} from "./shelf-sort.js" export {Shortcuts} from "./shortcuts" export {ShortcutInput} from "./shortcut-input" -// export {Sidebar} from "./sidebar.js" // export {SortableList} from "./sortable-list.js" -// export {SubmitOnChange} from "./submit-on-change.js" +export {SubmitOnChange} from "./submit-on-change.js" // export {Tabs} from "./tabs.js" // export {TagManager} from "./tag-manager.js" // export {TemplateManager} from "./template-manager.js" export {ToggleSwitch} from "./toggle-switch.js" -// export {TriLayout} from "./tri-layout.js" -// export {UserSelect} from "./user-select.js" -// export {WebhookEvents} from "./webhook-events"; +export {TriLayout} from "./tri-layout.js" +export {UserSelect} from "./user-select.js" +export {WebhookEvents} from "./webhook-events"; // export {WysiwygEditor} from "./wysiwyg-editor.js" \ No newline at end of file diff --git a/resources/js/components/list-sort-control.js b/resources/js/components/list-sort-control.js index 3b642dbde..b8d4de73a 100644 --- a/resources/js/components/list-sort-control.js +++ b/resources/js/components/list-sort-control.js @@ -1,9 +1,10 @@ /** * ListSortControl * Manages the logic for the control which provides list sorting options. - * @extends {Component} */ -class ListSortControl { +import {Component} from "./component"; + +export class ListSortControl extends Component { setup() { this.elem = this.$el; @@ -44,6 +45,4 @@ class ListSortControl { this.form.submit(); } -} - -export default ListSortControl; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/new-user-password.js b/resources/js/components/new-user-password.js index 9c4c21c14..a4ed4d15b 100644 --- a/resources/js/components/new-user-password.js +++ b/resources/js/components/new-user-password.js @@ -1,9 +1,11 @@ +import {Component} from "./component"; -class NewUserPassword { +export class NewUserPassword extends Component { - constructor(elem) { - this.elem = elem; - this.inviteOption = elem.querySelector('input[name=send_invite]'); + setup() { + this.container = this.$el; + this.inputContainer = this.$refs.inputContainer; + this.inviteOption = this.container.querySelector('input[name=send_invite]'); if (this.inviteOption) { this.inviteOption.addEventListener('change', this.inviteOptionChange.bind(this)); @@ -13,16 +15,12 @@ class NewUserPassword { inviteOptionChange() { const inviting = (this.inviteOption.value === 'true'); - const passwordBoxes = this.elem.querySelectorAll('input[type=password]'); + const passwordBoxes = this.container.querySelectorAll('input[type=password]'); for (const input of passwordBoxes) { input.disabled = inviting; } - const container = this.elem.querySelector('#password-input-container'); - if (container) { - container.style.display = inviting ? 'none' : 'block'; - } + + this.inputContainer.style.display = inviting ? 'none' : 'block'; } -} - -export default NewUserPassword; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/optional-input.js b/resources/js/components/optional-input.js index eab58e42a..cc429c991 100644 --- a/resources/js/components/optional-input.js +++ b/resources/js/components/optional-input.js @@ -1,6 +1,7 @@ import {onSelect} from "../services/dom"; +import {Component} from "./component"; -class OptionalInput { +export class OptionalInput extends Component { setup() { this.removeButton = this.$refs.remove; this.showButton = this.$refs.show; @@ -23,6 +24,4 @@ class OptionalInput { }); } -} - -export default OptionalInput; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/page-display.js b/resources/js/components/page-display.js index f8377130c..c06c3310d 100644 --- a/resources/js/components/page-display.js +++ b/resources/js/components/page-display.js @@ -1,11 +1,12 @@ import * as DOM from "../services/dom"; import {scrollAndHighlightElement} from "../services/util"; +import {Component} from "./component"; -class PageDisplay { +export class PageDisplay extends Component { - constructor(elem) { - this.elem = elem; - this.pageId = elem.getAttribute('page-display'); + setup() { + this.container = this.$el; + this.pageId = this.$opts.pageId; window.importVersioned('code').then(Code => Code.highlight()); this.setupNavHighlighting(); @@ -13,7 +14,7 @@ class PageDisplay { // Check the hash on load if (window.location.hash) { - let text = window.location.hash.replace(/\%20/g, ' ').substr(1); + const text = window.location.hash.replace(/%20/g, ' ').substring(1); this.goToText(text); } @@ -49,17 +50,10 @@ class PageDisplay { } setupNavHighlighting() { - // Check if support is present for IntersectionObserver - if (!('IntersectionObserver' in window) || - !('IntersectionObserverEntry' in window) || - !('intersectionRatio' in window.IntersectionObserverEntry.prototype)) { - return; - } - - let pageNav = document.querySelector('.sidebar-page-nav'); + const pageNav = document.querySelector('.sidebar-page-nav'); // fetch all the headings. - let headings = document.querySelector('.page-content').querySelectorAll('h1, h2, h3, h4, h5, h6'); + const headings = document.querySelector('.page-content').querySelectorAll('h1, h2, h3, h4, h5, h6'); // if headings are present, add observers. if (headings.length > 0 && pageNav !== null) { addNavObserver(headings); @@ -67,21 +61,21 @@ class PageDisplay { function addNavObserver(headings) { // Setup the intersection observer. - let intersectOpts = { + const intersectOpts = { rootMargin: '0px 0px 0px 0px', threshold: 1.0 }; - let pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts); + const pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts); // observe each heading - for (let heading of headings) { + for (const heading of headings) { pageNavObserver.observe(heading); } } function headingVisibilityChange(entries, observer) { - for (let entry of entries) { - let isVisible = (entry.intersectionRatio === 1); + for (const entry of entries) { + const isVisible = (entry.intersectionRatio === 1); toggleAnchorHighlighting(entry.target.id, isVisible); } } @@ -99,9 +93,7 @@ class PageDisplay { codeMirrors.forEach(cm => cm.CodeMirror && cm.CodeMirror.refresh()); }; - const details = [...this.elem.querySelectorAll('details')]; + const details = [...this.container.querySelectorAll('details')]; details.forEach(detail => detail.addEventListener('toggle', onToggle)); } -} - -export default PageDisplay; +} \ No newline at end of file diff --git a/resources/js/components/shelf-sort.js b/resources/js/components/shelf-sort.js index 30eda5a21..aa244086e 100644 --- a/resources/js/components/shelf-sort.js +++ b/resources/js/components/shelf-sort.js @@ -1,6 +1,7 @@ import Sortable from "sortablejs"; +import {Component} from "./component"; -class ShelfSort { +export class ShelfSort extends Component { setup() { this.elem = this.$el; @@ -15,7 +16,7 @@ class ShelfSort { initSortable() { const scrollBoxes = this.elem.querySelectorAll('.scroll-box'); - for (let scrollBox of scrollBoxes) { + for (const scrollBox of scrollBoxes) { new Sortable(scrollBox, { group: 'shelf-books', ghostClass: 'primary-background-light', diff --git a/resources/js/components/sidebar.js b/resources/js/components/sidebar.js deleted file mode 100644 index 0fecc5eae..000000000 --- a/resources/js/components/sidebar.js +++ /dev/null @@ -1,16 +0,0 @@ - -class Sidebar { - - constructor(elem) { - this.elem = elem; - this.toggleElem = elem.querySelector('.sidebar-toggle'); - this.toggleElem.addEventListener('click', this.toggle.bind(this)); - } - - toggle(show = true) { - this.elem.classList.toggle('open'); - } - -} - -export default Sidebar; \ No newline at end of file diff --git a/resources/js/components/submit-on-change.js b/resources/js/components/submit-on-change.js index aeacae232..da4ac6996 100644 --- a/resources/js/components/submit-on-change.js +++ b/resources/js/components/submit-on-change.js @@ -1,9 +1,10 @@ +import {Component} from "./component"; + /** * Submit on change * Simply submits a parent form when this input is changed. - * @extends {Component} */ -class SubmitOnChange { +export class SubmitOnChange extends Component { setup() { this.filter = this.$opts.filter; @@ -21,6 +22,4 @@ class SubmitOnChange { }); } -} - -export default SubmitOnChange; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/tri-layout.js b/resources/js/components/tri-layout.js index f801e52a1..ead2ac3d0 100644 --- a/resources/js/components/tri-layout.js +++ b/resources/js/components/tri-layout.js @@ -1,5 +1,6 @@ +import {Component} from "./component"; -class TriLayout { +export class TriLayout extends Component { setup() { this.container = this.$refs.container; @@ -108,6 +109,4 @@ class TriLayout { this.lastTabShown = tabName; } -} - -export default TriLayout; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/user-select.js b/resources/js/components/user-select.js index aba43e0a9..549963eed 100644 --- a/resources/js/components/user-select.js +++ b/resources/js/components/user-select.js @@ -1,6 +1,7 @@ import {onChildEvent} from "../services/dom"; +import {Component} from "./component"; -class UserSelect { +export class UserSelect extends Component { setup() { this.input = this.$refs.input; @@ -13,13 +14,10 @@ class UserSelect { selectUser(event, userEl) { event.preventDefault(); - const id = userEl.getAttribute('data-id'); - this.input.value = id; + this.input.value = userEl.getAttribute('data-id'); this.userInfoContainer.innerHTML = userEl.innerHTML; this.input.dispatchEvent(new Event('change', {bubbles: true})); this.hide(); } -} - -export default UserSelect; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/js/components/webhook-events.js b/resources/js/components/webhook-events.js index aa50aa9d8..ad8c59ac2 100644 --- a/resources/js/components/webhook-events.js +++ b/resources/js/components/webhook-events.js @@ -1,10 +1,10 @@ - /** * Webhook Events * Manages dynamic selection control in the webhook form interface. - * @extends {Component} */ -class WebhookEvents { +import {Component} from "./component"; + +export class WebhookEvents extends Component { setup() { this.checkboxes = this.$el.querySelectorAll('input[type="checkbox"]'); @@ -27,6 +27,4 @@ class WebhookEvents { } } -} - -export default WebhookEvents; \ No newline at end of file +} \ No newline at end of file diff --git a/resources/sass/_forms.scss b/resources/sass/_forms.scss index 7de8a9d7d..f341ce486 100644 --- a/resources/sass/_forms.scss +++ b/resources/sass/_forms.scss @@ -328,7 +328,7 @@ input[type=color] { } } -.form-group[collapsible] { +.form-group.collapsible { padding: 0 $-m; border: 1px solid; @include lightDark(border-color, #DDD, #000); diff --git a/resources/views/books/parts/form.blade.php b/resources/views/books/parts/form.blade.php index bb87089b2..e893bcead 100644 --- a/resources/views/books/parts/form.blade.php +++ b/resources/views/books/parts/form.blade.php @@ -10,11 +10,11 @@ @include('form.textarea', ['name' => 'description']) -