feat: Sostituito Laravel Mix con Vite

Vite permette di utilizzare i moduli ES6, in modo da caricare le views dei moduli custom tramite Import Maps (automatico). Gli assets devono essere inseriti nella cartella resources/static invece che nella cartella public.
Altri miglioramenti:
- I componenti sono stati migliorati, in modo da utilizzare collect.js (le collections di Laravel in JS) e classnames (per l'aggiunta di classi CSS ai componenti)
- Ogni cartella ha ora un file `index.js` così da poter importare facilmente i componenti nei moduli custom
- Possibilità di aggiungere un titolo nella pagina, definendolo nella route
-
This commit is contained in:
Maicol Battistini 2021-09-07 13:28:20 +02:00
parent 583ca50216
commit 2d37c8eda4
No known key found for this signature in database
GPG Key ID: 4FDB0F87CDB1D34A
66 changed files with 568 additions and 291 deletions

View File

@ -10,6 +10,8 @@
<data directive="@cannot" injection="true" prefix="&lt;?php if (app(\Illuminate\Contracts\Auth\Access\Gate::class)-&gt;denies(" suffix=")): ?&gt;" />
<data directive="@case" injection="true" prefix="&lt;?php case (" suffix="): ?&gt;" />
<data directive="@choice" injection="true" prefix="&lt;?php echo app('translator')-&gt;choice(" suffix="); ?&gt;" />
<data directive="@class" injection="true" prefix="class=&quot;&lt;?php echo \Illuminate\Support\Arr::toCssClasses(" suffix=")?&gt;&quot;" />
<data directive="@client" />
<data directive="@component" injection="true" prefix="&lt;?php $__env-&gt;startComponent(" suffix="); ?&gt;" />
<data directive="@componentfirst" injection="true" prefix="&lt;?php $__env-&gt;startComponentFirst(" suffix="); ?&gt;" />
<data directive="@continue" injection="true" prefix="&lt;?php if(" suffix=") continue; ?&gt;" />
@ -80,6 +82,7 @@
<data directive="@production" />
<data directive="@props" injection="true" prefix="&lt;?php $attributes = $attributes-&gt;exceptProps(" suffix="); ?&gt;" />
<data directive="@push" injection="true" prefix="&lt;?php $__env-&gt;startPush(" suffix="); ?&gt;" />
<data directive="@routes" />
<data directive="@section" injection="true" prefix="&lt;?php $__env-&gt;startSection(" suffix="); ?&gt;" />
<data directive="@sectionMissing" injection="true" prefix="&lt;?php if (empty(trim($__env-&gt;yieldContent(" suffix=")))): ?&gt;" />
<data directive="@show" />
@ -90,8 +93,9 @@
<data directive="@unless" injection="true" prefix="&lt;?php if (! (" suffix=")): ?&gt;" />
<data directive="@unset" injection="true" prefix="&lt;?php unset(" suffix="); ?&gt;" />
<data directive="@verbatim" />
<data directive="@vite" injection="true" prefix="&lt;?php echo $__env-&gt;yieldContent(" suffix="); ?&gt;" />
<data directive="@while" injection="true" prefix="&lt;?php while(" suffix="): ?&gt;" />
<data directive="@yield" injection="true" prefix="&lt;?php echo $__env-&gt;yieldContent(" suffix="); ?&gt;" />
</directives>
</component>
</project>
</project>

View File

@ -4,4 +4,4 @@
<file url="PROJECT" libraries="{@types/jquery, @types/mithril}" />
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>
</project>

View File

@ -3,4 +3,7 @@
<component name="JavaScriptSettings">
<option name="languageLevel" value="FLOW" />
</component>
<component name="ProjectResources">
<resource url="https://cdn.jsdelivr.net/npm/@mdi/font@5.9.55/css/materialdesignicons.min.css" location="$PROJECT_DIR$" />
</component>
</project>

View File

@ -91,10 +91,11 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/framework" />
<excludeFolder url="file://$MODULE_DIR$/vendor" />
<excludeFolder url="file://$MODULE_DIR$/.yarn/cache" />
<excludeFolder url="file://$MODULE_DIR$/public/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="@types/mithril" level="application" />
<orderEntry type="library" name="@types/jquery" level="application" />
</component>
</module>
</module>

View File

@ -109,10 +109,12 @@
<path value="$PROJECT_DIR$/vendor/doctrine/event-manager" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php81" />
<path value="$PROJECT_DIR$/vendor/jeroen-g/laravel-packager" />
<path value="$PROJECT_DIR$/vendor/openstamanger/tipi-attivita" />
<path value="$PROJECT_DIR$/vendor/tightenco/ziggy" />
<path value="$PROJECT_DIR$/vendor/mobiledetect/mobiledetectlib" />
<path value="$PROJECT_DIR$/vendor/riverskies/laravel-mobile-detect" />
<path value="$PROJECT_DIR$/vendor/spatie/laravel-package-tools" />
<path value="$PROJECT_DIR$/vendor/innocenzi/laravel-vite" />
<path value="$PROJECT_DIR$/vendor/openstamanager/tipi-attivita" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.0">

View File

@ -39,7 +39,9 @@
"laravel/tinker": "^2",
"nette/utils": "^3",
"riverskies/laravel-mobile-detect": "^1",
"tightenco/ziggy": "^1"
"tightenco/ziggy": "^1",
"innocenzi/laravel-vite": "^0.1.4",
"openstamanager/tipi-attivita": "@dev"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^2",

126
config/vite.php Normal file
View File

@ -0,0 +1,126 @@
<?php
/**
* List all the directories under a directory.
*
* @param string $dir Parent directory
* @param false $include_parent Include parent directory in the returned array
* @param bool $relative Use relative paths instead of absolute ones
* @source https://www.php.net/manual/en/function.glob.php#82182
*/
function listdirs(string $dir, bool $include_parent = false, bool $relative = true): array
{
$dirs = $include_parent ? [$dir] : [];
$next = 0;
while (true) {
$glob = glob($dir.'/*', GLOB_ONLYDIR);
if (count($glob) > 0) {
foreach ($glob as $_dir) {
$dirs[] = $_dir;
}
} else {
break;
}
$dir = $dirs[$next++];
}
return $relative ? array_unique(array_map(static fn ($dir) => ltrim(str_replace(base_path(), '', $dir), DIRECTORY_SEPARATOR), $dirs)) : $dirs;
}
return [
/*
|--------------------------------------------------------------------------
| Entrypoints
|--------------------------------------------------------------------------
| The files in the configured directories will be considered
| entry points and will not be required in the configuration file.
| To disable the feature, set to false.
*/
'entrypoints' => array_merge(
listdirs(resource_path('js'), true),
[resource_path('scss/app.scss')] // listdirs(resource_path('scss'), true)
),
'ignore_patterns' => ['/\\.d\\.ts$/'],
/*
|--------------------------------------------------------------------------
| Aliases
|--------------------------------------------------------------------------
| These aliases will be added to the Vite configuration and used
| to generate a proper tsconfig.json file.
*/
'aliases' => [
'@' => 'resources',
],
/*
|--------------------------------------------------------------------------
| Static assets path
|--------------------------------------------------------------------------
| This option defines the directory that Vite considers as the
| public directory. Its content will be copied to the build directory
| at build-time.
| https://vitejs.dev/config/#publicdir
*/
'public_directory' => resource_path('static'),
/*
|--------------------------------------------------------------------------
| Ping timeout
|--------------------------------------------------------------------------
| The maximum duration, in seconds, that the ping to the development
| server should take while trying to determine whether to use the
| manifest or the server in a local environment. Using false will disable
| the feature.
| https://laravel-vite.innocenzi.dev/guide/configuration.html#ping-timeout
*/
'ping_timeout' => .5,
/*
|--------------------------------------------------------------------------
| Build path
|--------------------------------------------------------------------------
| The directory, relative to /public, in which Vite will build
| the production files. This should match "build.outDir" in the Vite
| configuration file.
*/
'build_path' => 'build',
/*
|--------------------------------------------------------------------------
| Development URL
|--------------------------------------------------------------------------
| The URL at which the Vite development server runs.
| This is used to generate the script tags when developing.
*/
'dev_url' => 'http://localhost:3000',
/*
|--------------------------------------------------------------------------
| Inject asset-fixing plugin
|--------------------------------------------------------------------------
| Currently, Vite does not support loading assets from an URL other than
| the development server's URL. If this option is enabled, a plugin fixing
| this issue will be injected.
| See: https://github.com/innocenzi/laravel-vite/issues/31
*/
'asset_plugin' => [
'find_regex' => '/\/resources\/(.*)\.(svg|jp?g|png|webp)/',
'replace_with' => '/resources/$1.$2',
],
/*
|--------------------------------------------------------------------------
| Commands
|--------------------------------------------------------------------------
| Defines the list of artisan commands that will be executed when
| the development server starts.
*/
'commands' => [
'vite:aliases',
// 'typescript:generate'
],
];

View File

@ -1,15 +1,16 @@
{
"name": "openstamanager",
"packageManager": "yarn@3.0.0",
"version": "3.0.0",
"packageManager": "pnpm",
"private": true,
"main": "resources/js/index.js",
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
"build": "vite build",
"serve": "php artisan serve",
"watch": "vite build --watch",
"serve-watch": "concurrently --raw npm:serve npm:watch ",
"prepublishOnly": "pnpm run build",
"preinstall": "npx only-allow pnpm"
},
"dependencies": {
"@inertiajs/inertia": "^0.10.0",
@ -34,13 +35,15 @@
"@material/theme": "^12.0.0",
"@mdi/font": "^5.9.55",
"classnames": "^2.3.1",
"collect.js": "^4.28.6",
"jquery": "^3.6.0",
"lit-element": "^2.5.1",
"locale-code": "^2.0.2",
"lodash": "^4.17.21",
"mithril": "^2.0.4",
"mithril-node-render": "^3.0.1",
"modern-normalize": "^1.1.0"
"modern-normalize": "^1.1.0",
"openstamanager": "link:."
},
"devDependencies": {
"@babel/cli": "^7.14.8",
@ -50,8 +53,8 @@
"@babel/plugin-transform-react-jsx": "^7.14.9",
"@babel/preset-env": "^7.14.9",
"@babel/preset-flow": "^7.14.5",
"browser-sync": "^2.27.5",
"browser-sync-webpack-plugin": "2.3.0",
"@bunchtogether/vite-plugin-flow": "github:maicol07/vite-plugin-flow",
"concurrently": "^6.2.1",
"eslint": "^7.32.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-flowtype": "^5.8.2",
@ -70,6 +73,8 @@
"stylelint-config-recommended-scss": "^4.3.0",
"stylelint-config-standard": "^22.0.0",
"stylelint-scss": "^3.20.1",
"webpack": "^5.47.1"
"typescript": "^4.4.2",
"vite": "^2.5.3",
"vite-plugin-fonts": "^0.2.2"
}
}

