forked from deblan/side_menu
migrate admin settings
This commit is contained in:
parent
8c5c43dafa
commit
f9c3f96919
25 changed files with 1028 additions and 112 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -5,3 +5,5 @@
|
||||||
/package-lock.json
|
/package-lock.json
|
||||||
!/l10n/.gitkeep
|
!/l10n/.gitkeep
|
||||||
/yarn*.log
|
/yarn*.log
|
||||||
|
/src/admin.js.bk
|
||||||
|
/templates/settings/admin-form.php.bk
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use OCP\AppFramework\App;
|
||||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||||
|
use OCA\Theming\ThemingDefaults;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\INavigationManager;
|
use OCP\INavigationManager;
|
||||||
|
|
@ -20,6 +21,7 @@ use OCP\IUserSession;
|
||||||
use OCP\L10N\IFactory;
|
use OCP\L10N\IFactory;
|
||||||
use OCP\Util;
|
use OCP\Util;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
use OCA\SideMenu\Service\Color;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* class Application.
|
* class Application.
|
||||||
|
|
@ -81,6 +83,12 @@ class Application extends App implements IBootstrap
|
||||||
$c->get(IConfig::class),
|
$c->get(IConfig::class),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$context->registerService(Color::class, function (ContainerInterface $c) {
|
||||||
|
return new Color(
|
||||||
|
$c->get(ThemingDefaults::class),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function boot(IBootContext $context): void
|
public function boot(IBootContext $context): void
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,17 @@
|
||||||
namespace OCA\SideMenu\Controller;
|
namespace OCA\SideMenu\Controller;
|
||||||
|
|
||||||
use OCA\SideMenu\AppInfo\Application;
|
use OCA\SideMenu\AppInfo\Application;
|
||||||
|
use OCA\SideMenu\Service\ConfigProxy;
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
|
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
|
||||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||||
use OCP\AppFramework\Http\DataDownloadResponse;
|
use OCP\AppFramework\Http\DataDownloadResponse;
|
||||||
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
use OCP\AppFramework\Http\RedirectResponse;
|
use OCP\AppFramework\Http\RedirectResponse;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
|
use OCA\SideMenu\Service\Color;
|
||||||
|
|
||||||
class AdminSettingController extends Controller
|
class AdminSettingController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -35,7 +38,9 @@ class AdminSettingController extends Controller
|
||||||
$appName,
|
$appName,
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
protected IConfig $config,
|
protected IConfig $config,
|
||||||
protected IURLGenerator $urlGenerator
|
protected ConfigProxy $configProxy,
|
||||||
|
protected IURLGenerator $urlGenerator,
|
||||||
|
protected Color $color,
|
||||||
) {
|
) {
|
||||||
parent::__construct($appName, $request);
|
parent::__construct($appName, $request);
|
||||||
}
|
}
|
||||||
|
|
@ -76,4 +81,129 @@ class AdminSettingController extends Controller
|
||||||
'text/json'
|
'text/json'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[NoCSRFRequired]
|
||||||
|
#[FrontpageRoute(verb: 'GET', url: '/admin/config')]
|
||||||
|
public function configuration(): JSONResponse
|
||||||
|
{
|
||||||
|
$keys = $this->config->getAppKeys(Application::APP_ID);
|
||||||
|
$booleans = [
|
||||||
|
'opener-only',
|
||||||
|
'opener-hover',
|
||||||
|
'display-logo',
|
||||||
|
'use-avatar',
|
||||||
|
'add-logo-link',
|
||||||
|
'show-settings',
|
||||||
|
'loader-enabled',
|
||||||
|
'top-menu-mouse-over-hidden-label',
|
||||||
|
'always-displayed',
|
||||||
|
'enabled',
|
||||||
|
'force',
|
||||||
|
'big-menu',
|
||||||
|
'external-sites-in-top-menu',
|
||||||
|
'force-light-icon',
|
||||||
|
'side-with-categories',
|
||||||
|
'default-enabled',
|
||||||
|
];
|
||||||
|
|
||||||
|
$arrays = [
|
||||||
|
'apps-categories-custom',
|
||||||
|
'big-menu-hidden-apps',
|
||||||
|
'apps-order',
|
||||||
|
'categories-custom',
|
||||||
|
'categories-order',
|
||||||
|
'target-blank-apps',
|
||||||
|
'top-menu-apps',
|
||||||
|
'top-side-menu-apps',
|
||||||
|
];
|
||||||
|
|
||||||
|
$integers = [
|
||||||
|
'background-color-opacity',
|
||||||
|
'dark-mode-background-color-opacity',
|
||||||
|
'dark-mode-icon-invert-filter',
|
||||||
|
'dark-mode-icon-opacity',
|
||||||
|
'icon-invert-filter',
|
||||||
|
'icon-opacity',
|
||||||
|
'target-blank-mode',
|
||||||
|
'top-menu-mouse-over-hidden-label',
|
||||||
|
];
|
||||||
|
|
||||||
|
$defaults = [
|
||||||
|
'opener-only' => '0',
|
||||||
|
'opener-hover' => '0',
|
||||||
|
'display-logo' => '1',
|
||||||
|
'use-avatar' => '0',
|
||||||
|
'add-logo-link' => '1',
|
||||||
|
'show-settings' => '0',
|
||||||
|
'loader-enabled' => '1',
|
||||||
|
'top-menu-mouse-over-hidden-label' => '0',
|
||||||
|
'always-displayed' => '0',
|
||||||
|
'enabled' => '1',
|
||||||
|
'force' => '0',
|
||||||
|
'big-menu' => '0',
|
||||||
|
'external-sites-in-top-menu' => '0',
|
||||||
|
'force-light-icon' => '0',
|
||||||
|
'side-with-categories' => '0',
|
||||||
|
'cache' => '1',
|
||||||
|
'default-enabled' => '1',
|
||||||
|
|
||||||
|
'apps-categories-custom' => '[]',
|
||||||
|
'big-menu-hidden-apps' => '[]',
|
||||||
|
'apps-order' => '[]',
|
||||||
|
'categories-custom' => '[]',
|
||||||
|
'categories-order' => '[]',
|
||||||
|
'target-blank-apps' => '[]',
|
||||||
|
'top-menu-apps' => '[]',
|
||||||
|
'top-side-menu-apps' => '[]',
|
||||||
|
'cache-categories' => '[]',
|
||||||
|
|
||||||
|
'background-color-opacity' => '100',
|
||||||
|
'dark-mode-background-color-opacity' => '100',
|
||||||
|
'dark-mode-icon-invert-filter' => '0',
|
||||||
|
'dark-mode-icon-opacity' => '100',
|
||||||
|
'icon-invert-filter' => '0',
|
||||||
|
'icon-opacity' => '100',
|
||||||
|
'top-menu-mouse-over-hidden-label' => '0',
|
||||||
|
|
||||||
|
'opener' => 'side-menu-opener',
|
||||||
|
'dark-mode-opener' => 'side-menu-opener',
|
||||||
|
'size-icon' => 'normal',
|
||||||
|
'size-text' => 'normal',
|
||||||
|
'opener-position' => 'before',
|
||||||
|
|
||||||
|
'background-color' => $this->color->getPrimaryColor(),
|
||||||
|
'background-color-to' => $this->color->getLightenPrimaryColor(),
|
||||||
|
'current-app-background-color' => $this->color->getDarkenPrimaryColor(),
|
||||||
|
'text-color' => $this->color->getTextColorPrimary(),
|
||||||
|
'loader-color' => $this->color->getLightenPrimaryColor(),
|
||||||
|
|
||||||
|
'dark-mode-background-color' => $this->color->getDarkenPrimaryColor(),
|
||||||
|
'dark-mode-background-color-to' => $this->color->getDarkenPrimaryColor(),
|
||||||
|
'dark-mode-current-app-background-color' => $this->color->getDarkenPrimaryColor2(),
|
||||||
|
'dark-mode-text-color' => $this->color->getTextColorPrimary(),
|
||||||
|
'dark-mode-loader-color' => $this->color->getLightenPrimaryColor(),
|
||||||
|
|
||||||
|
'categories-order-type' => 'default',
|
||||||
|
];
|
||||||
|
|
||||||
|
$config = [];
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (!isset($defaults[$key])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($key, $booleans)) {
|
||||||
|
$config[$key] = $this->configProxy->getAppValueBool($key, $defaults[$key]);
|
||||||
|
} elseif (in_array($key, $arrays)) {
|
||||||
|
$config[$key] = $this->configProxy->getAppValueArray($key, $defaults[$key]);
|
||||||
|
} elseif (in_array($key, $integers)) {
|
||||||
|
$config[$key] = $this->configProxy->getAppValueInt($key, $defaults[$key]);
|
||||||
|
} else {
|
||||||
|
$config[$key] = $this->configProxy->getAppValue($key, $defaults[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JSONResponse($config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,11 +95,10 @@ class CssController extends Controller
|
||||||
$isDarkMode = ($isAccessibilityAppEnabled && $isDarkThemeUserEnabled)
|
$isDarkMode = ($isAccessibilityAppEnabled && $isDarkThemeUserEnabled)
|
||||||
|| ($isBreezeDarkAppEnabled && $isBreezeDarkUserEnabled);
|
|| ($isBreezeDarkAppEnabled && $isBreezeDarkUserEnabled);
|
||||||
|
|
||||||
$primaryColor = $this->theming->getColorPrimary();
|
$lightenPrimaryColor = $this->color->getLightenPrimaryColor();
|
||||||
$lightenPrimaryColor = $this->color->adjustBrightness($primaryColor, 0.2);
|
$darkenPrimaryColor = $this->color->getDarkenPrimaryColor();
|
||||||
$darkenPrimaryColor = $this->color->adjustBrightness($primaryColor, -0.2);
|
$darkenPrimaryColor2 = $this->color->getDarkenPrimaryColor2();
|
||||||
$darkenPrimaryColor2 = $this->color->adjustBrightness($primaryColor, -0.3);
|
$textColor = $this->color->getTextColorPrimary();
|
||||||
$textColor = $this->theming->getTextColorPrimary();
|
|
||||||
|
|
||||||
if ($isDarkMode) {
|
if ($isDarkMode) {
|
||||||
$backgroundColor = $this->config->getAppValue('dark-mode-background-color', $darkenPrimaryColor);
|
$backgroundColor = $this->config->getAppValue('dark-mode-background-color', $darkenPrimaryColor);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace OCA\SideMenu\Service;
|
namespace OCA\SideMenu\Service;
|
||||||
|
|
||||||
|
use OCA\Theming\ThemingDefaults;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* class Color.
|
* class Color.
|
||||||
*
|
*
|
||||||
|
|
@ -9,6 +11,10 @@ namespace OCA\SideMenu\Service;
|
||||||
*/
|
*/
|
||||||
class Color
|
class Color
|
||||||
{
|
{
|
||||||
|
public function __construct(protected ThemingDefaults $theming)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @thanks https://stackoverflow.com/posts/54393956/revision
|
* @thanks https://stackoverflow.com/posts/54393956/revision
|
||||||
*/
|
*/
|
||||||
|
|
@ -31,4 +37,29 @@ class Color
|
||||||
|
|
||||||
return '#'.implode($hexCode);
|
return '#'.implode($hexCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPrimaryColor()
|
||||||
|
{
|
||||||
|
return $this->theming->getColorPrimary();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLightenPrimaryColor()
|
||||||
|
{
|
||||||
|
return $this->adjustBrightness($this->getPrimaryColor(), 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDarkenPrimaryColor()
|
||||||
|
{
|
||||||
|
return $this->adjustBrightness($this->getPrimaryColor(), -0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDarkenPrimaryColor2()
|
||||||
|
{
|
||||||
|
return $this->adjustBrightness($this->getPrimaryColor(), -0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTextColorPrimary()
|
||||||
|
{
|
||||||
|
return $this->theming->getTextColorPrimary();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,6 @@ waitContainer('#side-menu-admin-settings').then((selector) => {
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
const app = createApp(AdminSettings)
|
const app = createApp(AdminSettings)
|
||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.mixin({ methods: { t, n }})
|
app.mixin({ methods: { t, n } })
|
||||||
app.mount(selector)
|
app.mount(selector)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
<template>
|
<template></template>
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
// import AlwaysDisplayImg from '../../img/admin/layout-always-displayed.svg'
|
// import AlwaysDisplayImg from '../../img/admin/layout-always-displayed.svg'
|
||||||
|
|
@ -17,7 +15,7 @@ const choices = [
|
||||||
|
|
||||||
const carouselConfig = {
|
const carouselConfig = {
|
||||||
itemsToShow: 1.3,
|
itemsToShow: 1.3,
|
||||||
wrapAround: true
|
wrapAround: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
const { mode, alwaysDisplayed } = defineProps({
|
const { mode, alwaysDisplayed } = defineProps({
|
||||||
|
|
@ -28,6 +26,6 @@ const { mode, alwaysDisplayed } = defineProps({
|
||||||
alwaysDisplayed: {
|
alwaysDisplayed: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
19
src/components/settings/SectionTitle.vue
Normal file
19
src/components/settings/SectionTitle.vue
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<h2>{{ t('side_menu', label) }}</h2>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
h2 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const { label } = defineProps({
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -1,10 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="side-menu-setting-label" :class="{
|
<div
|
||||||
'side-menu-setting-label-short': short,
|
class="side-menu-setting-label"
|
||||||
'side-menu-setting-label--top': top,
|
:class="{
|
||||||
'side-menu-setting-label--middle': middle,
|
'side-menu-setting-label-short': short,
|
||||||
}">
|
'side-menu-setting-label--top': top,
|
||||||
|
'side-menu-setting-label--middle': middle,
|
||||||
|
}"
|
||||||
|
>
|
||||||
{{ t('side_menu', label) }}
|
{{ t('side_menu', label) }}
|
||||||
|
|
||||||
|
<template v-if="help">
|
||||||
|
<br />
|
||||||
|
<em>{{ help }}</em>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -29,5 +37,10 @@ const { short, label } = defineProps({
|
||||||
required: false,
|
required: false,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
help: {
|
||||||
|
type: [String, null],
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="side-menu-setting-form" :class="{'side-menu-setting-form-long': long}">
|
<div
|
||||||
|
class="side-menu-setting-form"
|
||||||
|
:class="{ 'side-menu-setting-form-long': long }"
|
||||||
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
82
src/components/settings/form/FormAppPicker.vue
Normal file
82
src/components/settings/form/FormAppPicker.vue
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<NcButton
|
||||||
|
aria-label="t('side_menu', 'Select apps')"
|
||||||
|
@click="openModal"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
{{ t('side_menu', 'Select apps') }} ({{ model.length }})
|
||||||
|
</NcButton>
|
||||||
|
|
||||||
|
<NcModal
|
||||||
|
v-if="modal"
|
||||||
|
@close="closeModal"
|
||||||
|
>
|
||||||
|
<div class="modal__content">
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
v-for="(item, key) in apps"
|
||||||
|
v-model="model"
|
||||||
|
name="value"
|
||||||
|
:value="item.id"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="item.icon"
|
||||||
|
:alt="item.name"
|
||||||
|
/>
|
||||||
|
{{ item.name }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
|
||||||
|
<div class="modal__footer">
|
||||||
|
<NcButton
|
||||||
|
@click="closeModal"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
{{ t('side_menu', 'Close') }}
|
||||||
|
</NcButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NcModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.modal__content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__footer {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__footer button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { NcButton, NcModal, NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
||||||
|
import { useNavStore } from '../../../store/nav.js'
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
|
const model = defineModel()
|
||||||
|
const navStore = useNavStore()
|
||||||
|
const modal = ref(false)
|
||||||
|
const apps = ref([])
|
||||||
|
|
||||||
|
const openModal = () => {
|
||||||
|
modal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
modal.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
apps.value = await navStore.getApps()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
17
src/components/settings/form/FormColorPicker.vue
Normal file
17
src/components/settings/form/FormColorPicker.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<NcColorPicker
|
||||||
|
v-model="model"
|
||||||
|
class="side-menu-setting-color-picker"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:style="{ 'background-color': model }"
|
||||||
|
class="side-menu-setting-color-picker-value"
|
||||||
|
/>
|
||||||
|
</NcColorPicker>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { NcColorPicker } from '@nextcloud/vue'
|
||||||
|
|
||||||
|
const model = defineModel()
|
||||||
|
</script>
|
||||||
20
src/components/settings/form/FormOpener.vue
Normal file
20
src/components/settings/form/FormOpener.vue
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<FormSelect
|
||||||
|
v-model="model"
|
||||||
|
:options="options"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import FormSelect from './FormSelect'
|
||||||
|
|
||||||
|
const model = defineModel()
|
||||||
|
const options = [
|
||||||
|
{ id: 'side-menu-opener', label: 'Default' },
|
||||||
|
{ id: 'side-menu-opener-dark', label: 'Default (dark)' },
|
||||||
|
{ id: 'side-menu-opener-hamburger', label: 'Hamburger' },
|
||||||
|
{ id: 'side-menu-opener-hamburger-dark', label: 'Hamburger (dark)' },
|
||||||
|
{ id: 'side-menu-opener-hamburger-2', label: 'Hamburger 2' },
|
||||||
|
{ id: 'side-menu-opener-hamburger-2-dark', label: 'Hamburger 2 (dark)' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
56
src/components/settings/form/FormRange.vue
Normal file
56
src/components/settings/form/FormRange.vue
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<em v-if="prepend">{{ t('side_menu', prepend) }}</em>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
:min="min"
|
||||||
|
:max="max"
|
||||||
|
v-model="model"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<em v-if="append">{{ t('side_menu', append) }}</em>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
input {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div * {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
em + input,
|
||||||
|
input + em {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const model = defineModel()
|
||||||
|
|
||||||
|
const { prefix, suffix } = defineProps({
|
||||||
|
prepend: {
|
||||||
|
type: [String, null],
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
append: {
|
||||||
|
type: [String, null],
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 100,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
48
src/components/settings/form/FormSelect.vue
Normal file
48
src/components/settings/form/FormSelect.vue
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<template v-if="!expanded">
|
||||||
|
<select v-model="model" v-if="!expanded" :multiple="multiple">
|
||||||
|
<option
|
||||||
|
v-for="option in options"
|
||||||
|
:value="option.id"
|
||||||
|
>
|
||||||
|
{{ t('side_menu', option.label) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
v-for="option in options"
|
||||||
|
v-model="model"
|
||||||
|
:value="option.id"
|
||||||
|
:key="option.id"
|
||||||
|
:type="multiple ? 'checkbox' : 'radio'"
|
||||||
|
name="value"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
||||||
|
|
||||||
|
const model = defineModel()
|
||||||
|
const { options, expanded } = defineProps({
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
expanded: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
18
src/components/settings/form/FormSize.vue
Normal file
18
src/components/settings/form/FormSize.vue
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<template>
|
||||||
|
<FormSelect
|
||||||
|
v-model="model"
|
||||||
|
:options="options"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import FormSelect from './FormSelect'
|
||||||
|
|
||||||
|
const model = defineModel()
|
||||||
|
const options = [
|
||||||
|
{ id: 'hidden', label: 'Hidden' },
|
||||||
|
{ id: 'small', label: 'Small' },
|
||||||
|
{ id: 'normal', label: 'Normal' },
|
||||||
|
{ id: 'big', label: 'Big' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
12
src/components/settings/form/FormYesNo.vue
Normal file
12
src/components/settings/form/FormYesNo.vue
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<template>
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
v-model="model"
|
||||||
|
type="switch"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
||||||
|
|
||||||
|
const model = defineModel()
|
||||||
|
</script>
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
const focusActiveApp = (menu) => {
|
const focusActiveApp = (menu) => {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
const a = menu.querySelector('.side-menu-app.active a')
|
const a = menu.querySelector('.side-menu-app.active a') || menu.querySelector('.side-menu-app a')
|
||||||
|| menu.querySelector('.side-menu-app a')
|
|
||||||
|
|
||||||
if (a) {
|
if (a) {
|
||||||
a.focus()
|
a.focus()
|
||||||
|
|
|
||||||
|
|
@ -109,11 +109,14 @@ const openerHover = ref(false)
|
||||||
const menu = useTemplateRef('menu')
|
const menu = useTemplateRef('menu')
|
||||||
const isTouchDevice = window.matchMedia('(pointer: coarse)').matches
|
const isTouchDevice = window.matchMedia('(pointer: coarse)').matches
|
||||||
|
|
||||||
watch(() => open, (val) => {
|
watch(
|
||||||
if (val) {
|
() => open,
|
||||||
focusActiveApp(menu.value)
|
(val) => {
|
||||||
}
|
if (val) {
|
||||||
})
|
focusActiveApp(menu.value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const config = await configStore.getConfig()
|
const config = await configStore.getConfig()
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
:label="settings.name"
|
:label="settings.name"
|
||||||
:avatar="settings.avatar"
|
:avatar="settings.avatar"
|
||||||
/>
|
/>
|
||||||
<AppSearch v-model="search" v-if="open" />
|
<AppSearch
|
||||||
|
v-model="search"
|
||||||
|
v-if="open"
|
||||||
|
/>
|
||||||
<OpenerButton
|
<OpenerButton
|
||||||
v-if="!openerHover || isTouchDevice"
|
v-if="!openerHover || isTouchDevice"
|
||||||
@click="$emit('toggle')"
|
@click="$emit('toggle')"
|
||||||
|
|
@ -108,11 +111,14 @@ watch(apps, (val) => {
|
||||||
document.querySelector('html').classList.toggle('side-menu-always-displayed', alwaysDisplayed.value && val.length)
|
document.querySelector('html').classList.toggle('side-menu-always-displayed', alwaysDisplayed.value && val.length)
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => open, (val) => {
|
watch(
|
||||||
if (val) {
|
() => open,
|
||||||
focusActiveApp(menu.value)
|
(val) => {
|
||||||
}
|
if (val) {
|
||||||
})
|
focusActiveApp(menu.value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
function getFiltredAndSortedApps(items, order, topMenuApps, topSideMenuApps) {
|
function getFiltredAndSortedApps(items, order, topMenuApps, topSideMenuApps) {
|
||||||
const data = []
|
const data = []
|
||||||
|
|
@ -148,10 +154,7 @@ onMounted(async () => {
|
||||||
avatar.value = config['avatar']
|
avatar.value = config['avatar']
|
||||||
logo.value = config['logo']
|
logo.value = config['logo']
|
||||||
useAvatarAsLogo.value = config['use-avatar']
|
useAvatarAsLogo.value = config['use-avatar']
|
||||||
displayLogo.value = config['display-logo'] && !alwaysDisplayed.value && (
|
displayLogo.value = config['display-logo'] && !alwaysDisplayed.value && ((!useAvatarAsLogo.value && logo.value) || (useAvatarAsLogo.value && avatar.value))
|
||||||
(!useAvatarAsLogo.value && logo.value) ||
|
|
||||||
(useAvatarAsLogo.value && avatar.value)
|
|
||||||
)
|
|
||||||
|
|
||||||
logoLink.value = config['logo-link']
|
logoLink.value = config['logo-link']
|
||||||
settings.value = config['settings']
|
settings.value = config['settings']
|
||||||
|
|
|
||||||
|
|
@ -114,11 +114,14 @@ const openerHover = ref(false)
|
||||||
const menu = useTemplateRef('menu')
|
const menu = useTemplateRef('menu')
|
||||||
const isTouchDevice = window.matchMedia('(pointer: coarse)').matches
|
const isTouchDevice = window.matchMedia('(pointer: coarse)').matches
|
||||||
|
|
||||||
watch(() => open, (val) => {
|
watch(
|
||||||
if (val) {
|
() => open,
|
||||||
focusActiveApp(menu.value)
|
(val) => {
|
||||||
}
|
if (val) {
|
||||||
})
|
focusActiveApp(menu.value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const config = await configStore.getConfig()
|
const config = await configStore.getConfig()
|
||||||
|
|
|
||||||
|
|
@ -1,92 +1,505 @@
|
||||||
<template>
|
<template>
|
||||||
<NcContent app-name="side_menu">
|
<NcContent
|
||||||
<NcAppContent>
|
app-name="side_menu"
|
||||||
<div class="side-menu-setting">
|
v-if="config"
|
||||||
<NcButton
|
>
|
||||||
v-for="item in menu"
|
<NcAppContent>
|
||||||
@click="setSection(item.section)"
|
<div class="side-menu-setting">
|
||||||
:variant="item.section === section ? 'primary' : 'secondary'"
|
<NcButton
|
||||||
>{{ trans(item.label) }}</NcButton>
|
v-for="item in menu"
|
||||||
</div>
|
@click="setSection(item.section)"
|
||||||
|
:variant="item.section === section ? 'primary' : 'secondary'"
|
||||||
|
>{{ trans(item.label) }}</NcButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<TableContainer :class="sectionClass('panel')">
|
<TableContainer :class="sectionClass('panel')">
|
||||||
</TableContainer>
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Display the logo"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormYesNo v-model="config['display-logo']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Use the avatar instead of the logo"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormYesNo v-model="config['use-avatar']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="The logo is a link to the default app"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormYesNo v-model="config['add-logo-link']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Show the link to settings"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormYesNo v-model="config['show-settings']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Icons"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormSize v-model="config['size-icon']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Texts"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormSize v-model="config['size-text']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Loader enabled"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormYesNo v-model="config['loader-enabled']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
</TableContainer>
|
||||||
|
|
||||||
<TableContainer :class="sectionClass('color')">
|
<TableContainer :class="sectionClass('topMenu')">
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableLabel label="Background color" :short="true" :middle="true"></TableLabel>
|
<TableLabel
|
||||||
<TableValue>
|
label="Applications kept in the top menu"
|
||||||
<NcColorPicker v-model="color" class="side-menu-setting-color-picker">
|
:middle="true"
|
||||||
<div :style="{'background-color': color}" class="side-menu-setting-color-picker-value" />
|
/>
|
||||||
</NcColorPicker>
|
<TableValue>
|
||||||
<NcColorPicker v-model="color" class="side-menu-setting-color-picker">
|
<FormAppPicker v-model="config['top-menu-apps']" />
|
||||||
<div :style="{'background-color': color}" class="side-menu-setting-color-picker-value" />
|
</TableValue>
|
||||||
</NcColorPicker>
|
</TableRow>
|
||||||
</TableValue>
|
|
||||||
</TableRow>
|
|
||||||
|
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableLabel label="Background color of current app" :short="true" :middle="true"></TableLabel>
|
<TableLabel
|
||||||
<TableValue>
|
label="Applications kept in the top menu but also shown in side menu"
|
||||||
<NcColorPicker v-model="color" class="side-menu-setting-color-picker">
|
help="These applications must be selected in the previous option."
|
||||||
<div :style="{'background-color': color}" class="side-menu-setting-color-picker-value" />
|
:middle="true"
|
||||||
</NcColorPicker>
|
/>
|
||||||
</TableValue>
|
<TableValue>
|
||||||
</TableRow>
|
<FormAppPicker v-model="config['top-side-menu-apps']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableLabel label="Text color" :short="true" :middle="true"></TableLabel>
|
<TableLabel
|
||||||
<TableValue>
|
label="Hide labels on mouse over"
|
||||||
<NcColorPicker v-model="color" class="side-menu-setting-color-picker">
|
:top="true"
|
||||||
<div :style="{'background-color': color}" class="side-menu-setting-color-picker-value" />
|
/>
|
||||||
</NcColorPicker>
|
<TableValue>
|
||||||
</TableValue>
|
<FormSelect
|
||||||
</TableRow>
|
v-model="config['top-menu-mouse-over-hidden-label']"
|
||||||
</TableContainer>
|
:expanded="true"
|
||||||
</NcAppContent>
|
:options="[
|
||||||
</NcContent>
|
{id: 1, label: 'Yes'},
|
||||||
|
{id: 0, label: 'No'},
|
||||||
|
{id: 2, label: 'Except the hovered app'},
|
||||||
|
]" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
</TableContainer>
|
||||||
|
|
||||||
|
<TableContainer :class="sectionClass('apps')">
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Apps that should not be displayed in the menu"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormAppPicker v-model="config['big-menu-hidden-apps']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Open apps in new tab"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormAppPicker v-model="config['target-blank-apps']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
</TableContainer>
|
||||||
|
|
||||||
|
<TableContainer :class="sectionClass('opener')">
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Opener"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormOpener v-model="config['opener']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Dark mode opener"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormOpener v-model="config['dark-mode-opener']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Position"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormSelect
|
||||||
|
v-model="config['opener-position']"
|
||||||
|
:options="[
|
||||||
|
{ id: 'before', label: 'Before the logo' },
|
||||||
|
{ id: 'after', label: 'After the logo' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Show only the opener (hidden logo)"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormYesNo v-model="config['opener-only']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Open the menu when the mouse is hover the opener (automatically disabled on touch screens)"
|
||||||
|
help="This is the automatic behavior when the menu is always displayed."
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormYesNo v-model="config['opener-hover']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
</TableContainer>
|
||||||
|
|
||||||
|
<TableContainer :class="sectionClass('colors')">
|
||||||
|
<SectionTitle label="Colors" />
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Background color"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormColorPicker v-model="config['background-color']" />
|
||||||
|
<FormColorPicker v-model="config['background-color-to']" />
|
||||||
|
<FormRange
|
||||||
|
v-model="config['background-color-opacity']"
|
||||||
|
prepend="Transparent"
|
||||||
|
append="Opaque"
|
||||||
|
/>
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Background color of current app"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormColorPicker v-model="config['current-app-background-color']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Text color"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormColorPicker v-model="config['text-color']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Loader"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormColorPicker v-model="config['loader-color']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Icon"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormRange
|
||||||
|
v-model="config['icon-invert-filter']"
|
||||||
|
prepend="Same color"
|
||||||
|
append="Opposite color"
|
||||||
|
/>
|
||||||
|
<FormRange
|
||||||
|
v-model="config['icon-opacity']"
|
||||||
|
prepend="Transparent"
|
||||||
|
append="Opaque"
|
||||||
|
/>
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<SectionTitle label="Dark mode colors" />
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Background color"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormColorPicker v-model="config['dark-mode-background-color']" />
|
||||||
|
<FormColorPicker v-model="config['dark-mode-background-color-to']" />
|
||||||
|
<FormRange
|
||||||
|
v-model="config['dark-mode-background-color-opacity']"
|
||||||
|
prepend="Transparent"
|
||||||
|
append="Opaque"
|
||||||
|
/>
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Background color of current app"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormColorPicker v-model="config['dark-mode-current-app-background-color']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Text color"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormColorPicker v-model="config['dark-mode-text-color']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Loader"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormColorPicker v-model="config['dark-mode-loader-color']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Icon"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormRange
|
||||||
|
v-model="config['dark-mode-icon-invert-filter']"
|
||||||
|
prepend="Same color"
|
||||||
|
append="Opposite color"
|
||||||
|
/>
|
||||||
|
<FormRange
|
||||||
|
v-model="config['dark-mode-icon-opacity']"
|
||||||
|
prepend="Transparent"
|
||||||
|
append="Opaque"
|
||||||
|
/>
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
</TableContainer>
|
||||||
|
|
||||||
|
<TableContainer :class="sectionClass('global')">
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="The menu is enabled by default for users"
|
||||||
|
help="Except when the configuration is forced."
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormYesNo v-model="config['default-enabled']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Force this configuration to users"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<FormYesNo v-model="config['force']" />
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
</TableContainer>
|
||||||
|
|
||||||
|
<TableContainer :class="sectionClass('support')">
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="You like this app and you want to support me?"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<a target="_blank" href="https://www.buymeacoffee.com/deblan" rel="noopener">
|
||||||
|
<NcButton variant="secondary">{{ trans('Buy me a coffee ☕') }}</NcButton>
|
||||||
|
</a>
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Need help"
|
||||||
|
/>
|
||||||
|
<TableValue class="button-inline">
|
||||||
|
<a target="_blank" href="https://deblan.gitnet.page/side_menu_doc/" rel="noopener">
|
||||||
|
<NcButton variant="secondary">{{ trans('Open the documentation') }}</NcButton>
|
||||||
|
</a>
|
||||||
|
<a target="_blank" href="https://gitnet.fr/deblan/side_menu/issues/new?template=.gitea%2fissue_template%2fQUESTION_TEMPLATE.yml" rel="noopener">
|
||||||
|
<NcButton variant="secondary">{{ trans('Ask the developer') }}</NcButton>
|
||||||
|
</a>
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="I would like a new feature"
|
||||||
|
:middle="true"
|
||||||
|
/>
|
||||||
|
<TableValue>
|
||||||
|
<a target="_blank" href="https://gitnet.fr/deblan/side_menu/issues/new?template=.gitea%2fissue_template%2fFEATURE_TEMPLATE.yml" rel="noopener">
|
||||||
|
<NcButton variant="secondary">{{ trans('New request') }}</NcButton>
|
||||||
|
</a>
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableLabel
|
||||||
|
label="Something went wrong"
|
||||||
|
:top="true"
|
||||||
|
/>
|
||||||
|
<TableValue class="button-inline">
|
||||||
|
<a target="_blank" href="https://gitnet.fr/deblan/side_menu/issues/new?template=.gitea%2fissue_template%2fISSUE_TEMPLATE.yml" rel="noopener">
|
||||||
|
<NcButton variant="secondary">{{ trans('Report a bug') }}</NcButton>
|
||||||
|
</a>
|
||||||
|
<NcButton variant="secondary" @click="showConfig = true">{{ trans('Show the configuration') }}</NcButton>
|
||||||
|
|
||||||
|
<NcModal
|
||||||
|
v-if="showConfig"
|
||||||
|
@close="showConfig = false"
|
||||||
|
>
|
||||||
|
<div class="modal__content">
|
||||||
|
<p style="margin-bottom: 5px">{{ trans('Configuration:') }}</p>
|
||||||
|
<textarea class="config" readonly>{{ filterConfig(config) }}</textarea>
|
||||||
|
|
||||||
|
<div class="modal__footer">
|
||||||
|
<NcButton
|
||||||
|
@click="copyConfig"
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
|
<span v-if="configCopied">{{ trans('Done!') }}</span>
|
||||||
|
<span v-else>{{ trans('Copy') }}</span>
|
||||||
|
</NcButton>
|
||||||
|
<NcButton
|
||||||
|
@click="showConfig = false"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
{{ t('side_menu', 'Close') }}
|
||||||
|
</NcButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NcModal>
|
||||||
|
</TableValue>
|
||||||
|
</TableRow>
|
||||||
|
</TableContainer>
|
||||||
|
</NcAppContent>
|
||||||
|
</NcContent>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.wrapper {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
padding-top: calc(var(--default-grid-baseline) * 5);
|
|
||||||
padding-left: calc(var(--default-grid-baseline) * 7);
|
|
||||||
padding-right: calc(var(--default-grid-baseline) * 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker {
|
.button-inline button {
|
||||||
width: 100px;
|
display: inline-block;
|
||||||
height: 34px;
|
margin-right: 5px;
|
||||||
border-radius: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex {
|
.config {
|
||||||
display: flex;
|
width: 100%;
|
||||||
|
height: 30vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__footer {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__footer button {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { NcContent, NcAppContent, NcColorPicker, NcButton } from '@nextcloud/vue'
|
import { NcContent, NcAppContent, NcButton, NcModal } from '@nextcloud/vue'
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useConfigStore } from '../store/config.js'
|
||||||
|
|
||||||
import MenuDisplay from '../components/settings/MenuDisplay'
|
import MenuDisplay from '../components/settings/MenuDisplay'
|
||||||
import TableContainer from '../components/settings/TableContainer'
|
import TableContainer from '../components/settings/TableContainer'
|
||||||
import TableRow from '../components/settings/TableRow'
|
import TableRow from '../components/settings/TableRow'
|
||||||
import TableLabel from '../components/settings/TableLabel'
|
import TableLabel from '../components/settings/TableLabel'
|
||||||
import TableValue from '../components/settings/TableValue'
|
import TableValue from '../components/settings/TableValue'
|
||||||
import { ref, onMounted } from 'vue'
|
import SectionTitle from '../components/settings/SectionTitle'
|
||||||
|
import FormRange from '../components/settings/form/FormRange'
|
||||||
|
import FormColorPicker from '../components/settings/form/FormColorPicker'
|
||||||
|
import FormOpener from '../components/settings/form/FormOpener'
|
||||||
|
import FormSelect from '../components/settings/form/FormSelect'
|
||||||
|
import FormYesNo from '../components/settings/form/FormYesNo'
|
||||||
|
import FormSize from '../components/settings/form/FormSize'
|
||||||
|
import FormAppPicker from '../components/settings/form/FormAppPicker'
|
||||||
|
|
||||||
const menu = [
|
const menu = [
|
||||||
{label: 'Panel', section: 'panel'},
|
{ label: 'Global', section: 'global' },
|
||||||
{label: 'Colors', section: 'color'},
|
{ label: 'Panel', section: 'panel' },
|
||||||
|
{ label: 'Applications', section: 'apps' },
|
||||||
|
{ label: 'Colors', section: 'colors' },
|
||||||
|
{ label: 'Opener', section: 'opener' },
|
||||||
|
{ label: 'Top menu', section: 'topMenu' },
|
||||||
|
{ label: 'Support', section: 'support' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const section = ref('panel')
|
const config = ref(null)
|
||||||
const color = ref('#ff9')
|
const showConfig = ref(false)
|
||||||
|
const configCopied = ref(false)
|
||||||
|
const configStore = useConfigStore()
|
||||||
|
const section = ref(menu[0].section)
|
||||||
|
|
||||||
const setSection = (value) => {
|
const setSection = (value) => {
|
||||||
section.value = value
|
section.value = value
|
||||||
|
|
@ -101,4 +514,30 @@ const sectionClass = (value) => {
|
||||||
hidden: value !== section.value,
|
hidden: value !== section.value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const copyConfig = () => {
|
||||||
|
navigator.clipboard.writeText(JSON.stringify(filterConfig(config.value), null, 2))
|
||||||
|
|
||||||
|
configCopied.value = true
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
configCopied.value = false
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterConfig = (value) => {
|
||||||
|
const result = {}
|
||||||
|
|
||||||
|
for (let key in value) {
|
||||||
|
if (['cache-categories', 'cache'].includes(key) === false) {
|
||||||
|
result[key] = value[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
config.value = await configStore.getAppConfig()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@
|
||||||
.side-menu-setting-table {
|
.side-menu-setting-table {
|
||||||
display: table;
|
display: table;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-menu-setting-row {
|
.side-menu-setting-row {
|
||||||
|
|
@ -225,9 +225,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-menu-setting {
|
.side-menu-setting {
|
||||||
padding: 10px;
|
padding: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-menu-setting-color-picker {
|
.side-menu-setting-color-picker {
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.side-menu-big, &.side-menu-with-categories {
|
&.side-menu-big,
|
||||||
|
&.side-menu-with-categories {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +226,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-menu-big, .side-menu-with-categories {
|
.side-menu-big,
|
||||||
|
.side-menu-with-categories {
|
||||||
.side-menu-apps-list {
|
.side-menu-apps-list {
|
||||||
height: auto;
|
height: auto;
|
||||||
position: static;
|
position: static;
|
||||||
|
|
@ -284,7 +286,6 @@
|
||||||
stroke: var(--side-menu-text-color, #fff);
|
stroke: var(--side-menu-text-color, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.side-menu-always-displayed {
|
.side-menu-always-displayed {
|
||||||
body {
|
body {
|
||||||
width: calc(100% - 50px) !important;
|
width: calc(100% - 50px) !important;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { generateUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
export const useConfigStore = defineStore('config', () => {
|
export const useConfigStore = defineStore('config', () => {
|
||||||
const config = ref(null)
|
const config = ref(null)
|
||||||
|
const appConfig = ref(null)
|
||||||
|
|
||||||
async function getConfig() {
|
async function getConfig() {
|
||||||
if (config.value !== null) {
|
if (config.value !== null) {
|
||||||
|
|
@ -16,7 +17,18 @@ export const useConfigStore = defineStore('config', () => {
|
||||||
return config.value
|
return config.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getAppConfig() {
|
||||||
|
if (appConfig.value !== null) {
|
||||||
|
return appConfig.value
|
||||||
|
}
|
||||||
|
|
||||||
|
appConfig.value = await axios.get(generateUrl('/apps/side_menu/admin/config')).then((response) => response.data)
|
||||||
|
|
||||||
|
return appConfig.value
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getConfig,
|
getConfig,
|
||||||
|
getAppConfig,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue