mirror of
https://github.com/symfony/ux.symfony.com.git
synced 2026-03-24 00:02:09 +01:00
[Toolkit] Updated documentation in order to preview components
This commit is contained in:
committed by
Hugo Alliaume
parent
400dcb4511
commit
288570f61b
@@ -26,7 +26,7 @@
|
||||
"symfony/mercure-bundle": "^0.3.9",
|
||||
"symfony/monolog-bundle": "^3.10",
|
||||
"symfony/notifier": "7.2.*",
|
||||
"symfony/runtime": "7.2.*",
|
||||
"symfony/runtime": "^7.2",
|
||||
"symfony/serializer": "7.2.*",
|
||||
"symfony/stimulus-bundle": "2.x-dev",
|
||||
"symfony/translation": "7.2.*",
|
||||
@@ -46,6 +46,7 @@
|
||||
"symfony/ux-svelte": "2.x-dev",
|
||||
"symfony/ux-swup": "2.x-dev",
|
||||
"symfony/ux-toggle-password": "2.x-dev",
|
||||
"symfony/ux-toolkit": "2.x-dev",
|
||||
"symfony/ux-translator": "2.x-dev",
|
||||
"symfony/ux-turbo": "2.x-dev",
|
||||
"symfony/ux-twig-component": "2.x-dev",
|
||||
@@ -55,6 +56,7 @@
|
||||
"symfony/yaml": "7.2.*",
|
||||
"symfonycasts/dynamic-forms": "^0.1.2",
|
||||
"symfonycasts/sass-bundle": "0.8.*",
|
||||
"tales-from-a-dev/twig-tailwind-extra": "^0.3.0",
|
||||
"tempest/highlight": "^2.11.2",
|
||||
"twbs/bootstrap": "^5.3.3",
|
||||
"twig/extra-bundle": "^3.17",
|
||||
|
||||
576
composer.lock
generated
576
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -32,4 +32,6 @@ return [
|
||||
Symfonycasts\SassBundle\SymfonycastsSassBundle::class => ['all' => true],
|
||||
Symfony\UX\Icons\UXIconsBundle::class => ['all' => true],
|
||||
Symfony\UX\Map\UXMapBundle::class => ['all' => true],
|
||||
Symfony\UX\Toolkit\UxToolkitBundle::class => ['all' => true],
|
||||
TalesFromADev\Twig\Extra\Tailwind\Bridge\Symfony\Bundle\TalesFromADevTwigExtraTailwindBundle::class => ['all' => true],
|
||||
];
|
||||
|
||||
4
config/packages/ux_toolkit.yaml
Normal file
4
config/packages/ux_toolkit.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
when@dev:
|
||||
ux_toolkit:
|
||||
theme: default
|
||||
prefix: null
|
||||
@@ -42,15 +42,15 @@ So here you can see we have an `Alert` component that itself uses an Icon compon
|
||||
Or you can compose with the following syntax:
|
||||
|
||||
```twig
|
||||
<twig:Card>
|
||||
<twig:Alert>
|
||||
<twig:ux:icon name="info"/>
|
||||
<twig:Button>
|
||||
<twig:ux:icon name="close" />
|
||||
</twig:Button>
|
||||
</twig:Card>
|
||||
</twig:Alert>
|
||||
```
|
||||
|
||||
So here we have a `Card` component, and we provide the content of this component with two other components.
|
||||
So here we have a `Alert` component, and we provide the content of this component with two other components.
|
||||
|
||||
### Independence
|
||||
|
||||
|
||||
104
src/Controller/UxPackage/ToolkitController.php
Normal file
104
src/Controller/UxPackage/ToolkitController.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Controller\UxPackage;
|
||||
|
||||
use App\Service\ToolkitComponentService;
|
||||
use App\Service\UxPackageRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\UX\Toolkit\Registry\Registry;
|
||||
use Symfony\UX\Toolkit\Registry\RegistryItemType;
|
||||
|
||||
class ToolkitController extends AbstractController
|
||||
{
|
||||
#[Route('/toolkit', name: 'app_toolkit')]
|
||||
public function index(UxPackageRepository $packageRepository, Registry $registry): Response
|
||||
{
|
||||
$package = $packageRepository->find('toolkit');
|
||||
return $this->render('ux_packages/toolkit.html.twig', [
|
||||
'components' => $registry->all(),
|
||||
'package' => $package,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/components/{currentComponent}', name: 'app_toolkit_components', defaults: ['currentComponent' => ''])]
|
||||
public function components(
|
||||
UxPackageRepository $packageRepository,
|
||||
ToolkitComponentService $toolkitComponentService,
|
||||
string $currentComponent
|
||||
): Response
|
||||
{
|
||||
$package = $packageRepository->find('toolkit');
|
||||
$registry = $toolkitComponentService->getRegistry();
|
||||
|
||||
$component = $registry->get($currentComponent);
|
||||
if (null == $component) {
|
||||
// get the first non-example component
|
||||
$components = array_filter($registry->all(), function ($component) {
|
||||
if($component->type !== RegistryItemType::Component) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $component;
|
||||
});
|
||||
$component = reset($components);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (null === $component) {
|
||||
throw $this->createNotFoundException('No component found');
|
||||
}
|
||||
|
||||
// get all examples for this component
|
||||
$examples = array_filter($registry->all(), function ($component) use ($currentComponent) {
|
||||
if($component->type !== RegistryItemType::Example) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($component->parentName !== $currentComponent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $component;
|
||||
});
|
||||
|
||||
return $this->render('ux_packages/toolkit/components.html.twig', [
|
||||
'components' => $registry->all(),
|
||||
'currentComponent' => $component,
|
||||
'examples' => $examples,
|
||||
'package' => $package,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/components/{currentComponent}/preview', name: 'app_toolkit_component_preview')]
|
||||
public function componentPreview(
|
||||
ToolkitComponentService $toolkitComponentService,
|
||||
string $currentComponent,
|
||||
): Response
|
||||
{
|
||||
$currentComponentFromRegistry = $toolkitComponentService->getRegistry()->get($currentComponent, RegistryItemType::Example);
|
||||
if (null === $currentComponentFromRegistry) {
|
||||
throw $this->createNotFoundException('Example not found');
|
||||
}
|
||||
|
||||
$html = $toolkitComponentService->preview($currentComponentFromRegistry->name, RegistryItemType::Example);
|
||||
return $this->render('ux_packages/toolkit/preview.html.twig', [
|
||||
'component' => $currentComponentFromRegistry,
|
||||
'html' => $html,
|
||||
// in the future, we'll change the framework dynamically
|
||||
'cssFramework' => 'tailwindcss',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,6 +29,7 @@ class UxPackage
|
||||
private ?string $createString = null,
|
||||
private ?string $imageFileName = null,
|
||||
private ?string $composerName = null,
|
||||
private bool $onlyInDevMode = false,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -79,6 +80,10 @@ class UxPackage
|
||||
|
||||
public function getComposerRequireCommand(): string
|
||||
{
|
||||
if ($this->onlyInDevMode) {
|
||||
return 'composer require --dev '.$this->getComposerName();
|
||||
}
|
||||
|
||||
return 'composer require '.$this->getComposerName();
|
||||
}
|
||||
|
||||
|
||||
140
src/Service/ToolkitComponentService.php
Normal file
140
src/Service/ToolkitComponentService.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceLocator;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||
use Symfony\UX\Toolkit\Registry\Registry;
|
||||
use Symfony\UX\Toolkit\Registry\RegistryFactory;
|
||||
use Symfony\UX\Toolkit\Registry\RegistryItem;
|
||||
use Symfony\UX\Toolkit\Registry\RegistryItemType;
|
||||
use Symfony\UX\TwigComponent\ComponentFactory;
|
||||
use Symfony\UX\TwigComponent\ComponentProperties;
|
||||
use Symfony\UX\TwigComponent\ComponentRenderer;
|
||||
use Symfony\UX\TwigComponent\ComponentStack;
|
||||
use Symfony\UX\TwigComponent\ComponentTemplateFinder;
|
||||
use Symfony\UX\TwigComponent\DependencyInjection\TwigComponentExtension;
|
||||
use Twig\Environment;
|
||||
|
||||
final class ToolkitComponentService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RegistryFactory $registryFactory,
|
||||
private readonly Environment $twig,
|
||||
private readonly string $manifest = __DIR__ . '/../../vendor/symfony/ux-toolkit/registry/default',
|
||||
) {}
|
||||
|
||||
|
||||
public function getRegistry(): Registry
|
||||
{
|
||||
return $this->registryFactory->create((new Finder())->files()->in($this->manifest));
|
||||
}
|
||||
|
||||
public function get(string $componentName): ?RegistryItem
|
||||
{
|
||||
$registry = $this->getRegistry();
|
||||
|
||||
foreach ($registry->all() as $component) {
|
||||
if ($component->name === $componentName && $component->type === RegistryItemType::Component) {
|
||||
return $component;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getWorkdir(): string {
|
||||
|
||||
// Put the code in a temporary file. We can image in the future use another way to pass the code to the component,
|
||||
// but today the ComponentFactory need a folder
|
||||
// We do it only once, for all visitors
|
||||
$filesystem = new Filesystem();
|
||||
$workdir = sys_get_temp_dir() . '/uxcomponent';
|
||||
if (!$filesystem->exists($workdir)) {
|
||||
$filesystem->mkdir($workdir);
|
||||
|
||||
// we put all components in this folder
|
||||
foreach ($this->getRegistry()->all() as $component) {
|
||||
$componentFile = $workdir . '/' . $component->name . '.twig';
|
||||
file_put_contents($componentFile, $component->code);
|
||||
}
|
||||
}
|
||||
|
||||
return $workdir;
|
||||
}
|
||||
|
||||
public function getComponentTwigPath(string $componentName): string
|
||||
{
|
||||
return $this->getWorkdir() . '/' . $componentName . '.twig';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allow to render a dynamic component, without using the auto-wired TwigComponentExtension
|
||||
*
|
||||
* Actually, the extension compile components. If we want to render a dynamic component, we need to do it manually
|
||||
* This should be improved in the future.
|
||||
*
|
||||
* @param string $componentName
|
||||
* @return string
|
||||
*/
|
||||
public function preview(string $componentName, RegistryItemType $type = RegistryItemType::Component): string
|
||||
{
|
||||
$registry = $this->getRegistry();
|
||||
if (!$registry->has($componentName, $type)) {
|
||||
throw new \InvalidArgumentException(sprintf('Component "%s" not found.', $componentName));
|
||||
}
|
||||
|
||||
$currentComponentFromRegistry = $registry->get($componentName, $type);
|
||||
|
||||
|
||||
|
||||
// Add this path to twig
|
||||
$loader = $this->twig->getLoader();
|
||||
$loader->addPath($this->getWorkdir());
|
||||
|
||||
// Component finder use compiled values. We need to construct new one
|
||||
$componentFactory = new ComponentFactory(
|
||||
new ComponentTemplateFinder($this->twig),
|
||||
new ServiceLocator(function() {
|
||||
return [];
|
||||
}, []),
|
||||
new PropertyAccessor(),
|
||||
new EventDispatcher(),
|
||||
[],
|
||||
[],
|
||||
);
|
||||
|
||||
if ($type === RegistryItemType::Component) {
|
||||
// Preview a component
|
||||
$mounted = $componentFactory->create($currentComponentFromRegistry->name);
|
||||
|
||||
$componentProperties = new ComponentProperties(new PropertyAccessor());
|
||||
$componentStack = new ComponentStack();
|
||||
|
||||
$renderer = new ComponentRenderer(
|
||||
$this->twig,
|
||||
new EventDispatcher(),
|
||||
$componentFactory,
|
||||
$componentProperties,
|
||||
$componentStack,
|
||||
);
|
||||
|
||||
return $renderer->render($mounted);
|
||||
}
|
||||
|
||||
// Preview an example
|
||||
return $this->twig->render($currentComponentFromRegistry->name . '.twig');
|
||||
}
|
||||
}
|
||||
@@ -231,6 +231,20 @@ class UxPackageRepository
|
||||
'Switch the visibility of a password field',
|
||||
),
|
||||
|
||||
new UxPackage(
|
||||
'toolkit',
|
||||
'Toolkit',
|
||||
'app_toolkit',
|
||||
'#BE0404',
|
||||
'linear-gradient(142deg,rgb(60, 230, 236) -15%,rgb(77, 97, 214) 95%)',
|
||||
'Build your Design System.',
|
||||
'Collection of components and templates that you can use to build your pages.',
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true
|
||||
),
|
||||
|
||||
(new UxPackage(
|
||||
'typed',
|
||||
'Typed',
|
||||
|
||||
@@ -678,6 +678,15 @@
|
||||
"symfonycasts/sass-bundle": {
|
||||
"version": "v0.1.0"
|
||||
},
|
||||
"tales-from-a-dev/twig-tailwind-extra": {
|
||||
"version": "0.3",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes-contrib",
|
||||
"branch": "main",
|
||||
"version": "0.2",
|
||||
"ref": "7243ab070ed66198eb82c026684e9b9773e7b64a"
|
||||
}
|
||||
},
|
||||
"theseer/tokenizer": {
|
||||
"version": "1.2.1"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<div class="alert alert-{{ type }} alert-dismissible" role="alert">
|
||||
<twig:ux:icon name="{{ this.icon }}" class="Icon bi" />
|
||||
{{ message }}
|
||||
<div {{ attributes.without('class') }}
|
||||
class="{{ (' ' ~ attributes.render('class'))|tailwind_merge }}"
|
||||
>
|
||||
<twig:Button>Dependency test</twig:Button>
|
||||
{% block content %}Alert{% endblock %}
|
||||
</div>
|
||||
|
||||
9
templates/components/AspectRatio.html.twig
Normal file
9
templates/components/AspectRatio.html.twig
Normal file
@@ -0,0 +1,9 @@
|
||||
{%- props ratio = (4/3) -%}
|
||||
<div
|
||||
style="width:100%; position: relative; padding-bottom: calc((1/({{ ratio }})) * 100%);"
|
||||
{{ attributes.defaults({}).without('style') }}
|
||||
>
|
||||
<div style="position: absolute; left:0; right:0; top:0; bottom: 0">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
13
templates/components/Avatar.html.twig
Normal file
13
templates/components/Avatar.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<span
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</span>
|
||||
13
templates/components/Avatar/AvatarFallback.html.twig
Normal file
13
templates/components/Avatar/AvatarFallback.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<span
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</span>
|
||||
11
templates/components/Avatar/AvatarImage.html.twig
Normal file
11
templates/components/Avatar/AvatarImage.html.twig
Normal file
@@ -0,0 +1,11 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'aspect-square h-full w-full',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<img
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
/>
|
||||
13
templates/components/AvatarFallback.html.twig
Normal file
13
templates/components/AvatarFallback.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<span
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</span>
|
||||
11
templates/components/AvatarImage.html.twig
Normal file
11
templates/components/AvatarImage.html.twig
Normal file
@@ -0,0 +1,11 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'aspect-square h-full w-full',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<img
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
/>
|
||||
@@ -1,19 +1,34 @@
|
||||
<div {{ attributes.defaults({
|
||||
class: 'Badge',
|
||||
}) }}>
|
||||
<span class="Badge__label">
|
||||
{%- if icon %}
|
||||
<twig:ux:Icon name="{{ icon }}" class="Badge__icon" />
|
||||
{% endif -%}
|
||||
{{- label -}}
|
||||
</span>
|
||||
<span class="Badge__value">
|
||||
{%- if url %}
|
||||
<a href="{{ url }}" class="Badge__link">
|
||||
{{- value -}}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ value }}
|
||||
{% endif -%}
|
||||
</span>
|
||||
{%- props variant = 'default', outline = false -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
variants: {
|
||||
variant: {
|
||||
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
},
|
||||
outline: {
|
||||
true: "text-foreground bg-white",
|
||||
}
|
||||
},
|
||||
compoundVariants: [{
|
||||
variant: ['default'],
|
||||
outline: ['true'],
|
||||
class: 'border-primary',
|
||||
}, {
|
||||
variant: ['secondary'],
|
||||
outline: ['true'],
|
||||
class: 'border-secondary',
|
||||
}, {
|
||||
variant: ['destructive'],
|
||||
outline: ['true'],
|
||||
class: 'border-destructive',
|
||||
},]
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({variant, outline}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
13
templates/components/Blank.html.twig
Normal file
13
templates/components/Blank.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: '',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<span
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</span>
|
||||
13
templates/components/Breadcrumb.html.twig
Normal file
13
templates/components/Breadcrumb.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: '',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<span
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</span>
|
||||
20
templates/components/Breadcrumb/BreadcrumbEllipsis.html.twig
Normal file
20
templates/components/Breadcrumb/BreadcrumbEllipsis.html.twig
Normal file
@@ -0,0 +1,20 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex h-9 w-9 items-center justify-center',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<span
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{% set _block = block('content') %}
|
||||
{% if content is defined and content is not empty %}
|
||||
{% block content %}{% endblock %}
|
||||
{% else %}
|
||||
<span class="h-4 w-4">...</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
13
templates/components/Breadcrumb/BreadcrumbItem.html.twig
Normal file
13
templates/components/Breadcrumb/BreadcrumbItem.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'inline-flex items-center gap-1.5',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<li
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</li>
|
||||
13
templates/components/Breadcrumb/BreadcrumbLink.html.twig
Normal file
13
templates/components/Breadcrumb/BreadcrumbLink.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'transition-colors hover:text-foreground',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<a
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</a>
|
||||
13
templates/components/Breadcrumb/BreadcrumbList.html.twig
Normal file
13
templates/components/Breadcrumb/BreadcrumbList.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<ol
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</ol>
|
||||
18
templates/components/Breadcrumb/BreadcrumbPage.html.twig
Normal file
18
templates/components/Breadcrumb/BreadcrumbPage.html.twig
Normal file
@@ -0,0 +1,18 @@
|
||||
{%- props
|
||||
disabled = false
|
||||
-%}
|
||||
{%- set style = html_cva(
|
||||
base: 'font-normal text-foreground',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<span
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
role="link"
|
||||
aria-disabled="{{ disabled ? 'true' : 'false' }}"
|
||||
aria-current="page"
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</span>
|
||||
@@ -0,0 +1,20 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: '[&>svg]:w-3.5 [&>svg]:h-3.5',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<li
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{% set _block = block('content') %}
|
||||
{% if content is defined and content is not empty %}
|
||||
{% block content %}{% endblock %}
|
||||
{% else %}
|
||||
<twig:ux:Icon name="mdi:chevron-right" />
|
||||
{% endif %}
|
||||
</li>
|
||||
20
templates/components/BreadcrumbEllipsis.html.twig
Normal file
20
templates/components/BreadcrumbEllipsis.html.twig
Normal file
@@ -0,0 +1,20 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex h-9 w-9 items-center justify-center',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<span
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{% set _block = block('content') %}
|
||||
{% if content is defined and content is not empty %}
|
||||
{% block content %}{% endblock %}
|
||||
{% else %}
|
||||
<span class="h-4 w-4">...</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
13
templates/components/BreadcrumbItem.html.twig
Normal file
13
templates/components/BreadcrumbItem.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'inline-flex items-center gap-1.5',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<li
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</li>
|
||||
13
templates/components/BreadcrumbLink.html.twig
Normal file
13
templates/components/BreadcrumbLink.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'transition-colors hover:text-foreground',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<a
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</a>
|
||||
13
templates/components/BreadcrumbList.html.twig
Normal file
13
templates/components/BreadcrumbList.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<ol
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</ol>
|
||||
18
templates/components/BreadcrumbPage.html.twig
Normal file
18
templates/components/BreadcrumbPage.html.twig
Normal file
@@ -0,0 +1,18 @@
|
||||
{%- props
|
||||
disabled = false
|
||||
-%}
|
||||
{%- set style = html_cva(
|
||||
base: 'font-normal text-foreground',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<span
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
role="link"
|
||||
aria-disabled="{{ disabled ? 'true' : 'false' }}"
|
||||
aria-current="page"
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</span>
|
||||
20
templates/components/BreadcrumbSeparator.html.twig
Normal file
20
templates/components/BreadcrumbSeparator.html.twig
Normal file
@@ -0,0 +1,20 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: '[&>svg]:w-3.5 [&>svg]:h-3.5',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<li
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{% set _block = block('content') %}
|
||||
{% if content is defined and content is not empty %}
|
||||
{% block content %}{% endblock %}
|
||||
{% else %}
|
||||
<twig:ux:Icon name="mdi:chevron-right" />
|
||||
{% endif %}
|
||||
</li>
|
||||
42
templates/components/Button.html.twig
Normal file
42
templates/components/Button.html.twig
Normal file
@@ -0,0 +1,42 @@
|
||||
{%- props variant = 'default', outline = false, size = 'default' -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
outline: {
|
||||
true: "text-foreground bg-white",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
compoundVariants: [{
|
||||
variant: ['default'],
|
||||
outline: ['true'],
|
||||
class: 'border-primary',
|
||||
}, {
|
||||
variant: ['secondary'],
|
||||
outline: ['true'],
|
||||
class: 'border-secondary',
|
||||
}, {
|
||||
variant: ['destructive'],
|
||||
outline: ['true'],
|
||||
class: 'border-destructive',
|
||||
},]
|
||||
) -%}
|
||||
|
||||
<button
|
||||
class="{{ style.apply({variant, outline, size}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</button>
|
||||
@@ -1,33 +1,13 @@
|
||||
{% props name, image, url, description, tags, lazyload = true %}
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'rounded-xl border bg-card text-card-foreground shadow',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<article class="DemoCard">
|
||||
|
||||
<div class="DemoCard__media">
|
||||
<img class="DemoCard__image"
|
||||
src="{{ image }}"
|
||||
width="640"
|
||||
height="360"
|
||||
alt="{{ name }}"
|
||||
{% if lazyload %}
|
||||
loading="lazy"
|
||||
{% endif %}
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="DemoCard__content">
|
||||
<h4 class="DemoCard__title">
|
||||
<a href="{{ url }}" class="stretched-link">
|
||||
{{- name -}}
|
||||
</a>
|
||||
</h4>
|
||||
<p class="DemoCard__description">
|
||||
{{- description -}}
|
||||
</p>
|
||||
<p class="DemoCard__tags">
|
||||
{% for tag in tags %}
|
||||
<span class="Tag">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</article>
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
13
templates/components/Card/CardContent.html.twig
Normal file
13
templates/components/Card/CardContent.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'p-6 pt-0',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/Card/CardDescription.html.twig
Normal file
13
templates/components/Card/CardDescription.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'text-sm text-muted-foreground',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/Card/CardFooter.html.twig
Normal file
13
templates/components/Card/CardFooter.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex items-center p-6 pt-0',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/Card/CardHeader.html.twig
Normal file
13
templates/components/Card/CardHeader.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex flex-col space-y-1.5 p-6',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/Card/CardTitle.html.twig
Normal file
13
templates/components/Card/CardTitle.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'font-semibold leading-none tracking-tight',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/CardContent.html.twig
Normal file
13
templates/components/CardContent.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'p-6 pt-0',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/CardDescription.html.twig
Normal file
13
templates/components/CardDescription.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'text-sm text-muted-foreground',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/CardFooter.html.twig
Normal file
13
templates/components/CardFooter.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex items-center p-6 pt-0',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/CardHeader.html.twig
Normal file
13
templates/components/CardHeader.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'flex flex-col space-y-1.5 p-6',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/CardTitle.html.twig
Normal file
13
templates/components/CardTitle.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'font-semibold leading-none tracking-tight',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
34
templates/components/Code/CodeBlockEmbed.html.twig
Normal file
34
templates/components/Code/CodeBlockEmbed.html.twig
Normal file
@@ -0,0 +1,34 @@
|
||||
{% props
|
||||
code = '',
|
||||
language = 'twig',
|
||||
highlight = true,
|
||||
%}
|
||||
|
||||
<div
|
||||
class="IconModal__code"
|
||||
data-controller="clipboarder"
|
||||
>
|
||||
{% set _code -%}
|
||||
{% block content -%}
|
||||
{{ code ? code|trim|raw }}
|
||||
{%- endblock -%}
|
||||
{%- endset %}
|
||||
|
||||
{% if highlight %}
|
||||
<pre
|
||||
class="language-{{ language }}"
|
||||
style="--height: auto"
|
||||
data-clipboarder-target="source"
|
||||
><code>{{ _code ? _code|trim|raw|highlight(language) : code|highlight(language) }}</code>
|
||||
</pre>
|
||||
{% else %}
|
||||
<pre
|
||||
style="--height: auto"
|
||||
data-clipboarder-target="source"
|
||||
><code>{{ _code ? _code|trim|raw : code }}</code></pre>
|
||||
{% endif %}
|
||||
|
||||
<button class="btn btn-sm IconModalcopy" data-action="clipboarder#copy" data-clipboarder-target="button">
|
||||
<twig:ux:icon name="copy" />
|
||||
</button>
|
||||
</div>
|
||||
33
templates/components/CookbookCard.html.twig
Normal file
33
templates/components/CookbookCard.html.twig
Normal file
@@ -0,0 +1,33 @@
|
||||
{% props name, image, url, description, tags, lazyload = true %}
|
||||
|
||||
<article class="DemoCard">
|
||||
|
||||
<div class="DemoCard__media">
|
||||
<img class="DemoCard__image"
|
||||
src="{{ image }}"
|
||||
width="640"
|
||||
height="360"
|
||||
alt="{{ name }}"
|
||||
{% if lazyload %}
|
||||
loading="lazy"
|
||||
{% endif %}
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="DemoCard__content">
|
||||
<h4 class="DemoCard__title">
|
||||
<a href="{{ url }}" class="stretched-link">
|
||||
{{- name -}}
|
||||
</a>
|
||||
</h4>
|
||||
<p class="DemoCard__description">
|
||||
{{- description -}}
|
||||
</p>
|
||||
<p class="DemoCard__tags">
|
||||
{% for tag in tags %}
|
||||
<span class="Tag">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</article>
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="DocsLink_content">
|
||||
{% if isSmall %}
|
||||
<p class="d-flex align-items-center">
|
||||
<twig:ux:icon name="mdi:book-open-variant-outline" class="Icon me-2" />
|
||||
<twig:ux:icon name="{{ icon|default('mdi:book-open-variant-outline') }}" class="Icon me-2" />
|
||||
|
||||
<a href="{{ url }}" class="DocsLink_link"
|
||||
rel="{{ isExternal ? 'external noopened noreferrer' }}"
|
||||
|
||||
68
templates/components/Grid.html.twig
Normal file
68
templates/components/Grid.html.twig
Normal file
@@ -0,0 +1,68 @@
|
||||
{%- props
|
||||
xs = 0,
|
||||
sm = 0,
|
||||
md = 0,
|
||||
lg = 0,
|
||||
xl = 0,
|
||||
align = '',
|
||||
justify = '',
|
||||
direction = '',
|
||||
gap = 0,
|
||||
-%}
|
||||
|
||||
{% set xsSize = false %}{% if xs > 0 %}{% set xsSize = "true" %}{% endif %}
|
||||
{% set smSize = false %}{% if sm > 0 %}{% set smSize = "true" %}{% endif %}
|
||||
{% set mdSize = false %}{% if md > 0 %}{% set mdSize = "true" %}{% endif %}
|
||||
{% set lgSize = false %}{% if lg > 0 %}{% set lgSize = "true" %}{% endif %}
|
||||
{% set xlSize = false %}{% if xl > 0 %}{% set xlSize = "true" %}{% endif %}
|
||||
|
||||
{% set alignClass = '' %}
|
||||
{% if align == 'start' %}{% set alignClass = 'items-start' %}
|
||||
{% elseif align == 'center' %}{% set alignClass = 'items-center' %}
|
||||
{% elseif align == 'end' %}{% set alignClass = 'items-end' %}
|
||||
{% endif %}
|
||||
|
||||
{% set justifyClass = '' %}
|
||||
{% if justify == 'start' %}{% set justifyClass = 'justify-start' %}
|
||||
{% elseif justify == 'center' %}{% set justifyClass = 'justify-center' %}
|
||||
{% elseif justify == 'end' %}{% set justifyClass = 'justify-end' %}
|
||||
{% elseif justify == 'between' %}{% set justifyClass = 'justify-between' %}
|
||||
{% elseif justify == 'around' %}{% set justifyClass = 'justify-around' %}
|
||||
{% elseif justify == 'evenly' %}{% set justifyClass = 'justify-evenly' %}
|
||||
{% endif %}
|
||||
|
||||
{% set directionClass = '' %}
|
||||
{% if direction == 'row' %}{% set directionClass = 'flex-row' %}
|
||||
{% elseif direction == 'column' %}{% set directionClass = 'flex-col' %}
|
||||
{% endif %}
|
||||
|
||||
{% set gapClass = '' %}
|
||||
{% if gap > 0 %}
|
||||
{% set gapClass = 'gap-' ~ gap %}
|
||||
{% endif %}
|
||||
{%- set style = html_cva(
|
||||
base: 'grid',
|
||||
variants: {
|
||||
xsSize: {
|
||||
"true": 'grid-cols-' ~ xs,
|
||||
},
|
||||
smSize: {
|
||||
"true": 'sm:grid-cols-' ~ sm,
|
||||
},
|
||||
mdSize: {
|
||||
"true": 'md:grid-cols-' ~ md,
|
||||
},
|
||||
lgSize: {
|
||||
"true": 'lg:grid-cols-' ~ lg,
|
||||
},
|
||||
xlSize: {
|
||||
"true": 'xl:grid-cols-' ~ xl,
|
||||
},
|
||||
},
|
||||
) -%}
|
||||
<div
|
||||
class="{{ style.apply({xsSize, smSize, mdSize, lgSize, xlSize}, attributes.render('class'))|tailwind_merge }} {{ alignClass }} {{ justifyClass }} {{ directionClass }} {{ gapClass }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
77
templates/components/Grid/Col.html.twig
Normal file
77
templates/components/Grid/Col.html.twig
Normal file
@@ -0,0 +1,77 @@
|
||||
{%- props
|
||||
xs = 0,
|
||||
sm = 0,
|
||||
md = 0,
|
||||
lg = 0,
|
||||
xl = 0,
|
||||
xsOffset = 0,
|
||||
smOffset = 0,
|
||||
mdOffset = 0,
|
||||
lgOffset = 0,
|
||||
xlOffset = 0,
|
||||
first = false,
|
||||
last = false,
|
||||
-%}
|
||||
|
||||
{% set xsCols = false %}{% if xs > 0 %}{% set xsCols = "true" %}{% endif %}
|
||||
{% set smCols = false %}{% if sm > 0 %}{% set smCols = "true" %}{% endif %}
|
||||
{% set mdCols = false %}{% if md > 0 %}{% set mdCols = "true" %}{% endif %}
|
||||
{% set lgCols = false %}{% if lg > 0 %}{% set lgCols = "true" %}{% endif %}
|
||||
{% set xlCols = false %}{% if xl > 0 %}{% set xlCols = "true" %}{% endif %}
|
||||
|
||||
{% set xsOffsetCols = false %}{% if xsOffset > 0 %}{% set xsOffsetCols = "true" %}{% endif %}
|
||||
{% set smOffsetCols = false %}{% if smOffset > 0 %}{% set smOffsetCols = "true" %}{% endif %}
|
||||
{% set mdOffsetCols = false %}{% if mdOffset > 0 %}{% set mdOffsetCols = "true" %}{% endif %}
|
||||
{% set lgOffsetCols = false %}{% if lgOffset > 0 %}{% set lgOffsetCols = "true" %}{% endif %}
|
||||
{% set xlOffsetCols = false %}{% if xlOffset > 0 %}{% set xlOffsetCols = "true" %}{% endif %}
|
||||
|
||||
{% set firstClass = false %}{% if first %}{% set firstClass = "true" %}{% endif %}
|
||||
{% set lastClass = false %}{% if last %}{% set lastClass = "true" %}{% endif %}
|
||||
|
||||
{%- set style = html_cva(
|
||||
base: '',
|
||||
variants: {
|
||||
xsCols: {
|
||||
"true": 'col-span-' ~ xs,
|
||||
},
|
||||
smCols: {
|
||||
"true": 'sm:col-span-' ~ sm,
|
||||
},
|
||||
mdCols: {
|
||||
"true": 'md:col-span-' ~ md,
|
||||
},
|
||||
lgCols: {
|
||||
"true": 'lg:col-span-' ~ lg,
|
||||
},
|
||||
xlCols: {
|
||||
"true": 'xl:col-span-' ~ xl,
|
||||
},
|
||||
xsOffsetCols: {
|
||||
"true": 'col-start-' ~ (xsOffset + 1),
|
||||
},
|
||||
smOffsetCols: {
|
||||
"true": 'sm:col-start-' ~ (smOffset + 1),
|
||||
},
|
||||
mdOffsetCols: {
|
||||
"true": 'md:col-start-' ~ (mdOffset + 1),
|
||||
},
|
||||
lgOffsetCols: {
|
||||
"true": 'lg:col-start-' ~ (lgOffset + 1),
|
||||
},
|
||||
xlOffsetCols: {
|
||||
"true": 'xl:col-start-' ~ (xlOffset + 1),
|
||||
},
|
||||
firstClass: {
|
||||
"true": 'order-first',
|
||||
},
|
||||
lastClass: {
|
||||
"true": 'order-last',
|
||||
},
|
||||
},
|
||||
) -%}
|
||||
<div
|
||||
class="{{ style.apply({xsCols, smCols, mdCols, lgCols, xlCols, xsOffsetCols, smOffsetCols, mdOffsetCols, lgOffsetCols, xlOffsetCols, firstClass, lastClass}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
60
templates/components/Grid/Row.html.twig
Normal file
60
templates/components/Grid/Row.html.twig
Normal file
@@ -0,0 +1,60 @@
|
||||
{%- props
|
||||
start = 'auto',
|
||||
center = false,
|
||||
end = false,
|
||||
top = false,
|
||||
middle = false,
|
||||
bottom = false,
|
||||
around = false,
|
||||
between = false,
|
||||
reverse = false,
|
||||
-%}
|
||||
|
||||
{% set startClass = false %}{% if start != 'auto' %}{% set startClass = "true" %}{% endif %}
|
||||
{% set centerClass = false %}{% if center %}{% set centerClass = "true" %}{% endif %}
|
||||
{% set endClass = false %}{% if end %}{% set endClass = "true" %}{% endif %}
|
||||
{% set topClass = false %}{% if top %}{% set topClass = "true" %}{% endif %}
|
||||
{% set middleClass = false %}{% if middle %}{% set middleClass = "true" %}{% endif %}
|
||||
{% set bottomClass = false %}{% if bottom %}{% set bottomClass = "true" %}{% endif %}
|
||||
{% set aroundClass = false %}{% if around %}{% set aroundClass = "true" %}{% endif %}
|
||||
{% set betweenClass = false %}{% if between %}{% set betweenClass = "true" %}{% endif %}
|
||||
{% set reverseClass = false %}{% if reverse %}{% set reverseClass = "true" %}{% endif %}
|
||||
|
||||
{%- set style = html_cva(
|
||||
base: 'flex flex-wrap',
|
||||
variants: {
|
||||
startClass: {
|
||||
"true": 'justify-start',
|
||||
},
|
||||
centerClass: {
|
||||
"true": 'justify-center',
|
||||
},
|
||||
endClass: {
|
||||
"true": 'justify-end',
|
||||
},
|
||||
topClass: {
|
||||
"true": 'items-start',
|
||||
},
|
||||
middleClass: {
|
||||
"true": 'items-center',
|
||||
},
|
||||
bottomClass: {
|
||||
"true": 'items-end',
|
||||
},
|
||||
aroundClass: {
|
||||
"true": 'justify-around',
|
||||
},
|
||||
betweenClass: {
|
||||
"true": 'justify-between',
|
||||
},
|
||||
reverseClass: {
|
||||
"true": 'flex-row-reverse',
|
||||
},
|
||||
},
|
||||
) -%}
|
||||
<div
|
||||
class="{{ style.apply({startClass, centerClass, endClass, topClass, middleClass, bottomClass, aroundClass, betweenClass, reverseClass}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
5
templates/components/Navbar.html.twig
Normal file
5
templates/components/Navbar.html.twig
Normal file
@@ -0,0 +1,5 @@
|
||||
<nav {{ attributes.without('class') }}
|
||||
class="{{ (' ' ~ attributes.render('class'))|tailwind_merge }}"
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</nav>
|
||||
@@ -27,6 +27,16 @@
|
||||
title="Read the docs"
|
||||
url="{{ package.officialDocsUrl }}"
|
||||
/>
|
||||
|
||||
{% if packageBrowseUrl is defined %}
|
||||
<twig:DocsLink
|
||||
icon="mdi:package-variant-closed"
|
||||
class="ms-3"
|
||||
size="sm"
|
||||
title="{{ packageBrowseText|default('Explore') }}"
|
||||
url="{{ packageBrowseUrl }}"
|
||||
/>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
13
templates/components/Table.html.twig
Normal file
13
templates/components/Table.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'relative w-full overflow-auto',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<table
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</table>
|
||||
13
templates/components/Table/TableBody.html.twig
Normal file
13
templates/components/Table/TableBody.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: '[&_tr:last-child]:border-0',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<tbody
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</tbody>
|
||||
13
templates/components/Table/TableCaption.html.twig
Normal file
13
templates/components/Table/TableCaption.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'text-muted-foreground mt-4 text-sm',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<caption
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</caption>
|
||||
13
templates/components/Table/TableCell.html.twig
Normal file
13
templates/components/Table/TableCell.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<td
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</td>
|
||||
13
templates/components/Table/TableFooter.html.twig
Normal file
13
templates/components/Table/TableFooter.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<tfoot
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</tfoot>
|
||||
13
templates/components/Table/TableHead.html.twig
Normal file
13
templates/components/Table/TableHead.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'text-muted-foreground h-10 px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<th
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</th>
|
||||
13
templates/components/Table/TableHeader.html.twig
Normal file
13
templates/components/Table/TableHeader.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: '[&_tr]:border-b',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<div
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
13
templates/components/Table/TableRow.html.twig
Normal file
13
templates/components/Table/TableRow.html.twig
Normal file
@@ -0,0 +1,13 @@
|
||||
{%- props -%}
|
||||
{%- set style = html_cva(
|
||||
base: 'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
|
||||
variants: {},
|
||||
compoundVariants: []
|
||||
) -%}
|
||||
|
||||
<tr
|
||||
class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}"
|
||||
{{ attributes.defaults({}).without('class') }}
|
||||
>
|
||||
{% block content %}{% endblock %}
|
||||
</tr>
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="container-fluid container-xxl px-4 pt-4 px-md-5 pt-md-5">
|
||||
<div style="display: grid; gap: 3rem; grid-template-columns: 1fr 1fr 1fr;">
|
||||
{% for cookbook in cookbooks %}
|
||||
<twig:Card
|
||||
<twig:CookbookCard
|
||||
name="{{ cookbook.title }}"
|
||||
description="{{ cookbook.description }}"
|
||||
image="{{ asset('images/cookbook/%s-1280x720.png'|format(cookbook.slug)) }}"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="col-12 col-md-6">
|
||||
{% component Terminal with {bottomPadding: 20} %}
|
||||
{% block content %}
|
||||
composer require {{ package.composerName }}
|
||||
composer require {% if installOnlyInDevMode|default(false) == true%} --dev {% endif %}{{ package.composerName }}
|
||||
{% endblock %}
|
||||
{% endcomponent %}
|
||||
</div>
|
||||
|
||||
185
templates/ux_packages/toolkit.html.twig
Normal file
185
templates/ux_packages/toolkit.html.twig
Normal file
@@ -0,0 +1,185 @@
|
||||
{% extends 'ux_packages/package.html.twig' %}
|
||||
|
||||
{% block banner %}
|
||||
{{ include('_banner.html.twig', {color_back: '#4a5d20'}) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block package_header %}
|
||||
{% component PackageHeader with {
|
||||
package: 'toolkit',
|
||||
eyebrowText: 'Toolkit for your pages and templates',
|
||||
packageBrowseText: 'Components',
|
||||
packageBrowseUrl: path('app_toolkit_components'),
|
||||
} %}
|
||||
{% block title_header %}
|
||||
Build your Design System.
|
||||
{% endblock %}
|
||||
|
||||
{% block sub_content %}
|
||||
The UX Toolkit is a collection of components and templates that you can use to build your pages.
|
||||
{% endblock %}
|
||||
{% endcomponent %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block demo_title %}UX Toolkit{% endblock %}
|
||||
|
||||
{% block demo_content %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<twig:CodeBlock
|
||||
filename="templates/ux_packages/toolkit.html.twig"
|
||||
:showFilename="false"
|
||||
targetTwigBlock="card_example"
|
||||
:showTwigExtends="false"
|
||||
height="auto"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{% block card_example %}
|
||||
<twig:Card>
|
||||
<twig:CardHeader>
|
||||
<twig:CardTitle>Symfony is cool</twig:CardTitle>
|
||||
<twig:CardDescription>Symfony is a set of reusable PHP components...</twig:CardDescription>
|
||||
</twig:CardHeader>
|
||||
<twig:CardContent>
|
||||
... and a PHP framework for web projects
|
||||
</twig:CardContent>
|
||||
<twig:CardFooter>
|
||||
<twig:Button primary class="w-full">Visit symfony.com</twig:Button>
|
||||
</twig:CardFooter>
|
||||
</twig:Card>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row my-3">
|
||||
<div class="col-6">
|
||||
<twig:CodeBlock
|
||||
filename="templates/ux_packages/toolkit.html.twig"
|
||||
:showFilename="false"
|
||||
targetTwigBlock="avatar_example"
|
||||
:showTwigExtends="false"
|
||||
height="auto"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{% block avatar_example %}
|
||||
<twig:Avatar class="inline-block">
|
||||
<twig:AvatarImage src="https://github.com/symfony.png" alt="@symfony" />
|
||||
<twig:AvatarFallback>SF</twig:AvatarFallback>
|
||||
</twig:Avatar>
|
||||
|
||||
<twig:Avatar class="inline-block">
|
||||
<twig:AvatarFallback class="bg-blue-400">SF</twig:AvatarFallback>
|
||||
</twig:Avatar>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block package_install %}
|
||||
{% set installOnlyInDevMode = true %}
|
||||
{{ parent() }}
|
||||
<section class="container-fluid container-xxl px-4 pt-4 px-md-5 pt-md-5">
|
||||
<p>
|
||||
Then install your components with the following command:
|
||||
</p>
|
||||
|
||||
{% component Terminal with {bottomPadding: 20} %}
|
||||
{% block content %}
|
||||
php bin/console ux:toolkit:install badge
|
||||
php bin/console ux:toolkit:install button
|
||||
php bin/console ux:toolkit:install card
|
||||
php bin/console ux:toolkit:install ...
|
||||
{% endblock %}
|
||||
{% endcomponent %}
|
||||
|
||||
<p class="mt-4">
|
||||
Official components require tailwindcss to be installed in your project. Visit the <a class="text-underline" target="_blank" href="https://tailwindcss.com/docs/installation" target="_blank">Tailwind CSS documentation</a> for more information.
|
||||
Components are based on <a class="text-underline" target="_blank" href="https://ui.shadcn.com/" >shadcdn/ui</a> design system.
|
||||
</p>
|
||||
|
||||
<p class="mt-4">
|
||||
If you use component library using <code>html_cva()</code> or <code>tailwind_merge()</code> functions, remember to install them with:
|
||||
</p>
|
||||
|
||||
{% component Terminal with {bottomPadding: 20} %}
|
||||
{% block content %}
|
||||
composer require --dev tales-from-a-dev/twig-tailwind-extra twig/extra-bundle
|
||||
{% endblock %}
|
||||
{% endcomponent %}
|
||||
|
||||
|
||||
</section>
|
||||
<section class="container-fluid container-xxl px-4 pt-4 px-md-5 pt-md-5">
|
||||
<h2 class="ubuntu text-center mt-5">Philosophy</h2>
|
||||
<div class=" justify-content-center mt-5">
|
||||
<div class="col-12">
|
||||
<p>
|
||||
Toolkit provides ready-to-use components, but freely customizable.
|
||||
</p>
|
||||
<p>
|
||||
Official components are copied into your project in the <code>templates/components</code> folder, like classic TwigComponent.
|
||||
Feel free to modify the code to suit your needs.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-12 mt-4">
|
||||
<p>
|
||||
It is quite possible to install other component libraries, or even redistribute your own components
|
||||
(for example, if you want to provide your internal Design System).
|
||||
</p>
|
||||
<p>
|
||||
The choice of the component library is made in the configuration of your project:
|
||||
</p>
|
||||
|
||||
<twig:CodeBlock
|
||||
filename="config/packages/ux_toolkit.yaml"
|
||||
:showTwigExtends="false"
|
||||
language="yaml"
|
||||
showFilename="false"
|
||||
:copyButton="true"
|
||||
:stripExcessHtml="false"
|
||||
height="{{ height|default('auto') }}"
|
||||
style="--bg-header: rgba(13, 13, 13, 0.729); --header-opacity: .5;"
|
||||
/>
|
||||
|
||||
<p class="mt-4">
|
||||
The <code>theme</code> key allows you to choose the component library to use, and can be any GitHub repository.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mt-4">
|
||||
<p>
|
||||
If you want to distribute your own theme, please <a class="text-underline" target="_blank" href="https://docs.github.com/fr/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/classifying-your-repository-with-topics">
|
||||
classify your repository</a> with the <code>ux-toolkit</code> topic to be referenced easily.
|
||||
</p>
|
||||
<p>
|
||||
Your repository will be automatically detected by the <a class="text-underline" target="_blank" href="https://github.com/search?q=topic%3Aux-toolkit&type=repositories">GitHub search engine</a>.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{# some tailwind classes are overrided by current theme #}
|
||||
<style type="text/css">
|
||||
.bg-card {
|
||||
background-color: var(--color-white);
|
||||
}
|
||||
.text-primary-foreground {
|
||||
color: var(--color-white);
|
||||
}
|
||||
button.rounded-md {
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
107
templates/ux_packages/toolkit/components.html.twig
Normal file
107
templates/ux_packages/toolkit/components.html.twig
Normal file
@@ -0,0 +1,107 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<meta name="turbo-prefetch" content="false">
|
||||
{% endblock %}
|
||||
|
||||
{% set meta = {
|
||||
title: 'UX Toolkit - Build your Design System.',
|
||||
description: 'Collection of components and templates that you can use to build your pages.',
|
||||
canonical: url('app_toolkit_components'),
|
||||
social: {
|
||||
title: 'UX Toolkit - Build your Design System.',
|
||||
description: 'Collection of components and templates that you can use to build your pages.',
|
||||
image: {
|
||||
url: absolute_url(asset(package.getSocialImage('1200x675'))),
|
||||
type: 'image/png',
|
||||
width: 1200,
|
||||
height: 675,
|
||||
alt: package.humanName ~ ' - Component Toolkit',
|
||||
},
|
||||
}
|
||||
} %}
|
||||
|
||||
{% block header %}
|
||||
{{ include('_header.html.twig', {
|
||||
theme: 'white'
|
||||
}) }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% component PackageHeader with {
|
||||
package: package.name,
|
||||
eyebrowText: "Build your Design System.",
|
||||
installOnlyInDevMode: true
|
||||
} %}
|
||||
{% block title_header %}
|
||||
Build your Design System.
|
||||
{% endblock %}
|
||||
|
||||
{% block sub_content %}
|
||||
Collection of components and templates that you can use to build your pages.
|
||||
{% endblock %}
|
||||
{% endcomponent %}
|
||||
|
||||
<section class="container-fluid container-xxl px-4 px-md-5 py-3">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<h3>Components</h2>
|
||||
<div>
|
||||
{% for component in components %}
|
||||
{% if component.type.value == 'component' and component.parentName is null %}
|
||||
<a href="{{ url('app_toolkit_components', {currentComponent: component.name}) }}"
|
||||
class="d-block py-2 px-3 mb-2 rounded-3 text-decoration-none {% if currentComponent.name == component.name %}bg-primary text-white{% else %}text-dark{% endif %}">
|
||||
{{ component.name }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-8">
|
||||
<h3>{{ currentComponent.name }}</h3>
|
||||
<div class="pt-3">
|
||||
<h4>Installation</h4>
|
||||
<p>
|
||||
Run this command once to generate the <code>templates/components/{{ currentComponent.name|lower }}.html.twig</code> file in your project.
|
||||
</p>
|
||||
{% component Terminal with {bottomPadding: 20} %}
|
||||
{% block content %}
|
||||
php bin/console ux:toolkit:install {{ currentComponent.name }}
|
||||
{% endblock %}
|
||||
{% endcomponent %}
|
||||
</div>
|
||||
|
||||
{% if examples is not empty %}
|
||||
<div class="pt-5">
|
||||
<h4>Examples</h4>
|
||||
<p>
|
||||
Now you can directly use the following examples in your twig templates.
|
||||
</p>
|
||||
|
||||
{% for example in examples %}
|
||||
<div class="mt-4 mb-3">
|
||||
<h5>{{ example.name }}</h5>
|
||||
<iframe src="{{ url('app_toolkit_component_preview', {currentComponent: example.name}) }}"
|
||||
class="w-100 border-0 rounded-3 shadow-sm p-4"
|
||||
style="height: 400px;"></iframe>
|
||||
|
||||
<div class="mt-3">Your code:</div>
|
||||
<div class="pt-3">
|
||||
<twig:Code:CodeBlockEmbed code="{{ example.code|trim }}" language="html" highlight="true" />
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
5
templates/ux_packages/toolkit/preview.html.twig
Normal file
5
templates/ux_packages/toolkit/preview.html.twig
Normal file
@@ -0,0 +1,5 @@
|
||||
{% extends 'ux_toolkit_preview.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
{{ html | raw }}
|
||||
{% endblock %}
|
||||
91
templates/ux_toolkit_preview.html.twig
Normal file
91
templates/ux_toolkit_preview.html.twig
Normal file
@@ -0,0 +1,91 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{% block body %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{% if cssFramework|default('tailwindcss') == 'tailwindcss' %}
|
||||
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{% if cssFramework|default('tailwindcss') == 'tailwindcss' %}
|
||||
<style>
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 47.4% 11.2%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 47.4% 11.2%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 100% 50%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--ring: 215 20.2% 65.1%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
{# we use CDN for the moment. We compile the syles manually #}
|
||||
{% set classesToUses = [
|
||||
'background',
|
||||
'foreground',
|
||||
'muted',
|
||||
'muted-foreground',
|
||||
'popover',
|
||||
'popover-foreground',
|
||||
'border',
|
||||
'input',
|
||||
'card',
|
||||
'card-foreground',
|
||||
'primary',
|
||||
'primary-foreground',
|
||||
'secondary',
|
||||
'secondary-foreground',
|
||||
'accent',
|
||||
'accent-foreground',
|
||||
'destructive',
|
||||
'destructive-foreground',
|
||||
'ring',
|
||||
] %}
|
||||
{% for class in classesToUses %}
|
||||
.{{ class }} { background-color: hsl(var(--{{ class }})); }
|
||||
.bg-{{ class }} { background-color: hsl(var(--{{ class }})); }
|
||||
.text-{{ class }} { color: hsl(var(--{{ class }})); }
|
||||
|
||||
.hover:{{ class }} { background-color: hsl(var(--{{ class }})); }
|
||||
.focus:{{ class }} { background-color: hsl(var(--{{ class }})); }
|
||||
.active:{{ class }} { background-color: hsl(var(--{{ class }})); }
|
||||
|
||||
.hover:bg-{{ class }} { background-color: hsl(var(--{{ class }})); }
|
||||
.focus:bg-{{ class }} { background-color: hsl(var(--{{ class }})); }
|
||||
.active:bg-{{ class }} { background-color: hsl(var(--{{ class }})); }
|
||||
|
||||
.hover:text-{{ class }} { color: hsl(var(--{{ class }})); }
|
||||
.focus:text-{{ class }} { color: hsl(var(--{{ class }})); }
|
||||
.active:text-{{ class }} { color: hsl(var(--{{ class }})); }
|
||||
{% endfor %}
|
||||
</style>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block aside %}
|
||||
{% endblock %}
|
||||
|
||||
{% block meta %}
|
||||
{% endblock %}
|
||||
|
||||
{% block banner %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user