Skip to content

Commit

Permalink
New overhauled loader implementation (#101)
Browse files Browse the repository at this point in the history
* Added value- & nested based syncInput notification label feature

* Made the loader more composable

* Making labels visible again

* Concept to dispatch event on elements instead of window

* Label splitting + translation is now being done inside the Loader Hydrator

* Loader layout and binding restructure

* Notification Messenger implementation

* Notification messenger design completion

* Notification messenger logic improvements

* Message loading status busy / done icons

* Notification System Configurations

* StyleCI fixes

* System config field comment improvement

* Message failure implementation

* Loader wildcard feature

* AlpineJS v2 compatibility (thanks @Vinai)

* Loader plugin documentation

* StyleCI fix

---------

Co-authored-by: Willem Poortman <[email protected]>
  • Loading branch information
wpoortman and Willem Poortman authored Jun 1, 2023
1 parent 14d5b80 commit e75a738
Show file tree
Hide file tree
Showing 19 changed files with 764 additions and 332 deletions.
44 changes: 29 additions & 15 deletions docs/Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@
- [Indicator Customization](#indicator-customization)
- [Indicator Removal](#indicator-removal)
- [Custom Example](#custom-example-dls)
- [Notification Type Styling](#notification-type-styling)
- [Plugins](#plugins)
- [Plugin: Loader](#plugin--loader)
- [Loader System Settings](#loader--system-settings)
- [Plugin: Error](#plugin--error)
- [Custom onError callback](#custom-onerror-callback)
- [Component Resolvers](#component-resolvers)
Expand Down Expand Up @@ -847,16 +849,20 @@ class Explanation extends \Magewirephp\Magewire\Component
**File**: html/magewire/loader.phtml

### Available Loader Window Events
```magewire:loader:start```
**magewire:loader:start**
> Dispatched as soon as the first ```$loader``` driven component is active.
```magewire:loader:tick```
**Test**: ```Magewire.dispatchEvent('loader:start', { detail: { title: 'Hello world', type: 'syncInput', use: true, start: Date.now(), failed: false }})```

**magewire:loader:tick**
> Dispatched when another component has ```$loader``` settings.
```magewire:loader:fail```
**Test**: ```Magewire.dispatchEvent('loader:tick', { detail: { title: 'Hello world', type: 'syncInput', use: true, start: Date.now(), failed: false }})```

**magewire:loader:fail**
> Dispatched when a ```$loader``` driven component failed for whatever reason.
```magewire:loader:stop```
**magewire:loader:stop**
> Dispatched as soon as all network requests were completed (this includes emitted requests).
### Indicator Removal
Expand Down Expand Up @@ -894,16 +900,14 @@ public function start(int $seconds)
```
> **Note**: This is just an example. For a disabled state you should or could use the ```wire:loading``` directive.
## Plugins
> **Important**: This is still a proof of concept. It's possible this won't make it into the first official release.
### Notification Type Styling
Each notification item can be one of three types (syncInput, fireEvent, callAction). By default, a notification item
has the ```magewire-notification``` class. A related (kebab-cased) subclass will be bind dynamically based on the
notification type.

It's a best practice to add your custom additions to Magewire inside the designated ```magewire.plugin``` container.
This can come in handy when you need to check if a plugin gives any trouble after installation to just temporary remove
it.

```xml
<referenceContainer name="magewire.plugin" remove="true"/>
```
- ```magewire-notification fire-event```
- ```magewire-notification sync-input```
- ```magewire-notification call-method```

### Plugin: Loader
```xml
Expand All @@ -914,8 +918,18 @@ The Loader plugin is closely related to the ```$loader``` property within a comp
either access the system configuration at **Store > Settings > Advanced > Developer > Magewire** or remove the block
through layout XML.

The loader is divided into several child blocks, giving you greater flexibility in customizing the appearance of both
the spinner and notifications without having to overwrite all functionality.
#### Loader System Settings
> **Note**: All Magewire specific settings by default can be found at Store > Settings > Advanced > Developer > Magewire.

- **Loader / Show:** Show or hide the global loading spinner.
- **Loader / Enable Notifications:** Show or hide optional notification messages.
- **Loader / Notifications / Message Fadeout Timeout:** Determine the duration for the message to fade out after its
target component has fully loaded.

The loader is divided into several elements, giving you greater flexibility in customizing the appearance of both
the global spinner and notifications, without having to overwrite everything.

All elements can be found in the Magewire core layout `default_hyva.xml` or be found in `Magewirephp_Magewire::html/loader`.

### Plugin: Error
```xml
Expand Down
16 changes: 9 additions & 7 deletions src/Model/Hydrator/Loader.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?php declare(strict_types=1);
<?php
/**
* Copyright © Willem Poortman 2021-present. All rights reserved.
*
* Please read the README and LICENSE files for more
* details on copyrights and license information.
*/

declare(strict_types=1);

namespace Magewirephp\Magewire\Model\Hydrator;

use Magewirephp\Magewire\Component;
use Magewirephp\Magewire\Helper\Functions as FunctionsHelper;
use Magewirephp\Magewire\Model\Action\CallMethod;
use Magewirephp\Magewire\Model\Action\SyncInput;
use Magewirephp\Magewire\Model\HydratorInterface;
use Magewirephp\Magewire\Model\RequestInterface;
use Magewirephp\Magewire\Model\ResponseInterface;
Expand All @@ -38,15 +38,17 @@ public function dehydrate(Component $component, ResponseInterface $response): vo
if ($loader) {
if (is_array($loader)) {
$loader = $this->functionsHelper->mapWithKeys(function ($value, $key) {
if (is_string($key) === false && is_string($value)) {
return [$value => true];
}
if (is_string($value)) {
$value = __($value);
$value = [$value];
}
if (is_array($value)) {
$value = array_map('__', array_filter($value, 'is_string'));
}

return [$key => $value];
}, $loader);
} elseif (is_string($loader)) {
$loader = __($loader);
}

$response->effects['loader'] = $loader;
Expand Down
81 changes: 81 additions & 0 deletions src/Model/Magento/System/ConfigMagewire.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php declare(strict_types=1);
/**
* Copyright © Willem Poortman 2021-present. All rights reserved.
*
* Please read the README and LICENSE files for more
* details on copyrights and license information.
*/

namespace Magewirephp\Magewire\Model\Magento\System;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Store\Model\ScopeInterface;

class ConfigMagewire
{
public const GROUP_LOADER = 'loader';
public const GROUP_NOTIFICATIONS = 'loader/notifications';

protected ScopeConfigInterface $scopeConfig;
protected Json $serializer;

public function __construct(
ScopeConfigInterface $scopeConfig,
Json $serializer
) {
$this->scopeConfig = $scopeConfig;
$this->serializer = $serializer;
}

public function canShowLoaderOverlay(): bool
{
return $this->isGroupFlag('enable', self::GROUP_LOADER) ?? true;
}

public function canShowLoaderNotificationMessages(): bool
{
return $this->isGroupFlag('enable', self::GROUP_NOTIFICATIONS) ?? true;
}

public function getNotificationMessageFadeoutTimeout(): int
{
return (int) $this->getGroupValue('message_fadeout_timeout', self::GROUP_NOTIFICATIONS) ?? 2500;
}

public function pageRequiresLoaderPluginScript(): bool
{
return $this->canShowLoaderOverlay() || $this->canShowLoaderNotificationMessages();
}

/**
* Retrieve grouped config value by path and scope.
*
* @return mixed
*/
public function getGroupValue(
string $path,
string $group = null,
string $scopeType = ScopeInterface::SCOPE_STORE,
$scopeCode = null
) {
return $this->scopeConfig->getValue($this->createPath($path, $group), $scopeType, $scopeCode);
}

/**
* Retrieve grouped config flag by path and scope.
*/
public function isGroupFlag(
string $path,
string $group = null,
string $scopeType = ScopeInterface::SCOPE_STORE,
$scopeCode = null
): bool {
return $this->scopeConfig->isSetFlag($this->createPath($path, $group), $scopeType, $scopeCode);
}

protected function createPath(string $path, string $group = null)
{
return sprintf('dev/%s/%s', $group ? 'magewire/' . $group : 'magewire', trim($path));
}
}
10 changes: 8 additions & 2 deletions src/ViewModel/Magewire.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Magento\Store\Model\StoreManagerInterface;
use Magewirephp\Magewire\Model\ComponentFactory;
use Magewirephp\Magewire\Model\LayoutRenderLifecycle;
use Magewirephp\Magewire\Model\Magento\System\ConfigMagewire as MagewireSystemConfig;

/**
* @api
Expand All @@ -38,14 +39,14 @@ public function __construct(
ProductMetadataInterface $productMetadata,
StoreManagerInterface $storeManager,
LayoutRenderLifecycle $layoutRenderLifecycle,
ComponentFactory $componentFactory
MagewireSystemConfig $magewireSystemConfig
) {
$this->formKey = $formKey;
$this->applicationState = $applicationState;
$this->productMetaData = $productMetadata;
$this->storeManager = $storeManager;
$this->layoutRenderLifecycle = $layoutRenderLifecycle;
$this->componentFactory = $componentFactory;
$this->magewireSystemConfig = $magewireSystemConfig;
}

public function isDeveloperMode(): bool
Expand Down Expand Up @@ -84,4 +85,9 @@ public function pageRequiresMagewire(): bool
{
return $this->layoutRenderLifecycle->hasHistory();
}

public function getSystemConfig(): MagewireSystemConfig
{
return $this->magewireSystemConfig;
}
}
31 changes: 29 additions & 2 deletions src/etc/adminhtml/system.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
showInDefault="1"
canRestore="1"
>
<label>Enable</label>
<label>Show</label>
<comment>Show a loading screen overlay while a component is busy loading.</comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>

Expand All @@ -41,12 +42,38 @@
>
<label>Enable Notifications</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<comment>Display additional component notifications if available.</comment>
<comment>Display notification component messages when set.</comment>

<depends>
<field id="dev/magewire/loader/enable">1</field>
</depends>
</field>

<group id="notifications"
translate="label"
sortOrder="10"
showInDefault="1"
showInWebsite="1"
showInStore="1"
>
<label>Notifications</label>

<depends>
<field id="dev/magewire/loader/enable_notifications">1</field>
</depends>

<field id="message_fadeout_timeout"
translate="label comment"
type="text"
sortOrder="10"
showInDefault="1"
canRestore="1"
>
<label>Message Fadeout Timeout</label>
<comment>Determine the duration, in milliseconds, for the message to fade out after its target component has fully loaded.</comment>
<validate>required-entry validate-digits validate-not-negative-number</validate>
</field>
</group>
</group>
</group>
</section>
Expand Down
4 changes: 4 additions & 0 deletions src/etc/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
<loader>
<enable>1</enable>
<enable_notifications>1</enable_notifications>

<notifications>
<message_fadeout_timeout>2500</message_fadeout_timeout>
</notifications>
</loader>
</magewire>
</dev>
Expand Down
63 changes: 58 additions & 5 deletions src/view/frontend/layout/default_hyva.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,65 @@
>
<body>
<referenceContainer name="page.bottom.container">
<!-- Floating Magewire element(s) which can be everywhere -->
<block name="magewire.loader"
ifconfig="dev/magewire/loader/enable"
template="Magewirephp_Magewire::html/loader.phtml">
template="Magewirephp_Magewire::html/loader.phtml"
>
<arguments>
<argument name="view_model" xsi:type="object">
Magewirephp\Magewire\ViewModel\Magewire
</argument>
</arguments>

<!-- Loader notifications plugin -->
<block name="magewire.loader.notifications"
as="notifications"
ifconfig="dev/magewire/loader/enable_notifications"
template="Magewirephp_Magewire::html/loader/notifications.phtml"/>
template="Magewirephp_Magewire::html/loader/notifications.phtml"
>
<arguments>
<argument name="view_model" xsi:type="object">
Magewirephp\Magewire\ViewModel\Magewire
</argument>
</arguments>

<!-- Loader plugins AlpineJS bindings -->
<block name="magewire.loader.notifications.messenger"
as="messenger"
template="Magewirephp_Magewire::html/loader/notifications/messenger.phtml"
>
<!-- Message item loader spinner SVG -->
<block name="magewire.loader.notifications.messenger.spinner"
as="spinner"
template="Magewirephp_Magewire::html/loader/spinner.phtml"/>
</block>

<!-- Loader notification AlpineJS bindings -->
<block name="magewire.loader.notifications.bindings"
as="bindings"
template="Magewirephp_Magewire::html/loader/notifications/bindings.phtml"/>
</block>

<!-- Loader overlay -->
<block name="magewire.loader.overlay"
as="overlay"
template="Magewirephp_Magewire::html/loader/overlay.phtml"
>
<arguments>
<argument name="view_model" xsi:type="object">
Magewirephp\Magewire\ViewModel\Magewire
</argument>
</arguments>

<!-- Loader spinner SVG -->
<block name="magewire.loader.overlay-spinner"
as="spinner"
template="Magewirephp_Magewire::html/loader/spinner.phtml"/>
</block>

<!-- Loader plugins AlpineJS bindings -->
<block name="magewire.loader.bindings"
as="bindings"
template="Magewirephp_Magewire::html/loader/bindings.phtml"/>
</block>
</referenceContainer>

Expand Down Expand Up @@ -47,9 +93,16 @@
</container>

<container name="magewire.plugin.scripts" as="magewire.plugins">
<!-- Inject the Magewire core Loader plugin -->
<block name="magewire.plugin.loader"
ifconfig="dev/magewire/loader/enable"
template="Magewirephp_Magewire::page/js/magewire/plugin/loader.phtml"/>
template="Magewirephp_Magewire::page/js/magewire/plugin/loader.phtml">
<arguments>
<argument name="view_model" xsi:type="object">
Magewirephp\Magewire\ViewModel\Magewire
</argument>
</arguments>
</block>

<block name="magewire.plugin.error"
template="Magewirephp_Magewire::page/js/magewire/plugin/error.phtml"
Expand Down
6 changes: 4 additions & 2 deletions src/view/frontend/tailwind/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module.exports = {
content: ['../../../../src/view/frontend/templates/**/*.phtml']
};
content: [
'../../../../src/view/frontend/templates/**/*.phtml'
]
};
Loading

0 comments on commit e75a738

Please sign in to comment.