View File

View File

@ -2,8 +2,11 @@ import Component from '../Component';
export default class Actions extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-card__actions', {
'mdc-card__actions--full-bleed': this.attrs.has('full-bleed')
});
return (
<div class={`mdc-card__actions ${vnode.attrs.fullbleed ? 'mdc-card__actions--full-bleed' : ''}`}>
<div {...this.attrs.all()}>
{vnode.children}
</div>
);

View File

@ -1,20 +0,0 @@
import Component from '../Component';
export default class Card extends Component {
view(vnode) {
return <div
class={
`mdc-card
${vnode.attrs.outlined ? 'mdc-card--outlined' : ''}
${vnode.attrs['columnspan-desktop'] ? `mdc-layout-grid__cell--span-${vnode.attrs['columnspan-desktop']}-desktop` : ''}
${vnode.attrs['columnspan-tablet'] ? `mdc-layout-grid__cell--span-${vnode.attrs['columnspan-tablet']}-tablet` : ''}
${vnode.attrs['columnspan-phone'] ? `mdc-layout-grid__cell--span-${vnode.attrs['columnspan-phone']}-phone` : ''}
${vnode.attrs.order ? `mdc-layout-grid__cell--order-${vnode.attrs.order}` : ''}
${vnode.attrs.align ? `mdc-layout-grid__cell--align-${vnode.attrs.align}` : ''}
${vnode.attrs.class ?? ''}`
}
style={vnode.attrs.style ?? ''}>
{vnode.children}
</div>;
}
}

