diff --git a/css/apps/files.scss b/css/apps/files.scss index 44e852d..8ee3ae0 100644 --- a/css/apps/files.scss +++ b/css/apps/files.scss @@ -407,6 +407,30 @@ table.files-filestable { } } +// Below 512px NC Teleports this to as a bottom-corner FAB. +// Selector must out-specify NC's `.--narrow[data-v-]` without using the hash. +form.upload-picker.files-list__header-upload-button--narrow { + inset-block-start: 86px; // grid button y=90, less the 4px form inset + inset-block-end: auto; + inset-inline-end: 80px; // 24px header margin + 40px grid button + 16px gap + + // NC sets `width: var(--button-size) !important` on icon-only buttons + button.action-item__menutoggle { + --button-size: 40px; + + width: 40px !important; + height: 40px !important; + min-width: 40px !important; + min-height: 40px !important; + + .button-vue__icon { + width: 40px; + min-width: 40px; + height: 40px; + } + } +} + #app-content-vue { // Override favorite marker stroke color @@ -471,6 +495,55 @@ table.files-filestable { } + .files-list__header-upload-button { + button.action-item__menutoggle { + all: unset; + align-items: center; + background-color: var(--color-primary); + border-radius: var(--border-radius-rounded); + cursor: pointer; + display: flex; + justify-content: center; + height: 40px; + margin-inline-end: 1rem; + + &:hover:not(:disabled) { + background-color: var(--color-primary-hover); + } + + &:active:not(:disabled) { + background-color: var(--color-primary-pressed); + } + + // all: unset strips the button-vue focus ring + &:focus-visible { + outline: 2px solid var(--color-main-text); + outline-offset: 2px; + } + + .button-vue__icon { + background-image: var(--original-icon-add-white); + background-size: 18px; + opacity: 1; + width: 18px; + height: 18px; + background-position: center; + background-repeat: no-repeat; + margin-inline: 4px -4px; + min-width: 40px; + + svg { + display: none; + } + } + + .button-vue__text { + color: var(--nmc-color-text-and-icon-white); + padding-right: 1rem; + } + } + } + .upload-picker__progress { width: 300px; max-width: 320px; diff --git a/src/nmcfiles.ts b/src/nmcfiles.ts index b561abd..7be3ee2 100644 --- a/src/nmcfiles.ts +++ b/src/nmcfiles.ts @@ -98,15 +98,35 @@ if (sharingStatusAction) { } -function updateLabel(selector) { - const buttonText = document.querySelector(selector)?.querySelector('.upload-picker')?.querySelector('.button-vue__text') +/** + * NC33 labels the upload button "New"; production calls it "Add". + */ +function fixUploadButton(container: Element): void { + const buttonText = container.querySelector('.button-vue__text') - if (buttonText) { + if (buttonText && buttonText.textContent !== t('nmctheme', 'Add')) { buttonText.textContent = t('nmctheme', 'Add') - return true } +} - return false +/** + * Watches the document because Vue re-renders the button on navigation. + */ +function setupUploadButtonFix(): void { + const apply = (): void => { + document.querySelectorAll('.files-list__header-upload-button').forEach(fixUploadButton) + } + + apply() + + const observer = new MutationObserver(() => apply()) + observer.observe(document.body, { childList: true, subtree: true }) +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', setupUploadButtonFix) +} else { + setupUploadButtonFix() } /** @@ -325,33 +345,4 @@ if (document.readyState === 'loading') { setupFilterRelocation() } -window.addEventListener('DOMContentLoaded', function() { - const breadcrumb = document.querySelector('.breadcrumb') - const empty = document.querySelector('.files-list__empty') - - if (breadcrumb) { - const observer = new MutationObserver(() => { - if (updateLabel('.breadcrumb')) { - observer.disconnect() - } - }) - - observer.observe(breadcrumb, { - childList: true, - subtree: true, - }) - } - if (empty) { - const observerB = new MutationObserver(() => { - if (updateLabel('.files-list__empty')) { - observerB.disconnect() - } - }) - - observerB.observe(empty, { - childList: true, - subtree: true, - }) - } -})