Skip to content

Commit

Permalink
disclosures
Browse files Browse the repository at this point in the history
  • Loading branch information
markhuot committed Nov 29, 2023
1 parent 350fdb0 commit 16408fd
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 78 deletions.
6 changes: 3 additions & 3 deletions src/assetbundles/KeystoneAssetBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public function init()
$this->depends = [];

$this->js = [
'components/create.js',
'components/edit.js',
'components/drag.js',
'lib/alpine.min.js',
'lib/axios.min.js',
'components/alpine.js',
'components/post.js',
'components/slideout.js',
'components/edit.js',
];

$this->css = [
Expand Down
5 changes: 4 additions & 1 deletion src/base/ComponentType.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ public function render(array $variables = []): string

abstract public function getTemplatePath(): string;

public function getSlotDefinitions()
/**
* @return Collection<SlotDefinition>
*/
public function getSlotDefinitions(): Collection
{
return $this->getSchema()[1];
}
Expand Down
4 changes: 3 additions & 1 deletion src/controllers/ComponentsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ public function actionUpdate()

(new EditComponentData)->handle($component, $fields);

return $this->asSuccess('Component saved');
return $this->asSuccess('Component saved', [
'fieldHtml' => $component->getElement()->getFieldHtml($component->getField()),
]);
}

public function actionDelete()
Expand Down
16 changes: 15 additions & 1 deletion src/models/Component.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,13 @@ public function getData(): ActiveQuery

public function getDisclosure(): ActiveQuery
{
return $this->hasOne(ComponentDisclosure::class, ['componentId' => 'id']);
$query = $this->hasOne(ComponentDisclosure::class, ['componentId' => 'id']);

if (app()->getUser()->getIdentity()) {
$query->where(['userId' => app()->getUser()->getIdentity()->id]);
}

return $query;
}

public function withDisclosures(bool $withDisclosures=true): self
Expand All @@ -87,6 +93,14 @@ public function withDisclosures(bool $withDisclosures=true): self
return $this;
}

public function isCollapsed(): bool
{
$shouldBeClosed = $this->getType()->getSlotDefinitions()->every(fn ($defn) => $defn->isCollapsed());

return ($shouldBeClosed && $this->disclosure->state !== 'open') ||
$this->disclosure->state == 'closed';
}

/**
* @return array<string>
*/
Expand Down
67 changes: 0 additions & 67 deletions src/resources/components/create.js

This file was deleted.