View File

@ -0,0 +1,15 @@
import Component from '../Component';
export default class Card extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-card', {
'mdc-card--outlined': this.attrs.has('outlined')
});
return (
<div {...this.attrs.all()}>
{vnode.children}
</div>
);
}
}

View File

@ -2,8 +2,9 @@ import Component from '../Component';
export default class Content extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-card__content');
return (
<div class="mdc-card__content">
<div {...this.attrs.all()}>
{vnode.children}
</div>
);

View File

@ -1,14 +0,0 @@
import Component from '../Component';
export default class Media extends Component {
view(vnode) {
return (
<div class={
`mdc-card__media ${vnode.attrs.noscaling ? '' : 'mdc-card__media--16-9'}
${vnode.attrs.square ? 'mdc-card__media--square' : ''}`
} style={vnode.attrs.background ? `background-image: url("${vnode.attrs.background}");` : ''}>
<div class="mdc-card__media-content">{vnode.attrs.title ?? ''}</div>
</div>
);
}
}

View File

@ -0,0 +1,19 @@
import Component from '../Component';
export default class Media extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-card__media', {
'mdc-card__media--16-9': !this.attrs.has('no-scaling'),
'mdc-card__media--square': this.attrs.has('square')
});
if (this.attrs.has('background')) {
this.attrs.addStyles(`background-image: url("${this.attrs.get('background')}");`);
}
return (
<div {...this.attrs.all()}>
<div class="mdc-card__media-content">{this.attrs.get('title')}</div>
</div>
);
}
}

View File

