Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

Contributte UI

Installation

composer require contributte/ui

Usage

Bundler

services:
	latte.latteFactory:
		setup:
			- addExtension(App\Model\ViteExtension(%wwwDir%/dist/manifest.json, /dist))

CSP Nonce Support

The Vite extension automatically supports Content Security Policy nonces via the uiNonce variable provided by nette/application.

{vitejs 'assets/js/app.js'}
{vitecss 'assets/js/app.js'}

When nonce is available, it's automatically injected:

<script type="module" src="/dist/app.js" nonce="..."></script>
<link rel="stylesheet" href="/dist/app.css" nonce="...">

To use nonce support, ensure your Nette application sets the nonce value in presenter or middleware:

$nonce = base64_encode(random_bytes(16));
$this->template->uiNonce = $nonce;
header("Content-Security-Policy: script-src 'nonce-{$nonce}'; style-src 'nonce-{$nonce}'");

Paginator

Register the factory:

services:
	- Contributte\UI\Paginator\PaginatorControlFactory

Create a data provider:

use Contributte\UI\Paginator\ArrayDataProvider;
use Contributte\UI\Paginator\PaginatorDataProvider;
use Nette\Utils\Paginator;

// Using built-in ArrayDataProvider
$provider = new ArrayDataProvider($data);

// Or implement your own
class MyDataProvider implements PaginatorDataProvider
{
	public function page(Paginator $paginator): array
	{
		$paginator->setItemCount($this->getTotalCount());
		return $this->fetchItems($paginator->getOffset(), $paginator->getLength());
	}
}

Use in presenter:

use Contributte\UI\Paginator\PaginatorControlFactory;

class ArticlePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private PaginatorControlFactory $paginatorFactory,
	) {}

	protected function createComponentPaginator(): PaginatorControl
	{
		$provider = new ArrayDataProvider($this->articles);
		$control = $this->paginatorFactory->create($provider, itemsPerPage: 10);
		$control->onPagination[] = fn() => $this->redrawControl('articles');
		return $control;
	}

	public function renderDefault(): void
	{
		$this->template->articles = $this['paginator']->getPage();
	}
}

Render in template:

<div n:snippet="articles">
	<div n:foreach="$articles as $article">...</div>
	{control paginator}
</div>

Custom template:

$control->setTemplateFile(__DIR__ . '/templates/myPaginator.latte');

Built-in templates: bootstrap4.latte, bootstrap5.latte (default).

Examples

Vite

import { defineConfig } from 'vite';
import { resolve } from 'path';

export default defineConfig(({ mode }) => {
	const DEV = mode === 'development';

	return {
		resolve: {
			alias: {
				'@': resolve(__dirname, 'assets/js'),
				'~': resolve(__dirname, 'node_modules'),
			},
		},
		server: {
			open: false,
			hmr: false,
		},
		build: {
			manifest: true,
			assetsDir: '',
			outDir: './www/dist/',
			emptyOutDir: false,
			minify: DEV ? false : 'esbuild',
			rollupOptions: {
				output: {
					manualChunks: undefined,
					chunkFileNames: DEV ? '[name].js' : '[name]-[hash].js',
					entryFileNames: DEV ? '[name].js' : '[name].[hash].js',
					assetFileNames: DEV ? '[name].[ext]' : '[name].[hash].[ext]',
				},
				input: {
					app: './assets/js/app.js'
				}
			}
		},
	}
});

Thanks for testing, reporting and contributing.