Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions css/apps/files.scss
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,17 @@ table.files-filestable {
.files-list__header {
margin-block: 15px;
margin-inline: 3rem 1.5rem;
display: flex;
align-items: center;

// NC33 renders "New" as a direct header child; push it to the right.
> div:has(> .files-list__header-upload-button) {
order: 2;
margin-left: auto;
}

.files-list__header-grid-button {
order: 3;
border: 1px solid var(--color-main-text);
border-radius: 50%;
width: 40px !important;
Expand Down Expand Up @@ -576,9 +585,107 @@ table.files-filestable {
padding-inline-end: 1rem;
}

// Chip styling for the NC33 filters; nmcfiles.ts relocates them here.
[data-test-id="files-list-filters"] {
display: flex;
align-items: center;
gap: 0.5rem;

// Hide the People filter (absent in the legacy layout).
> div:nth-child(3) {
display: none;
}

button.button-vue {
background-color: var(--nmc-ods-blue-primary);
color: var(--nmc-color-text-and-icon-black);
border: none;
border-radius: var(--border-radius-small);
height: 28px;
min-height: 28px;
min-width: unset;
padding: 0 10px;
font-weight: normal;

&:hover {
background-color: var(--nmc-ods-blue-hover);
}

&:active,
&[aria-expanded="true"] {
background-color: var(--nmc-ods-blue-active);
}

// Hide the leading icon; replace with a trailing caret like the chips.
.button-vue__icon {
display: none;
}

.button-vue__text {
color: var(--nmc-color-text-and-icon-black);
}

.button-vue__wrapper::after {
content: '';
display: inline-block;
width: 6px;
height: 6px;
border-left: 1.5px solid var(--nmc-color-text-and-icon-black);
border-bottom: 1.5px solid var(--nmc-color-text-and-icon-black);
transform: rotate(-45deg);
margin-left: 8px;
margin-bottom: 2px;
transition: transform 0.2s ease;
}

&[aria-expanded="true"] .button-vue__wrapper::after {
transform: rotate(135deg);
margin-bottom: -2px;
}
}
}

.files-list__filters {
padding-block: 1rem;
padding-inline: 4rem 1rem;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 0.5rem;

// NC33 renders the toggles after the chips; show them first (left) and
// cap their container so it can't stretch and push the chips off-screen.
> [data-test-id="files-list-filters"] {
order: -1;
flex: 0 0 auto;
width: max-content;
max-width: max-content;
}

> ul[aria-label="Active filters"] {
flex: 0 0 auto;
display: flex;
align-items: center;
gap: 0.5rem;
list-style: none;
margin: 0;
padding: 0;
}

// Divider anchored as the toggles' last flex item so it sits a fixed gap
// after "Modified"; only shown when filters are active.
&:has(> ul[aria-label="Active filters"] li) {
gap: 0.25rem;

> [data-test-id="files-list-filters"]::after {
content: "";
flex: 0 0 auto;
width: 1px;
height: 1.5rem;
margin-inline-start: -0.25rem;
background-color: var(--color-border);
}
}

.file-list-filters {
display: flex;
Expand Down
75 changes: 75 additions & 0 deletions css/components/ncactions.scss
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,81 @@
padding: 1rem;
}

// NC33 file-type filter uses NcButton items (no ul/li); reshape the
// popover to match the legacy action menu below.
files-file-list-filter-type .icon-vue svg path {
fill: var(--color-main-text);
}

&:has(files-file-list-filter-type) {
width: fit-content;
min-width: 213px;
max-height: min(312px, 60vh);
overflow-y: auto;

> div,
> div > div,
files-file-list-filter-type,
files-file-list-filter-type > div,
.button-vue {
min-width: 0;
}

> div,
> div > div {
padding: 0;
}

files-file-list-filter-type > div {
display: flex;
flex-direction: column;
gap: 0;
padding: 0;
}

.button-vue {
margin: 0;
border-radius: var(--border-radius);
padding-inline: 4px 16px;

// Grey on hover only, not the first item's focus-on-open.
&:hover,
&:focus-visible {
background-color: var(--color-background-hover);
}

.button-vue__text {
font-weight: normal;
}

// Selected: strip NcButton's magenta pressed fill, keep a dark
// label and add a checkmark like the legacy menu.
&[aria-pressed="true"] {
color: var(--color-main-text);

&:not(:hover):not(:focus-visible) {
background-color: transparent;
}

.button-vue__text {
color: var(--color-main-text);
}

.button-vue__wrapper::after {
content: "";
display: inline-block;
flex: 0 0 auto;
width: 16px;
height: 16px;
margin-inline-start: auto;
background-color: currentColor;
-webkit-mask: var(--icon-check-dark) no-repeat center / contain;
mask: var(--icon-check-dark) no-repeat center / contain;
}
}
}
}

ul {
display: flex;
flex-direction: column;
Expand Down
5 changes: 5 additions & 0 deletions css/components/ncappnavigation.scss
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@
// default navbar link styling
#app-navigation-vue .app-navigation-entry-wrapper {

// Cancel the framework's margin-top:auto so "Deleted files" isn't pinned to the bottom.
&.app-navigation-entry--pinned {
margin-top: 0;
}

.app-navigation-entry {
position: relative;
border-radius: var(--border-radius-large);
Expand Down
18 changes: 17 additions & 1 deletion css/components/ncusermenu.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@
order: 1;
}

li#profile {
li:has(a#logout) {
order: 3;
}

li:has(a#profile) {
display: none;
}

Expand Down Expand Up @@ -150,6 +154,18 @@
background-image: var(--icon-logout-dark);
}

> a#admin_settings::after {
background-image: var(--icon-admin-dark);
}

> a#core_apps::after {
background-image: var(--icon-apps-dark);
}

> a#core_users::after {
background-image: var(--icon-users-dark);
}

> a#nmc_welcome_popup-about {

.loading-icon {
Expand Down
1 change: 1 addition & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ protected function registerJSResourceLocatorExtension(IRegistrationContext $cont
$this->getContainer()->getServer()->registerService(JSResourceLocator::class, function (ContainerInterface $c) {
return new JSResourceLocatorExtension(
$c->get(LoggerInterface::class),
$c->get(IConfig::class),
$this->getContainer()->getServer()->query(JSCombiner::class),
$c->get(IAppManager::class)
);
Expand Down
13 changes: 11 additions & 2 deletions lib/JSResourceLocatorExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use OC\Template\JSCombiner;
use OC\Template\JSResourceLocator;
use OCP\App\IAppManager;
use OCP\IConfig;
use Psr\Log\LoggerInterface;

class JSResourceLocatorExtension extends JSResourceLocator {
Expand All @@ -28,9 +29,17 @@ class JSResourceLocatorExtension extends JSResourceLocator {
* so we try to handle different backports in this constructor.
*
*/
public function __construct(LoggerInterface $logger, JSCombiner $jsCombiner, IAppManager $appManager) {
public function __construct(LoggerInterface $logger, IConfig $config, JSCombiner $jsCombiner, IAppManager $appManager) {
$this->ownAppManager = $appManager;

// V33
try {
parent::__construct($logger, $config, $jsCombiner, $appManager);
return;
} catch (\Throwable $eWrongConstructNC33) {
// ignore the exception, try another constructor
}

// later
try {
parent::__construct($logger, $jsCombiner, $appManager);
Expand Down Expand Up @@ -59,7 +68,7 @@ public function __construct(LoggerInterface $logger, JSCombiner $jsCombiner, IAp
/**
* Deviate all language requests to the nmctheme language extension service
*/
public function doFind($script) {
public function doFind(string $script): void {
// Translation extensions
if (str_contains($script, '/l10n/')) {
// only add script if corresponding app has a language json
Expand Down
57 changes: 39 additions & 18 deletions src/js/filessettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,51 @@ const View = new StorageQuotaView({
pinia,
})

window.addEventListener('DOMContentLoaded', () => {
// Select the <li> that should be moved
const settingsButton = document.querySelector(
'li[data-cy-files-navigation-settings-button]'
)
let isMounted = false

// Target Files navigation list
const filesNavList = document.querySelector('.files-navigation__list')

if (!settingsButton || !filesNavList) {
return
const setupStorageQuota = () => {
if (isMounted) {
return true
}

// Move the settings button into the Files navigation list
filesNavList.appendChild(settingsButton)

// Create the mount anchor
// Rendered by Vue after DOMContentLoaded; retry via the observer until present.
const entrySettings = document.querySelector('.app-navigation-entry__settings')
if (!entrySettings) return
if (!entrySettings) {
return false
}

// Best-effort: lift the "Files settings" button into the main nav list.
const settingsButton = document.querySelector('li[data-cy-files-navigation-settings-button]')
const filesNavList = document.querySelector('.files-navigation__list, .app-navigation__list')
if (settingsButton && filesNavList) {
filesNavList.appendChild(settingsButton)
}

const anchor = document.createElement('div')
anchor.id = 'storage-quota-app'
entrySettings.appendChild(anchor)

// Mount the vue component
View.$mount('#storage-quota-app')
})

isMounted = true
return true
}

const waitForNavigation = () => {
if (setupStorageQuota()) {
return
}

const observer = new MutationObserver(() => {
if (setupStorageQuota()) {
observer.disconnect()
}
})
observer.observe(document.body, { childList: true, subtree: true })
}

// Handle the bundle loading after DOMContentLoaded has already fired.
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', waitForNavigation)
} else {
waitForNavigation()
}
Loading
Loading