@ -1,10 +1,12 @@
import Component from '../Component';
import '@material/mwc-ripple';
import Component from '../Component';
export default class PrimaryAction extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-card__primary-action');
return (
<div class="mdc-card__primary-action" tabindex="0">
<div {...this.attrs.all()} tabindex="0">
<mwc-ripple/>
{vnode.children}
</div>

6
resources/js/Components/Card/index.js vendored Normal file
View File

@ -0,0 +1,6 @@
export { default as Actions } from './Actions.jsx';
export { default as Card } from './Card.jsx';
export { default as Content } from './Content.jsx';
export { default as Media } from './Media.jsx';
export { default as PrimaryAction } from './PrimaryAction.jsx';

View File

@ -1,8 +1,16 @@
/* eslint-disable no-unused-vars */
import * as Mithril from 'mithril';
import classnames, {Argument as ClassNames} from 'classnames';
import collect, {Collection} from 'collect.js';
import m, {
Children, ClassComponent,
Vnode, VnodeDOM
} from 'mithril';
export interface ComponentAttrs extends Mithril.Attributes {}
interface Attributes extends Collection {
addClassNames(...classNames: ClassNames[]): void,
addStyles(...styles: string[]): void
}
// noinspection SpellCheckingInspection
/**
* @abstract
*
@ -31,9 +39,9 @@ export interface ComponentAttrs extends Mithril.Attributes {}
* @example
* return m('div', MyComponent.component({foo: 'bar'), m('p', 'Hello World!'));
*
* @see https://mithril.js.org/components.html
* @see https://js.org/components.html
*/
export default class Component implements ComponentAttrs {
export default class Component implements ClassComponent {
/**
* The root DOM element for the component.
*
@ -42,54 +50,58 @@ export default class Component implements ComponentAttrs {
element: Element;
/**
* The attributes passed into the component.
* The attributes passed into the component. They are transformed into a collection by initAttrs.
*
* @method <string> addClassNames()
*
* @see https://js.org/components.html#passing-data-to-components
* @see initAttrs
*
* @see https://mithril.js.org/components.html#passing-data-to-components
* @protected
*/
attrs;
attrs: Attributes;
/**
* @inheritdoc
* @abstract
*/
view(vnode: Mithril.Vnode): Mithril.Children {}
view(vnode: Vnode): Children {}
/**
* @inheritdoc
*/
oninit(vnode: Mithril.Vnode) {
oninit(vnode: Vnode) {
this.setAttrs(vnode.attrs);
}
/**
* @inheritdoc
*/
oncreate(vnode: Mithril.VnodeDOM) {
oncreate(vnode: VnodeDOM) {
this.element = vnode.dom;
}
/**
* @inheritdoc
*/
onbeforeupdate(vnode: Mithril.VnodeDOM) {
onbeforeupdate(vnode: VnodeDOM) {
this.setAttrs(vnode.attrs);
}
/**
* @inheritdoc
*/
onupdate(vnode: Mithril.VnodeDOM) {}
onupdate(vnode: VnodeDOM) {}
/**
* @inheritdoc
*/
onbeforeremove(vnode: Mithril.VnodeDOM) {}
onbeforeremove(vnode: VnodeDOM) {}
/**
* @inheritdoc
*/
onremove(vnode: Mithril.VnodeDOM) {}
onremove(vnode: VnodeDOM) {}
/**
* Returns a jQuery object for this component's element. If you pass in a
@ -105,22 +117,22 @@ export default class Component implements ComponentAttrs {
* @final
* @protected
*/
/* $(selector?: string): JQuery {
$(selector?: string): JQuery {
const $element: JQuery<HTMLElement> = $(this.element);
return selector ? $element.find(selector) : $element;
}; */
return selector ? $element.find((element) => selector(element)) : $element;
}
/**
* Convenience method to attach a component without JSX.
* Has the same effect as calling `m(THIS_CLASS, attrs, children)`.
*
* @see https://mithril.js.org/hyperscript.html#mselector,-attributes,-children
* @see https://js.org/hyperscript.html#mselector,-attributes,-children
*/
static component(attrs = {}, children = null): Mithril.Vnode {
const componentAttrs: Record<string, unknown> = { ...attrs};
static component(attributes = {}, children): Vnode {
const componentAttributes: Record<string, any> = { ...attributes};
return Mithril.m(this, componentAttrs, children);
return m(this, componentAttributes, children);
}
/**
@ -129,17 +141,32 @@ export default class Component implements ComponentAttrs {
*
* @private
*/
setAttrs(attrs = {}): void {
this.initAttrs(attrs);
if (attrs) {
if ('children' in attrs) {
setAttrs(attributes: Object = {}): void {
this.initAttrs(attributes);
if (attributes) {
if ('children' in attributes) {
// noinspection JSUnresolvedVariable
throw new Error(`[${this.constructor.name}] The "children" attribute of attrs should never be used. Either pass children in as the vnode children or rename the attribute`);
}
if ('tag' in attrs) {
if ('tag' in attributes) {
// noinspection JSUnresolvedVariable
throw new Error(`[${this.constructor.name}] You cannot use the "tag" attribute name with Mithril 2.`);
}
}
this.attrs = attrs;
this.attrs = collect(attributes);
this.attrs.macro('addClassNames', (...classNames: ClassNames[]) => {
this.attrs.put('className', classnames(this.attrs.get('className'), ...classNames));
});
this.attrs.macro('addStyles', (...styles: string[]) => {
let s: string = this.attrs.get('style', '');
if (!s.trimEnd().endsWith(';')) {
s += '; ';
}
s += styles.join('; ');
this.attrs.put('style', s);
});
}
/**
@ -149,5 +176,5 @@ export default class Component implements ComponentAttrs {
*
* @protected
*/
initAttrs(attrs): void {}
initAttrs(attributes: Object): void {}
}

View File

@ -1,18 +1,19 @@
import '@material/mwc-linear-progress';
import '@material/mwc-list/mwc-list-item';
import '@material/mwc-select';
import Component from '../Component';
import Mdi from '../Mdi';
export default class DataTable extends Component {
view(vnode) {
return <div className="mdc-data-table" {...vnode.attrs}>
return <div className="mdc-data-table" {...this.attrs.all()}>
<div className="mdc-data-table__table-container">
<table className="mdc-data-table__table" aria-label={vnode.attrs['aria-label']}>
{vnode.children}
</table>
{vnode.attrs.paginated ? <div className="mdc-data-table__pagination">
{this.attrs.has('paginated') ? <div className="mdc-data-table__pagination">
<div className="mdc-data-table__pagination-trailing">
<div className="mdc-data-table__pagination-rows-per-page">
<div className="mdc-data-table__pagination-rows-per-page-label">
@ -20,6 +21,8 @@ export default class DataTable extends Component {
</div>
<mwc-select className="mdc-data-table__pagination-rows-per-page-select">
{/* TODO: Rendere dinamico (permetti a chi chiama il componente di
scegliere i valori da visualizzare */}
<mwc-list-item value="10">10</mwc-list-item>
<mwc-list-item value="25">25</mwc-list-item>
<mwc-list-item value="50">50</mwc-list-item>
@ -30,6 +33,7 @@ export default class DataTable extends Component {
<div className="mdc-data-table__pagination-navigation">
<div className="mdc-data-table__pagination-total">
{/* TODO: Aggiungere i18n */}
110 di 100
</div>
<mwc-icon-button className="mdc-data-table__pagination-button" data-page="first" disabled>
@ -54,6 +58,5 @@ export default class DataTable extends Component {
</div>
</div>
</div>;
// TODO: Inserire traduzioni
}
}

View File

@ -2,6 +2,6 @@ import Component from '../Component';
export default class TableBody extends Component {
view(vnode) {
return <tbody {...vnode.attrs}>{vnode.children}</tbody>;
return <tbody {...this.attrs.all()}>{vnode.children}</tbody>;
}
}

View File

@ -1,13 +0,0 @@
import classnames from 'classnames';
import Component from '../Component';
/**
* Attributes:
* - type: numeric, checkbox
*/
export default class TableCell extends Component {
view(vnode) {
this.attrs.className = classnames('mdc-data-table__cell', vnode.attrs.className, `mdc-data-table__cell--${vnode.attrs.type}`);
return <td {...this.attrs}>{vnode.children}</td>;
}
}

View File

@ -0,0 +1,12 @@
import Component from '../Component';
/**
* Attributes:
* - type: numeric, checkbox
*/
export default class TableCell extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-data-table__cell', `mdc-data-table__cell--${this.attrs.get('type')}`);
return <td {...this.attrs.all()}>{vnode.children}</td>;
}
}

View File

@ -0,0 +1,7 @@
import Component from '../Component';
export default class TableFooter extends Component {
view(vnode) {
return <tfoot {...this.attrs.all()}>{vnode.children}</tfoot>;
}
}

View File

@ -2,6 +2,6 @@ import Component from '../Component';
export default class TableHead extends Component {
view(vnode) {
return <thead {...vnode.attrs}>{vnode.children}</thead>;
return <thead {...this.attrs.all()}>{vnode.children}</thead>;
}
}

View File

@ -1,15 +0,0 @@
import classnames from 'classnames';
import Component from '../Component';
/**
* Attributes:
* - type: numeric, checkbox
*/
export default class TableHeadCell extends Component {
view(vnode) {
this.attrs.className = classnames('mdc-data-table__header-cell', vnode.attrs.className, {
[`mdc-data-table__header-cell--${vnode.attrs.type}`]: vnode.attrs.type
});
return <th {...this.attrs} role="columnheader" scope="col">{vnode.children}</th>;
}
}

View File

@ -0,0 +1,14 @@
import Component from '../Component';
/**
* Attributes:
* - type: numeric, checkbox
*/
export default class TableHeadCell extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-data-table__header-cell', {
[`mdc-data-table__header-cell--${this.attrs.get('type')}`]: this.attrs.has('type')
});
return <th {...this.attrs.all()} role="columnheader" scope="col">{vnode.children}</th>;
}
}

View File

@ -1,9 +0,0 @@
import classnames from 'classnames';
import Component from '../Component';
export default class TableHeadRow extends Component {
view(vnode) {
this.attrs.className = classnames('mdc-data-table__header-row', vnode.attrs.className);
return <tr {...this.attrs}>{vnode.children}</tr>;
}
}

View File

@ -0,0 +1,8 @@
import Component from '../Component';
export default class TableHeadRow extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-data-table__header-row');
return <tr {...this.attrs.all()}>{vnode.children}</tr>;
}
}

View File

@ -1,9 +0,0 @@
import classnames from 'classnames';
import Component from '../Component';
export default class TableRow extends Component {
view(vnode) {
this.attrs.className = classnames('mdc-data-table__row', vnode.attrs.className);
return <tr {...this.attrs}>{vnode.children}</tr>;
}
}

View File

@ -0,0 +1,8 @@
import Component from '../Component';
export default class TableRow extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-data-table__row');
return <tr {...this.attrs.all()}>{vnode.children}</tr>;
}
}

View File

@ -0,0 +1,9 @@
export { default as DataTable } from './DataTable.jsx';
export { default as TableBody } from './TableBody.jsx';
export { default as TableCell } from './TableCell.jsx';
export { default as TableFooter } from './TableFooter.jsx';
export { default as TableHead } from './TableHead.jsx';
export { default as TableHeadCell } from './TableHeadCell.jsx';
export { default as TableHeadRow } from './TableHeadRow.jsx';
export { default as TableRow } from './TableRow.jsx';

View File

@ -1,20 +0,0 @@
import Component from '../Component';
export default class Cell extends Component {
view(vnode) {
return <div
class={
`mdc-layout-grid__cell
${vnode.attrs.columnspan ? `mdc-layout-grid__cell--span-${vnode.attrs.columnspan}` : ''}
${vnode.attrs['columnspan-desktop'] ? `mdc-layout-grid__cell--span-${vnode.attrs['columnspan-desktop']}-desktop` : ''}
${vnode.attrs['columnspan-tablet'] ? `mdc-layout-grid__cell--span-${vnode.attrs['columnspan-tablet']}-tablet` : ''}
${vnode.attrs['columnspan-phone'] ? `mdc-layout-grid__cell--span-${vnode.attrs['columnspan-phone']}-phone` : ''}
${vnode.attrs.order ? `mdc-layout-grid__cell--order-${vnode.attrs.order}` : ''}
${vnode.attrs.align ? `mdc-layout-grid__cell--align-${vnode.attrs.align}` : ''}
${vnode.attrs.class ?? ''}`
}
{...vnode.attrs}>
{vnode.children}
</div>;
}
}

View File

@ -0,0 +1,23 @@
import Component from '../Component';
export default class Cell extends Component {
view(vnode) {
const spans = [];
for (const device of ['desktop', 'tablet', 'phone']) {
const key = `columnspan-${device}`;
if (this.attrs.has(key)) {
spans.push(`mdc-layout-grid__cell--span-${this.attrs.get(key)}-${device}`);
}
}
this.attrs.addClassNames('mdc-layout-grid__cell', {
[`mdc-layout-grid__cell--span-${this.attrs.columnspan}`]: this.attrs.has('columnspan'),
[`mdc-layout-grid__cell--order-${this.attrs.get('order')}`]: this.attrs.has('order'),
[`mdc-layout-grid__cell--align-${this.attrs.get('align')}`]: this.attrs.has('align')
}, spans);
return <div {...this.attrs.all()}>
{vnode.children}
</div>;
}
}

View File

@ -1,15 +0,0 @@
import Component from '../Component';
export default class LayoutGrid extends Component {
view(vnode) {
return <div
class={
`mdc-layout-grid ${vnode.attrs.fixed ? 'mdc-layout-grid--fixed-column-width' : ''}
${vnode.attrs.align ? `mdc-layout-grid--align-${vnode.attrs.align}` : ''}
${vnode.attrs.class}`
}
style={vnode.attrs.style}>
{vnode.children}
</div>;
}
}

View File

@ -0,0 +1,14 @@
import Component from '../Component';
export default class LayoutGrid extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-layout-grid', {
'mdc-layout-grid--fixed-column-width': this.attrs.has('fixed'),
[`mdc-layout-grid--align-${this.attrs.get('align')}`]: this.attrs.has('align')
});
return <div {...this.attrs.all()}>
{vnode.children}
</div>;
}
}

