diff --git a/AdminStyleRock.js b/AdminStyleRock.js new file mode 100644 index 0000000..c271f32 --- /dev/null +++ b/AdminStyleRock.js @@ -0,0 +1,36 @@ +// add darkmode toggle to navbar +(() => { + if (!ProcessWire.config.asrDarkmodeToggle) return; + document.addEventListener("DOMContentLoaded", function () { + const navbarRight = document.querySelector( + "#pw-masthead nav.uk-navbar .uk-navbar-right" + ); + if (!navbarRight) return; + navbarRight.insertAdjacentHTML( + "afterbegin", + '
" + ); + }); + + // handle clicks on darkmode toggle + document.addEventListener("click", function (e) { + let el = e.target.closest("[data-darkmode]"); + if (!el) return; + var darkModeValue = el.getAttribute("data-darkmode"); + localStorage.setItem("asr-darkmode", darkModeValue); + toggleDarkMode(); + }); + + function toggleDarkMode() { + const isDarkMode = localStorage.getItem("asr-darkmode") == "1"; + if (isDarkMode) { + document.documentElement.classList.add("asr-dark"); + } else { + document.documentElement.classList.remove("asr-dark"); + } + } + toggleDarkMode(); +})(); diff --git a/AdminStyleRock.module.php b/AdminStyleRock.module.php index 7c40b74..eeaa119 100644 --- a/AdminStyleRock.module.php +++ b/AdminStyleRock.module.php @@ -11,6 +11,11 @@ class AdminStyleRock extends WireData implements Module, ConfigurableModule { const prefix = "adminstylerock_"; + const primary = "#e83561"; + const primary_h = 345; + const primary_s = '80%'; + const primary_l = '56%'; + const saturation = '5%'; const field_adminlogo = self::prefix . "adminlogo"; @@ -38,15 +43,22 @@ public function ready() { // add alfred style overrides $this->addHookAfter('RockFrontend::addAlfredStyles', $this, 'addAlfredStyles'); + $this->addHookAfter('Page::render', $this, 'addCssVariables'); + if ($this->wire->user->isSuperuser()) $this->addKitchenSink(); // do everything below only on admin pages if ($this->wire->page->template != 'admin') return; + $this->wire->adminTheme->addBodyClass('AdminStyleRock'); + + // add AdminStyleRock JS to backend $config = $this->wire()->config; - $min = !$config->debug; + $config->scripts->add($config->urls($this) . "AdminStyleRock.js"); + $config->js('asrDarkmodeToggle', !$this->noDarkmodeToggle); - $style = $config->paths($this) . "styles/rock.less"; + $style = $config->paths($this) . "styles/_rock.less"; $compiled = $config->paths->assets . "admin"; + $min = !$config->debug; if ($min) $compiled .= ".min.css"; else $compiled .= ".css"; @@ -54,18 +66,29 @@ public function ready() $vars = []; if ($this->rockprimary) $vars = ['rock-primary' => $this->rockprimary]; + // check if a file was changed + $mCSS = @filemtime($compiled); + $mLESS = 0; + $files = $this->wire->files->find(__DIR__ . "/styles", [ + 'extensions' => 'less', + ]); + foreach ($files as $file) { + if ($mLESS > $mCSS) continue; + $mLESS = max($mLESS, filemtime($file)); + } + $config->AdminThemeUikit = [ 'style' => $style, 'compress' => $min, 'customCssFile' => $compiled, - 'recompile' => @(filemtime($style) > filemtime($compiled)), + 'recompile' => $mLESS > $mCSS, 'vars' => $vars, ]; // attach hook to set logo url - $this->addHookAfter('AdminThemeUikit::renderFile', $this, "renderFile"); - $this->addHookAfter("Pages::saved", $this, "addUploadedLogo"); - $this->addHookAfter("ProcessPageEdit::buildForm", $this, "hideLogofield"); + wire()->addHookAfter('AdminThemeUikit::renderFile', $this, "renderFile"); + wire()->addHookAfter("Pages::saved", $this, "addUploadedLogo"); + wire()->addHookAfter("ProcessPageEdit::buildForm", $this, "hideLogofield"); } public function addAlfredStyles(HookEvent $event) @@ -79,12 +102,159 @@ public function addAlfredStyles(HookEvent $event) $name = 'rockfrontend'; } - $rf->styles($name)->add(__DIR__ . "/styles/alfred.less"); + $rf->styles($name)->add(__DIR__ . "/styles/_alfred.less"); if ($this->rockprimary) { $rf->styles($name)->setVar('alfred-primary', $this->rockprimary); } } + protected function addCssVariables(HookEvent $event): void + { + /** @var Page $page */ + $page = $event->object; + if ($page->template != 'admin') return; + if ($this->wire->config->ajax) return; + if ($this->wire->config->external) return; + + // add styles from styles.php + $styles = $this->wire->files->render(__DIR__ . "/styles.php", [ + 'h' => $this->rockprimary_h === null ? self::primary_h : $this->rockprimary_h, + 's' => $this->rockprimary_s === null ? self::primary_s : $this->rockprimary_s, + 'l' => $this->rockprimary_l === null ? self::primary_l : $this->rockprimary_l, + 'saturation' => $this->saturation === null ? self::saturation : $this->saturation, + ]); + $event->return = str_replace( + "", + "$styles", + $event->return + ); + } + + protected function addKitchenSink(): void + { + if ($this->wire->input->get->name !== 'AdminStyleRock') return; + + if ($this->wire->input->debug) { + $this->message("Demo Alert Message"); + $this->warning("Demo Alert Warning"); + $this->error("Demo Alert Error"); + } + + $this->addHookAfter('InputfieldForm::render', function ($event) { + if ($event->object->id !== 'ModuleEditForm') return; + + $form = new InputfieldForm(); + + $fs = new InputfieldFieldset(); + $fs->name = "kitchensink"; + $fs->label = 'Kitchen Sink'; + $fs->icon = 'paint-brush'; + $fs->notes = "Kitchen Sink Demo Note"; + $fs->collapsed = $this->wire->input->debug + ? Inputfield::collapsedNo + : Inputfield::collapsedYesAjax; + $fs->description = 'This is a preview of several fields to help with custom development. + In RockFrontend you can enable livereload also for module pages like this, which is handy when working on the style to get live preview. + You can add &debug=1 to this page\'s url to open Kitchen Sink by default and to show demo alerts.'; + $fs->entityEncodeText = false; + $form->add($fs); + + $fs->add([ + 'type' => 'text', + 'label' => 'Demo Text Input', + 'notes' => 'Demo Note', + 'name' => 'demotext', + 'columnWidth' => 33, + ]); + $fs->add([ + 'type' => 'select', + 'label' => 'Demo Select', + 'options' => [ + 'option1' => 'Option 1', + 'option2' => 'Option 2', + 'option3' => 'Option 3', + ], + 'value' => 'option1', + 'name' => 'demoselect', + 'columnWidth' => 33, + ]); + $fs->add([ + 'type' => 'checkbox', + 'label' => 'Demo Checkbox', + 'checkboxLabel' => 'Demo Checkbox Label', + 'name' => 'democheckbox', + 'columnWidth' => 34, + ]); + $fs->add([ + 'type' => 'asmSelect', + 'label' => 'Demo ASM Select', + 'name' => 'demoasm', + 'options' => [ + 'option1' => 'ASM Option 1', + 'option2' => 'ASM Option 2', + 'option3' => 'ASM Option 3', + ], + 'value' => ['option1'], // Default selected value + 'description' => 'This is a demo ASM Select field.', + ]); + $fs->add([ + 'type' => 'textarea', + 'label' => 'Demo Textarea', + 'name' => 'demotextarea', + 'columnWidth' => 33, + ]); + $fs->add([ + 'type' => 'radios', + 'label' => 'Demo Radios', + 'name' => 'demoradios', + 'options' => [ + 'option1' => 'Option 1', + 'option2' => 'Option 2', + 'option3' => 'Option 3', + ], + 'columnWidth' => 33, + ]); + $fs->add([ + 'type' => 'toggle', + 'label' => 'Demo Toggle', + 'value' => 'yes', + 'name' => 'demotoggle', + 'columnWidth' => 34, + ]); + $this->wire->modules->get('JqueryUI')->use('vex'); + $fs->add([ + 'type' => 'markup', + 'label' => 'VEX Demo', + 'value' => ' + + VEX Demo + + ', + 'columnWidth' => 40, + ]); + $fs->add([ + 'type' => 'markup', + 'label' => 'UIkit Notifications', + 'value' => ' + + + + + ', + 'columnWidth' => 60, + ]); + + $event->return = $form->render() . $event->return; + }); + } + public function addUploadedLogo(HookEvent $event): void { /** @var Page $page */ @@ -165,11 +335,6 @@ public function setLogoUrl($url, $m = 'AdminThemeUikit') $modules->saveConfig($m, $data); } - public function ___install() - { - $this->setLogoUrl($this->wire->config->urls($this) . "baumrock.svg"); - } - /** * Config inputfields * @param InputfieldWrapper $inputfields @@ -178,16 +343,42 @@ public function getModuleConfigInputfields($inputfields) { $this->iframe($inputfields); - // add main color + $inputfields->add([ + 'type' => 'markup', + 'label' => 'Color Palette', + 'icon' => 'paint-brush', + 'value' => $this->wire->files->render(__DIR__ . "/colorpicker.php", [ + 'rockprimary' => $this->rockprimary ?: self::primary, + 'saturation' => substr($this->saturation ?: self::saturation, 0, -1), + ]), + ]); + $inputfields->add([ 'type' => 'text', 'name' => 'rockprimary', - 'icon' => 'paint-brush', - 'notes' => 'eg #00ff00 or rgba(0,0,0,1)', - 'value' => $this->rockprimary, - 'label' => 'Primary Color', - 'description' => 'This color is used as @rock-primary of the rock.less style' - . ' and as @alfred-primary for the alfred.less frontend editing style', + 'value' => $this->rockprimary ?: self::primary, + 'columnWidth' => 50, + ]); + $inputfields->add([ + 'type' => 'text', + 'name' => 'saturation', + 'value' => $this->saturation ?: self::saturation, + 'columnWidth' => 50, + ]); + $inputfields->add([ + 'type' => 'hidden', + 'name' => 'rockprimary_h', + 'value' => $this->rockprimary_h, + ]); + $inputfields->add([ + 'type' => 'hidden', + 'name' => 'rockprimary_s', + 'value' => $this->rockprimary_s, + ]); + $inputfields->add([ + 'type' => 'hidden', + 'name' => 'rockprimary_l', + 'value' => $this->rockprimary_l, ]); /** @var RockMigrations $rm */ @@ -210,6 +401,14 @@ public function getModuleConfigInputfields($inputfields) } $inputfields->add($f); + $inputfields->add([ + 'type' => 'checkbox', + 'name' => 'noDarkmodeToggle', + 'icon' => 'moon-o', + 'checked' => $this->noDarkmodeToggle ? 'checked' : '', + 'label' => 'Don\'t add darkmode-toggle to backend navbar', + ]); + return $inputfields; } @@ -244,4 +443,14 @@ public function iframe($inputfields) $this->wire->session->asriframe = $iframe; } } + + public function ___uninstall() + { + $modules = $this->wire('modules'); + $adminThemeUikit = $modules->get('AdminThemeUikit'); + if ($adminThemeUikit) { + $adminThemeUikit->logoURL = ''; + $modules->saveConfig('AdminThemeUikit', 'logoURL', ''); + } + } } diff --git a/colorpicker.php b/colorpicker.php new file mode 100644 index 0000000..085294a --- /dev/null +++ b/colorpicker.php @@ -0,0 +1,160 @@ + +