File renamed without changes.
45 changes: 45 additions & 0 deletions src/resources/components/slideout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
window.slideout = function (action, params={}) {
const thens = [];

async function doSlideout(event) {
event.preventDefault();

let form = event.target.closest('form');
let editor = $.data(form, 'elementEditor');

// There might not be an editor if we're in live preview so we need to look around
// in the DOM for the real editor behind the scenes.
if (! editor && form.classList.contains('lp-editor')) {
editor = $.data(document.getElementById('main-form'), 'elementEditor');
}

await editor.ensureIsDraftOrRevision();

params.elementId = editor.settings.elementId;
const slideout = new Craft.CpScreenSlideout(action, {params});

slideout.on('submit', event => {
for (const then of thens) {
then(event.response, form);
}
});
}

doSlideout.then = function (callback) {
thens.push(callback);

return doSlideout;
}

doSlideout.swap = function (selector) {
thens.push((response, form) => {
const fragment = document.createElement('template');
fragment.innerHTML = response.data.fieldHtml;
form.querySelector(selector).replaceWith(fragment.content.querySelector(selector));
});

return doSlideout;
}

return doSlideout;
}
8 changes: 4 additions & 4 deletions src/templates/field.twig
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{% endif %}
<ul style="margin-left: 2rem;">
{% for child in component.getSlot(slot.name) %}
<li class="k-relative {{ child.getType().getSlotDefinitions().first().isCollapsed()|default(false) and child.disclosure.state != 'open' ? 'k-collapsed' }}"
<li class="k-relative {{ child.isCollapsed() ? 'k-collapsed' }}"
draggable="true"
data-draggable="{{ child.id }}"
data-draggable-type="{{ child.getType().getHandle() }}"
Expand All @@ -31,7 +31,7 @@
<div class="k-flex k-justify-between" data-draggable-row>
<div class="k-relative">
<div class="foo k-hidden k-pointer-events-none" style="height:10px; width: 2rem; position: absolute; top: 0.4em; left: -20px; border-radius: 0 0 0 10px; border-width: 0 0 1px 1px; border-color: rgba(96,125,159,.25); box-shadow: -2px 2px 0 white;"></div>
<a class="k-text-link" href="{{ cpUrl('keystone/components/edit', child.getQueryCondition()) }}" data-open-keystone-component-editor='{{ child.getQueryCondition()|json_encode }}'>
<a class="k-text-link" href="#" @click="slideout('{{ cpUrl('keystone/components/edit', child.getQueryCondition()) }}').swap('[data-field-id=&quot;{{ field.id }}&quot;]')">
<div style="background: white; display: inline-block; position: relative; padding: 4px;">{{ child.getType().getIcon({class: 'k-w-4 k-inline'})|raw }}</div>
{{ child.getType().getName() }}
<span class="k-text-gray-400">{{ child.getSummary() }}</span>
Expand All @@ -52,13 +52,13 @@
>
<div data-draggable-row class="k-relative">
<div class="foo k-hidden k-pointer-events-none" style="height:10px; width: 2rem; position: absolute; top: 0.4em; left: -20px; border-radius: 0 0 0 10px; border-width: 0 0 1px 1px; border-color: rgba(96,125,159,.25); box-shadow: -2px 2px 0 white;"></div>
<button data-open-keystone-component-selector='{{ {
<button @click="slideout('keystone/components/add', {{ {
elementId: element.id,
fieldId: field.id,
path: component.getChildPath(),
slot: slot.name,
sortOrder: lastOrder
}|json_encode }}' class="k-text-gray-400" style="background: white;">
}|json_encode }}).swap('[data-field-id=&quot;{{ field.id }}&quot;]')" class="k-text-gray-400" style="background: white;">
<div style="background: white; display: inline-block; position: relative; padding: 4px;">
<svg class="k-w-4 k-inline k-stroke-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path d="M128,26A102,102,0,1,0,230,128,102.12,102.12,0,0,0,128,26Zm0,192a90,90,0,1,1,90-90A90.1,90.1,0,0,1,128,218Zm46-90a6,6,0,0,1-6,6H134v34a6,6,0,0,1-12,0V134H88a6,6,0,0,1,0-12h34V88a6,6,0,0,1,12,0v34h34A6,6,0,0,1,174,128Z"></path></svg>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/templates/select.twig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<ul class="k-grid k-grid-cols-3 k-gap-8">
{% for type in types %}
<li data-keystone-select-type-container>
<label data-keystone-select-type class="k-block [&:has(:checked)]:k-bg-blue-500 [&:has(:checked)]:k-text-white [&:has(:checked)]:k-border-blue-900 [&:has(:disabled)]:k-opacity-25 k-border k-border-gray-400 k-rounded-lg k-p-4">
<label class="k-block [&:has(:checked)]:k-bg-blue-500 [&:has(:checked)]:k-text-white [&:has(:checked)]:k-border-blue-900 [&:has(:disabled)]:k-opacity-25 k-border k-border-gray-400 k-rounded-lg k-p-4" @dblclick="$($el.closest('form')).data('cpScreen').submit()">
<input type="radio" name="type" value="{{ type.getHandle() }}" class="k-hidden" {{ not (parent.getType().getSlotDefinition(slot).allows(type.getHandle()) ?? true) ? 'disabled' }}>
{{ type.getIcon({class: 'k-w-8 k-text-current k-stroke-current'}) }}
{{ type.getName() }}
Expand Down
49 changes: 49 additions & 0 deletions tests/DisclosureTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

use markhuot\craftpest\factories\User;
use markhuot\keystone\models\Component;

it('gets default open disclosure state', function () {
$component = Component::factory()->type('keystone/section')->create();

expect($component->isCollapsed())->toBeFalse();
});

it('overrides default open disclosure state', function () {
$component = Component::factory()->type('keystone/section')->create();
$component->disclosure->state = 'closed';

expect($component->isCollapsed())->toBeTrue();
});

it('gets default closed disclosure state', function () {
$component = Component::factory()->type('keystone/entry')->create();

expect($component->isCollapsed())->toBeTrue();
});

it('overrides default closed disclosure state', function () {
$component = Component::factory()->type('keystone/entry')->create();
$component->disclosure->state = 'open';

expect($component->isCollapsed())->toBeFalse();
});

it('respects user preferences by storing unique disclosures per user', function () {
[$user1, $user2] = User::factory()->count(2)->create();
$component = Component::factory()->type('keystone/entry')->create();

$this->actingAs($user1);
$component1 = Component::findOne($component->getQueryCondition());
$component1->disclosure->state = 'open';
$component1->disclosure->save();

$this->actingAs($user2);
$component2 = Component::findOne($component->getQueryCondition());
$component2->disclosure->state = 'closed';
$component2->disclosure->save();

expect($component1->getQueryCondition())->toEqualCanonicalizing($component2->getQueryCondition());
expect($component1->disclosure->id)->not->toEqual($component2->disclosure->id);
expect($component1->isCollapsed())->not->toBe($component2->isCollapsed());
});

0 comments on commit 16408fd

Please sign in to comment.