View File

@ -1,7 +0,0 @@
import Component from '../Component';
export default class Row extends Component {
view(vnode) {
return <div {...vnode.attrs} class={`mdc-layout-grid__inner ${vnode.attrs.class}`}>{vnode.children}</div>;
}
}

View File

@ -0,0 +1,8 @@
import Component from '../Component';
export default class Row extends Component {
view(vnode) {
this.attrs.addClassNames('mdc-layout-grid__inner');
return <div {...this.attrs.all()}>{vnode.children}</div>;
}
}

4
resources/js/Components/Grid/index.js vendored Normal file
View File

@ -0,0 +1,4 @@
export { default as Cell } from './Cell.jsx';
export { default as LayoutGrid } from './LayoutGrid.jsx';
export { default as Row } from './Row.jsx';

View File

@ -1,14 +0,0 @@
import Component from './Component';
export default class Mdi extends Component {
view(vnode) {
return <i {...vnode.attrs} class={`mdi mdi-${vnode.attrs.icon} ${vnode.attrs.class ?? ''}`} />;
/*
Quando MWC supporterà pienamente le icone SVG potremo
import * as mdi from '@mdi/js';
import {camelCase} from 'lodash/string';
return <svg class={`mdi ${vnode.attrs.class ?? ''}`} {...vnode.attrs} viewBox={vnode.attrs.viewBox ?? '0 0 24 24'}>
<path d={vnode.attrs.icon ? mdi[camelCase(`mdi-${vnode.attrs.icon}`)] : ''} />
</svg>; */
}
}

