Skip to content

Commit

Permalink
Add lens support (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
milewski authored Oct 8, 2023
1 parent 108840d commit 0c0ef22
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 144 deletions.
2 changes: 1 addition & 1 deletion dist/js/tool.js

Large diffs are not rendered by default.

50 changes: 36 additions & 14 deletions resources/js/components/ColumnToggler.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import isEqual from 'lodash/isEqual'
import cloneDeep from 'lodash/cloneDeep'

export function toList(state) {
return Object.keys(state).filter(key => state[ key ].visible)
}

export function encode(state) {
return btoa(unescape(encodeURIComponent(JSON.stringify(state))));
return btoa(unescape(encodeURIComponent(JSON.stringify(state))))
}

export function decode(state) {
return state ? JSON.parse(decodeURIComponent(escape(atob(state)))) : null
}

export function getStateFromLocalStorage(cacheKey) {
Expand All @@ -26,35 +33,50 @@ export function generateCacheKey(cacheKey) {
return `column-toggler/${ cacheKey }/${ Nova.$router.page.component }`
}

export function decode(state) {
return state ? JSON.parse(decodeURIComponent(escape(atob(state)))) : null;
}

export function registerMixin(component) {

if (component.computed.resourceRequestQueryString === undefined) {
component = component.mixins.find(({ computed }) => typeof computed.resourceRequestQueryString === 'function')
}

const originalResourceRequestQueryString = component.computed.resourceRequestQueryString

component.mixins.push({
mounted() {
Nova.$on(`column-toggler:state-changed:${ this.resourceName }`, this.onColumTogglerStateChange)
created() {
Nova.$on(this.columnTogglerEventKey, this.columnTogglerOnStateChange)
},
unmounted() {
Nova.$off(`column-toggler:state-changed:${ this.resourceName }`, this.onColumTogglerStateChange)
Nova.$off(this.columnTogglerEventKey, this.columnTogglerOnStateChange)
},
data() {
return {
columnTogglerState: getStateFromLocalStorage(this.resourceName),
}
},
methods: {
onColumTogglerStateChange(state) {
saveStateToLocalStorage(state, this.resourceName)
this.columnTogglerState = state
this.getResources()
columnTogglerOnStateChange(state) {

const currentState = this.columnTogglerState

if (isEqual(state, currentState) === false) {

saveStateToLocalStorage(state, this.resourceName)

this.columnTogglerState = cloneDeep(state)

if (currentState !== true) {
this.getResources()
}

}

},
},
computed: {
encodedColumnTogglerColumns() {
columnTogglerEventKey() {
return `column-toggler:state-changed:${ [ this.resourceName, this.lens ].filter(Boolean).join('/') }`
},
columnTogglerEncodedColumns() {

if (typeof this.columnTogglerState === 'boolean') {
return this.columnTogglerState
Expand All @@ -75,7 +97,7 @@ export function registerMixin(component) {
component.computed.resourceRequestQueryString = function () {
return {
...originalResourceRequestQueryString.call(this),
columnToggler: this.encodedColumnTogglerColumns,
columnToggler: this.columnTogglerEncodedColumns,
}
}

Expand Down
143 changes: 69 additions & 74 deletions resources/js/components/ColumnToggler.vue
Original file line number Diff line number Diff line change
@@ -1,78 +1,83 @@
<template>

<Dropdown
:handle-internal-clicks="false"
class="flex h-9 hover:bg-gray-100 dark:hover:bg-gray-700 rounded"
:class="{
'bg-primary-500 hover:bg-primary-600 border-primary-500': isDirty,
'dark:bg-primary-500 dark:hover:bg-primary-600 dark:border-primary-500': isDirty,
}">
<FadeTransition>

<span class="sr-only">{{ __('Columns Dropdown') }}</span>
<Dropdown
v-if="Object.keys(state).length > 0 && tableToolbar.resources.length > 0"
:handle-internal-clicks="false"
class="flex h-9 hover:bg-gray-100 dark:hover:bg-gray-700 rounded"
:class="{
'bg-primary-500 hover:bg-primary-600 border-primary-500': isDirty,
'dark:bg-primary-500 dark:hover:bg-primary-600 dark:border-primary-500': isDirty,
}">

<DropdownTrigger
class="toolbar-button px-2"
:class="{ 'text-white hover:text-white dark:text-gray-800 dark:hover:text-gray-800': isDirty }">
<span class="sr-only">{{ __('Columns Dropdown') }}</span>

<Icon type="adjustments"/>
<DropdownTrigger
class="toolbar-button px-2"
:class="{ 'text-white hover:text-white dark:text-gray-800 dark:hover:text-gray-800': isDirty }">

<span v-if="isDirty"
:class="{ 'text-white dark:text-gray-800': isDirty }"
class="ml-2 font-bold">
<Icon type="adjustments"/>

{{ dirtyCount }}
<span v-if="isDirty"
:class="{ 'text-white dark:text-gray-800': isDirty }"
class="ml-2 font-bold">

</span>
{{ dirtyCount }}

</DropdownTrigger>
</span>

<template #menu>
</DropdownTrigger>

<DropdownMenu width="260">
<template #menu>

<div class="bg-white dark:bg-gray-900">
<DropdownMenu width="260">

<div ref="theForm" class="divide-y divide-gray-200 dark:divide-gray-800 divide-solid">
<div class="bg-white dark:bg-gray-900">

<div v-if="isDirty" class="bg-gray-100">
<div ref="theForm" class="divide-y divide-gray-200 dark:divide-gray-800 divide-solid">

<button
class="py-2 w-full block text-xs uppercase tracking-wide text-center text-gray-500 dark:bg-gray-800 dark:hover:bg-gray-700 font-bold focus:outline-none"
@click="handleRestoreDefaultClick">
<div v-if="isDirty" class="bg-gray-100">

{{ __('Restore Default') }}
<button
class="py-2 w-full block text-xs uppercase tracking-wide text-center text-gray-500 dark:bg-gray-800 dark:hover:bg-gray-700 font-bold focus:outline-none"
@click="handleRestoreDefaultClick">

</button>
{{ __('Restore Default') }}

</div>
</button>

</div>

<h3 class="p-3 px-4 text-xs uppercase font-bold tracking-wide flex justify-between items-center">
{{ __('Toggle Columns') }}
</h3>
<h3 class="p-3 px-4 text-xs uppercase font-bold tracking-wide flex justify-between items-center">
{{ __('Toggle Columns') }}
</h3>

<div class="flex flex-wrap p-4 space-y-1">
<div class="flex flex-wrap p-4 space-y-1">

<CheckboxWithLabel
class="w-full leading-none whitespace-nowrap mr-4"
v-for="({ label, visible }, attribute) in state"
:checked="visible"
@input="updateCheckedState(attribute, $event.target.checked)">
<CheckboxWithLabel
class="w-full leading-none whitespace-nowrap mr-4"
v-for="({ label, visible }, attribute) in state"
:checked="visible"
@input="updateCheckedState(attribute, $event.target.checked)">

<span>{{ label }}</span>
<span>{{ label }}</span>

</CheckboxWithLabel>
</CheckboxWithLabel>

</div>

</div>

</div>

</div>
</DropdownMenu>

</DropdownMenu>
</template>

</template>
</Dropdown>

</Dropdown>
</FadeTransition>

</template>

Expand All @@ -83,38 +88,13 @@
export default {
name: 'ColumnToggler',
props: [ 'resourceName' ],
props: [ 'tableToolbar' ],
data() {
return {
extraParams: {},
state: {},
originalState: {},
}
},
mounted() {
Nova.$on('custom-relationship-field:extra-params', this.appendToRequestCallback)
Nova.$emit('custom-relationship-field:request-extra-params')
const queryString = new URLSearchParams(this.extraParams)
Nova.request().get(`/nova-vendor/column-toggler/fields/${ this.resourceName }?${ queryString }`).then(({ data }) => {
let localStorageState = getStateFromLocalStorage(this.resourceName)
if (localStorageState && this.isDifferentState(data.attributes, localStorageState)) {
localStorageState = data.attributes
}
this.originalState = cloneDeep(data.attributes)
this.state = localStorageState ?? data.attributes
})
},
unmounted() {
Nova.$off('custom-relationship-field:extra-params', this.appendToRequestCallback)
},
computed: {
dirtyCount() {
Expand All @@ -138,10 +118,28 @@
},
},
watch: {
'tableToolbar.resources'(resources) {
const attributes = resources[ 0 ]?.columnToggler
if (attributes) {
let localStorageState = getStateFromLocalStorage(this.tableToolbar.resourceName)
if (localStorageState && this.isDifferentState(attributes, localStorageState)) {
localStorageState = attributes
}
this.originalState = cloneDeep(attributes)
this.state = localStorageState ?? attributes
}
},
state: {
deep: true,
handler(state) {
Nova.$emit(`column-toggler:state-changed:${ this.resourceName }`, state)
Nova.$emit(`column-toggler:state-changed:${ [ this.tableToolbar.resourceName, this.tableToolbar.lens ].filter(Boolean).join('/') }`, state)
},
},
},
Expand All @@ -164,9 +162,6 @@
updateCheckedState(attribute, checked) {
this.state[ attribute ].visible = checked
},
appendToRequestCallback(params) {
this.extraParams = { ...this.extraParams, ...params }
},
},
}
Expand Down
15 changes: 13 additions & 2 deletions resources/js/tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import ColumnToggler from './components/ColumnToggler.vue'
import { registerMixin } from './components/ColumnToggler'
import { createVNode, render } from 'vue'

try {

registerMixin(Nova.pages[ 'Nova.Lens' ].components[ 'ResourceLens' ])

} catch {

console.error('ColumnToggler: Unable to register mixin for Lens view')

}

Nova.booting(app => {

const componentFn = app.component
Expand All @@ -22,17 +32,18 @@ Nova.booting(app => {
if (this._.type?.__file?.endsWith('ResourceTableToolbar.vue')) {

const toolbarTarget = '[dusk$="-index-component"] div.h-9.ml-auto.flex.items-center.pr-2.md\\:pr-3 > div.hidden.md\\:flex.px-2'
const lensToolbarTarget = '[dusk$="-lens-component"] div.h-9.ml-auto.flex.items-center.pr-2.md\\:pr-3 > div.hidden.md\\:flex.px-2'

const container = document.createElement('div')
container.id = 'column-toggler'

const element = this._.vnode.el.querySelector(toolbarTarget)
const element = this._.vnode.el.querySelector(this.isLensView ? lensToolbarTarget : toolbarTarget)

if (element) {

element.insertAdjacentElement('afterend', container)

const vnode = createVNode(ColumnToggler, { resourceName: this._.props.resourceName })
const vnode = createVNode(ColumnToggler, { tableToolbar: this })
vnode.appContext = app._context

render(vnode, container)
Expand Down
8 changes: 0 additions & 8 deletions routes/api.php

This file was deleted.

16 changes: 0 additions & 16 deletions src/ColumnTogglerServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace DigitalCreative\ColumnToggler;

use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Fields\Field;
Expand All @@ -14,10 +13,6 @@ class ColumnTogglerServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->app->booted(function (): void {
$this->routes();
});

Field::macro('hideByDefault', function () {
return $this->withMeta([ 'columnToggleVisible' => false ]);
});
Expand All @@ -26,15 +21,4 @@ public function boot(): void
Nova::script('column-toggler', __DIR__ . '/../dist/js/tool.js');
});
}

protected function routes(): void
{
if ($this->app->routesAreCached()) {
return;
}

Route::middleware([ 'nova' ])
->prefix('nova-vendor/column-toggler')
->group(__DIR__ . '/../routes/api.php');
}
}
Loading

0 comments on commit 0c0ef22

Please sign in to comment.