View File

@ -0,0 +1,19 @@
import Component from './Component';
export default class Mdi extends Component {
view(vnode) {
this.attrs.addClassNames('mdi', `mdi-${vnode.attrs.icon}`);
return <i {...this.attrs.all()} />;
}
}
/*
Quando MWC supporterà pienamente le icone SVG potremo fare così:
import * as mdi from '@mdi/js';
import {camelCase} from 'lodash/string';
return <svg class={`mdi ${vnode.attrs.class ?? ''}`}
{...vnode.attrs} viewBox={vnode.attrs.viewBox ?? '0 0 24 24'}>
<path d={vnode.attrs.icon ? mdi[camelCase(`mdi-${vnode.attrs.icon}`)] : ''} />
</svg>;
*/

View File

@ -1,5 +1,6 @@
import * as Mithril from 'mithril';
import * as render from 'mithril-node-render';
import {Vnode} from 'mithril';
import {sync as render} from 'mithril-node-render';
import Component from './Component';
/**
@ -21,21 +22,22 @@ export default class Page extends Component {
/**
* Ritorna una traduzione
*
* @param {string|Mithril.Vnode} key Stringa di cui prelevare la traduzione
* @param {string|Vnode} key Stringa di cui prelevare la traduzione
* @param {Object|boolean} replace Eventuali parametri da rimpiazzare.
* Se il parametro è "true" (valore booleano), verrà ritornato il valore come stringa
* (stesso funzionamento del parametro dedicato (sotto ))
* @param {boolean} returnAsString Se impostato a "true" vien ritornata una stringa invece di
* un Vnode di Mithril
* @returns {Mithril.Vnode}
*
* @returns {Vnode}
*
* @protected
*/
__(
key: string | Mithril.Vnode,
key: string | Vnode,
replace: Object | boolean = {},
returnAsString: boolean = false
): Mithril.Vnode {
): Vnode {
let translation = (this.page.translations && this.page.translations[key])
? this.page.translations[key] : key;
@ -44,11 +46,11 @@ export default class Page extends Component {
return translation;
}
Object.keys(replace).forEach(async (k: string) => {
// "'attrs' in replace[k]" controlla se replace[k] è un vnode di Mithril
translation = translation.replace(`:${k}`, ((typeof replace[k] === 'object' && 'attrs' in replace[k]) ? render.sync(replace[k]) : replace[k]));
});
for (const k of Object.keys(replace)) {
// `'attrs' in replace[k]` controlla se replace[k] è un vnode di Mithril
translation = translation.replace(`:${k}`, ((typeof replace[k] === 'object' && 'attrs' in replace[k]) ? render(replace[k]) : replace[k]));
}
return returnAsString ? translation : Mithril.m.trust(translation);
return returnAsString ? translation : window.m.trust(translation);
}
}

View File

@ -1,11 +1,11 @@
import Page from './Page';
import DataTable from './DataTable/DataTable';
import TableHeadCell from './DataTable/TableHeadCell';
import TableHead from './DataTable/TableHead';
import TableHeadRow from './DataTable/TableHeadRow';
import TableBody from './DataTable/TableBody';
import TableRow from './DataTable/TableRow';
import TableCell from './DataTable/TableCell';
import DataTable from '../DataTable/DataTable';
import TableBody from '../DataTable/TableBody';
import TableCell from '../DataTable/TableCell';
import TableHead from '../DataTable/TableHead';
import TableHeadCell from '../DataTable/TableHeadCell';
import TableHeadRow from '../DataTable/TableHeadRow';
import TableRow from '../DataTable/TableRow';
import Page from '../Page';
/**
* @abstract
@ -21,16 +21,16 @@ export default class ListPage extends Page {
view(vnode) {
const columns = this.columns.map(
(column, i) => (
<TableHeadCell key={i} type={null}>
(column, index) => (
<TableHeadCell key={index}>
{column}
</TableHeadCell>
)
);
const rows = this.rows.length ? this.rows.map((row, i) => (
<TableRow key={i}>
{row.map((cell, index) => <TableCell key={index}>{cell}</TableCell>)}
const rows = this.rows.length > 0 ? this.rows.map((row, index) => (
<TableRow key={index}>
{row.map((cell, index_) => <TableCell key={index_}>{cell}</TableCell>)}
</TableRow>
)) : <TableRow><TableCell colspan={columns.length}>{this.__('Non sono presenti dati')}</TableCell></TableRow>;

8
resources/js/Components/index.js vendored Normal file
View File

@ -0,0 +1,8 @@
export * from './Card';
export { default as Component } from './Component.jsx';
export * from './DataTable';
export { extend, override } from './extend';
export * from './Grid';
export { default as ListPage } from './Pages/ListPage.jsx';
export { default as Mdi } from './Mdi.jsx';
export { default as Page } from './Page.jsx';

View File

@ -6,30 +6,32 @@ import '@material/mwc-list/mwc-list-item';
import '@material/mwc-select';
import '@material/mwc-textarea';
import '@material/mwc-textfield';
import collect from 'collect.js';
import LocaleCode from 'locale-code';
import Mithril from 'mithril';
import Page from '../Components/Page';
import LayoutGrid from '../Components/Grid/LayoutGrid';
import Row from '../Components/Grid/Row';
import Cell from '../Components/Grid/Cell';
import Mdi from '../Components/Mdi';
import logoUrl from '@/static/images/logo_completo.png';
import Card from '../Components/Card/Card';
import Content from '../Components/Card/Content';
import Cell from '../Components/Grid/Cell';
import LayoutGrid from '../Components/Grid/LayoutGrid';
import Row from '../Components/Grid/Row';
import Mdi from '../Components/Mdi';
import Page from '../Components/Page';
export default class SetupPage extends Page {
// eslint-disable-next-line no-unused-vars
view(vnode) {
const listItems: array[Mithril.Vnode] = [];
const listItems: Array[Mithril.Vnode] = [];
// noinspection JSUnresolvedVariable
this.page.props.languages.forEach((lang) => {
const prop = {
for (const lang of this.page.props.languages) {
const attributes = {
selected: this.page.props.locale === lang
};
const langCode = lang.replace('_', '-');
listItems.push(
<mwc-list-item graphic="icon" value={lang} {...prop}>
<mwc-list-item graphic="icon" value={lang} {...attributes}>
<img
slot="graphic"
style="border-radius: 4px;"
@ -39,22 +41,28 @@ export default class SetupPage extends Page {
<span>{LocaleCode.getLanguageNativeName(langCode)}</span>
</mwc-list-item>
);
});
}
const examplesTexts = collect();
for (const example of ['localhost', 'root', 'mysql', 'openstamanager']) {
examplesTexts.put(example, this.__('Esempio: :example', {example}, true));
}
return (
<>
<Card outlined class="center" style="width: 85%;">
<Card outlined className="center" style="width: 85%;">
<Content>
<img src="images/logo_completo.png" class="center" alt={this.__('OpenSTAManager')} />
<img src={logoUrl} className="center" alt={this.__('OpenSTAManager')} />
<LayoutGrid>
<Row>
<Cell columnspan-desktop="8">
<h2>{this.__('Benvenuto in :name!', {name: <strong>{this.__('OpenSTAManager')}</strong>})}</h2>
<p>{this.__('Puoi procedere alla configurazione tecnica del software attraverso i '
+ 'parametri seguenti, che potranno essere corretti secondo necessità tramite il file .env.')}<br/>
{this.__("Se necessiti supporto puoi contattarci tramite l':contact_link o tramite il nostro :forum_link.", {
contact_link: <a href="https://www.openstamanager.com/contattaci/?subject=Assistenza%20installazione%20OSM">{this.__('assistenza ufficiale')}</a>,
forum_link: <a href="https://forum.openstamanager.com">{this.__('forum')}</a>
{this.__("Se necessiti supporto puoi contattarci tramite l':contactLink o tramite il nostro :forumLink.", {
// eslint-disable-next-line no-secrets/no-secrets
contactLink: <a href="https://www.openstamanager.com/contattaci/?subject=Assistenza%20installazione%20OSM">{this.__('assistenza ufficiale')}</a>,
forumLink: <a href="https://forum.openstamanager.com">{this.__('forum')}</a>
})}</p>
<h4>{this.__('Formato date')}</h4>
<small>
@ -77,16 +85,16 @@ export default class SetupPage extends Page {
<h4>{this.__('Database')}</h4>
<Row>
<Cell columnspan="4">
<mwc-textfield name="host" label={this.__('Host', true)} required helper={this.__('Esempio: :example', {example: 'localhost'}, true)} />
<mwc-textfield name="host" label={this.__('Host', true)} required helper={examplesTexts.get('localhost')} />
</Cell>
<Cell columnspan="4">
<mwc-textfield name="username" label={this.__('Nome utente', true)} required helper={this.__('Esempio: :example', {example: 'root'}, true)} />
<mwc-textfield name="username" label={this.__('Nome utente', true)} required helper={examplesTexts.get('root')} />
</Cell>
<Cell columnspan="4">
<mwc-textfield name="password" label={this.__('Password', true)} required helper={this.__('Esempio: :example', {example: 'mysql'}, true)} />
<mwc-textfield name="password" label={this.__('Password', true)} required helper={examplesTexts.get('mysql')} />
</Cell>
<Cell columnspan="4">
<mwc-textfield name="database_name" label={this.__('Nome database', true)} required helper={this.__('Esempio: :example', {example: 'openstamanager'}, true)} />
<mwc-textfield name="database_name" label={this.__('Nome database', true)} required helper={examplesTexts.get('openstamanager')} />
</Cell>
</Row>
<hr/>
@ -134,8 +142,8 @@ export default class SetupPage extends Page {
</LayoutGrid>
</Content>
</Card>
<mwc-fab id="contrast-switcher" class="sticky contrast-light" label={this.__('Attiva/disattiva contrasto elevato', true)}>
<Mdi icon="contrast-circle" slot="icon" class="light-bg"/>
<mwc-fab id="contrast-switcher" className="sticky contrast-light" label={this.__('Attiva/disattiva contrasto elevato', true)}>
<Mdi icon="contrast-circle" slot="icon" className="light-bg"/>
</mwc-fab>
</>
);

2
resources/js/Views/index.js vendored Normal file
View File

@ -0,0 +1,2 @@
export { default as SetupPage } from './SetupPage.jsx';

3
resources/js/WebComponents/index.js vendored Normal file
View File

@ -0,0 +1,3 @@
export { default as Drawer } from './MaterialDrawer';
export { default as TopAppBar } from './TopAppBar';

34
resources/js/app.js vendored
View File

@ -1,12 +1,36 @@
import m from 'mithril';
import {createInertiaApp} from '@maicol07/inertia-mithril';
import '../scss/app.scss';
import './_material';
import {createInertiaApp} from '@maicol07/inertia-mithril';
import {waitUntil} from 'async-wait-until';
import jQuery from 'jquery';
import m from 'mithril';
// Fix Mithril JSX durante la compilazione
m.Fragment = '[';
// Variabili globali
window.$ = jQuery;
window.jQuery = jQuery;
window.m = m;
// noinspection JSIgnoredPromiseFromCall
createInertiaApp({
title: (title) => `${title} - OpenStaManager`,
resolve: async (name) => import(`./Views/${name}`),
title: title => `${title} - OpenSTAManager`,
resolve: async (name) => {
const split = name.split('::');
if (split.length === 1) {
return (await import(`./Views/${name}.jsx`)).default;
}
const [, page] = split;
// noinspection JSUnresolvedVariable
await waitUntil(() => typeof window.extmodule !== 'undefined');
// noinspection JSUnresolvedVariable
return window.extmodule[page];
},
setup({ el, app }) {
m.mount(el, app);
},
}
});

4
resources/js/index.js vendored Normal file
View File

@ -0,0 +1,4 @@
export * from './Components';
export { default as utils } from './utils';
export * from './Views';
export * from './WebComponents';

View File

@ -1,12 +1,13 @@
@use "variables";
@use '~@material/theme' with (
@use '@material/theme' with (
$primary: variables.$primary,
$secondary: variables.$secondary
);
@use "~@material/card";
@use "~@material/layout-grid/mdc-layout-grid";
@use "~@material/data-table/data-table";
@use "~@mdi/font";
@use "@material/card";
@use "@material/layout-grid/mdc-layout-grid";
@use "@material/data-table/data-table";
@import "https://cdn.jsdelivr.net/npm/@mdi/font@5.9.55/css/materialdesignicons.min.css";
@include card.core-styles;
@include data-table.core-styles;

View File

@ -1,6 +1,6 @@
@use 'variables';
@import '~modern-normalize/modern-normalize.css';
@import 'modern-normalize/modern-normalize.css';
@import 'material';
h1,

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 520 B

View File

Before

Width:  |  Height:  |  Size: 711 B

After

Width:  |  Height:  |  Size: 711 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -22,6 +22,7 @@ Route::inertia('setup', 'SetupPage', [
glob(resource_path('lang').'/*.json')
),
'license' => file_get_contents(base_path('LICENSE')),
'title' => __('Configurazione'),
]);
Route::get('lang/{language}', function ($language) {

57
vite.config.js vendored Normal file
View File

@ -0,0 +1,57 @@
import { esbuildFlowPlugin, flowPlugin } from '@bunchtogether/vite-plugin-flow';
import { defineConfig } from 'laravel-vite';
// import ViteFonts from 'vite-plugin-fonts';
export default defineConfig({
assetsInclude: ['js', 'png'],
/* NOT WORKING: waiting a fix
css: {
preprocessorOptions: {
scss: {
additionalData: '$mdi-font-path: "./build";',
},
},
}, */
build: {
minify: false,
rollupOptions: {
manualChunks: {},
output: {
entryFileNames: '[name].js',
chunkFileNames: '[name].js',
assetFileNames: '[name].[ext]'
},
preserveEntrySignatures: 'allow-extension'
}
},
esbuild: {
jsxFactory: 'm',
jsxFragment: 'm.Fragment'
},
optimizeDeps: {
esbuildOptions: {
plugins: [esbuildFlowPlugin(/\.(flow|jsx?)$/, (path) => (/\.jsx$/.test(path) ? 'jsx' : 'js'), {
all: true,
pretty: true,
ignoreUninitializedFields: false,
})]
},
},
plugins: [
flowPlugin({
include: /\.(flow|jsx?)$/,
exclude: /node_modules/,
flow: {
all: true,
pretty: true,
ignoreUninitializedFields: false,
}
})
/* NOT WORKING. Waiting a fix
ViteFonts({
google: {
families: ['Montserrat', 'Nunito']
}
}) */
]
});

42
webpack.mix.js vendored
View File

@ -1,42 +0,0 @@
/* eslint-disable import/no-extraneous-dependencies */
// noinspection JSUnresolvedFunction
const mix = require('laravel-mix');
require('laravel-mix-versionhash');
require('laravel-mix-serve');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel applications. By default, we are compiling the CSS
| file for the application as well as bundling up all the JS files.
|
*/
mix.disableSuccessNotifications();
mix.js('resources/js/app.js', 'public/js')
.sass('resources/scss/app.scss', 'public/css', {
sassOptions: {
includePaths: ['./node_modules'],
},
}).extract();
mix.autoload({
jquery: ['$', 'global.$', 'window.$', 'jQuery', 'window.jQuery', 'global.jQuery'],
mithril: ['m']
});
if (mix.inProduction()) {
mix.versionHash();
} else {
// noinspection ChainedFunctionCallJS
mix.webpackConfig({
devtool: 'source-map',
resolve: {
modules: ['./node_modules'],
},
}).sourceMaps().serve().browserSync('localhost:8000');
}