Skip to content

Commit

Permalink
Assets: rewrite to TS and plugin system
Browse files Browse the repository at this point in the history
  • Loading branch information
f3l1x committed Jun 7, 2023
1 parent fd8691c commit dca24b4
Show file tree
Hide file tree
Showing 40 changed files with 2,424 additions and 1,307 deletions.
185 changes: 86 additions & 99 deletions .docs/assets.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,116 +6,103 @@ Table of contents

# Assets

Datagrid needs for its precise functionality some third party scripts and styles. Install all required assets with NPM.
There are prepare JS/TS and CSS files for precise functionality. The best way is to use some frontend bundler, for example [Vite](https://vitejs.dev).

**CSS (external)**
## Installation

- bootstrap 5
- bootstrap datepicker
- bootstrap select

**CSS**

- datagrid.css
- datagrid-spinners.css

**JS (external)**

- jquery
- nette forms
- nette ajax / naja
- bootstrap
- bootstrap datepicker
- bootstrap select

**JS**

- datagrid.js
- datagrid-instant-url-refresh.js
- datagrid-spinners.js

**Icons**

You will probably want to use some icon font, but that is in your command.
On this project website we use font awesome (you can change the icon prefix by setting new value to static property `Datagrid::$iconPrefix = 'fa fa-';`).

**Spinners**

As you can see, there is also a `datagrid-spinners.js` script in a datagrid repository. If you include this file within you project layout, there are some actions, that will show spinner/some other animation when waiting for ajax response. Actions, that has somehow animated spinner:

- Group actions
- Pagination
- Changing items per page
- Toggling item detail - loading the detail for the first time

## NPM

```
npm install --save ublaboo-datagrid
```

package.json:
You need to install datagrid's assets. For example this way.

```json
{
"dependencies": {
"bootstrap-datepicker": "^1.9",
"bootstrap-select": "^1.14-beta2",
"bootstrap": "^5.0.0",
"happy-inputs": "^2.0",
"jquery": "^3.4.1",
"jquery-ui-sortable": "^1.0",
"nette-forms": "^3.0",
"nette.ajax.js": "^2.3",
"popper.js": "^1.14.7",
"ublaboo-datagrid": "^6.9"
}
"dependencies": {
"@contributte/datagrid": "git+ssh://[email protected]:contributte/datagrid.git#next"
}
}
```

## Example html when not using NPM

```html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/src/happy.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap-datepicker.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/assets/datagrid.css">

<!-- Use this css for ajax spinners -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/assets/datagrid-spinners.css">

<!-- Include this css when using FilterMultiSelect (https://developer.snapappointments.com/bootstrap-select/) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap-select.css">
##

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
</head>
## Demo

<body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src/nomodule-es5-fallback.js"></script>
<script>
var happy = new Happy;
Full example of using bundler.

happy.init();
</script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap-datepicker.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/jquery-ui.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/nette.ajax.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/assets/datagrid.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src/assets/netteForms.min.js"></script>
**package.json**

<!-- It is recommended to include this JS file with just a few bits. It refreshes URL on non ajax request -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/assets/datagrid-instant-url-refresh.js"></script>

<!-- Use this little extension for ajax spinners -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/assets/datagrid-spinners.js"></script>
```json
{
"dependencies": {
"@contributte/datagrid": "git+ssh://[email protected]:contributte/datagrid.git#next",
"@fortawesome/fontawesome-free": "^6.3.0",
"bootstrap": "^5.3.0-alpha3",
"naja": "^2.5.0",
"nette-forms": "^3.3.1",
"prismjs": "^1.29.0",
"sortablejs": "^1.15.0",
"tom-select": "^2.2.2",
"vanillajs-datepicker": "^1.3.1"
},
"devDependencies": {
"@types/bootstrap-select": "^1.13.4",
"@types/jquery": "^3.5.16",
"@types/jqueryui": "^1.12.16",
"@types/sortablejs": "^1.15.1",
"@types/vanillajs-datepicker": "^1.2.1",
"autoprefixer": "^10.4.0",
"typescript": "^4.9.5",
"vite": "^2.6.10"
},
"scripts": {
"watch": "vite build --watch --mode=development",
"build": "vite build --mode=production"
}
}
```

<!-- Include bootstrap-select.js when using FilterMultiSelect (https://developer.snapappointments.com/bootstrap-select/) -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap-select.js"></script>
</body>
</html>
**vite.config.js**

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

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

return {
publicDir: './assets/public',
resolve: {
alias: {
'@': resolve(__dirname, 'assets/js'),
'~': resolve(__dirname, 'node_modules'),
},
},
base: '/dist/',
server: {
open: false,
hmr: false,
},
css: {
postcss: [
"autoprefixer"
]
},
build: {
manifest: true,
assetsDir: '',
outDir: './www/dist/',
emptyOutDir: true,
minify: DEV ? false : 'esbuild',
rollupOptions: {
output: {
manualChunks: undefined,
chunkFileNames: '[name].js',
entryFileNames: '[name].js',
assetFileNames: '[name].[ext]',
},
input: {
app: './assets/js/main.js'
}
}
},
}
});
```
1 change: 1 addition & 0 deletions assets/ajax/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./naja";
135 changes: 135 additions & 0 deletions assets/ajax/naja.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import type { Naja } from "naja";
import type {
Ajax,
AjaxEventMap as BaseAjaxEventMap,
BaseRequestParams as AjaxBaseRequestParams,
BeforeEventDetail as BaseBeforeEventDetail,
DatagridPayload,
ErrorEventDetail as BaseErrorEventDetail,
EventDetail,
EventListener,
InteractEventDetail as BaseInteractEventDetail,
Payload,
RequestParams,
Response as AjaxResponse,
SuccessEventDetail as BaseSuccessEventDetail,
} from "../types";
import { Datagrid } from "../datagrid";
import { BeforeEvent, ErrorEvent, Payload as NajaPayload, SuccessEvent } from "naja/dist/Naja";
import { InteractionEvent } from "naja/dist/core/UIHandler";

export interface BaseRequestParams extends AjaxBaseRequestParams, Request {
url: string;
method: string;
}

export interface BeforeEventDetail<D = {}> extends BaseBeforeEventDetail<D> {
params: EventDetail<BeforeEvent> & RequestParams<D>;
}

export interface InteractEventDetail<
E extends HTMLElement = HTMLElement
> extends BaseInteractEventDetail<E>, EventDetail<InteractionEvent> {
element: E;
}

export interface SuccessEventDetail<
P = DatagridPayload
> extends BaseSuccessEventDetail<P, Response>, EventDetail<SuccessEvent> {
params: BaseRequestParams;
payload: Payload<P> & NajaPayload;
response: AjaxResponse & Response;
}

export interface ErrorEventDetail<
E extends Error = Error,
> extends BaseErrorEventDetail<E, Response>, EventDetail<ErrorEvent> {
params: BaseRequestParams;
response: (AjaxResponse & Response) | undefined;
error: E;
}

export interface AjaxEventMap extends BaseAjaxEventMap {
before: CustomEvent<BeforeEventDetail>;
interact: CustomEvent<InteractEventDetail>;
snippetUpdate: CustomEvent<InteractEventDetail>;
success: CustomEvent<SuccessEventDetail>;
error: CustomEvent<ErrorEventDetail>;
}

export class NajaAjax<C extends Naja = Naja, G extends Datagrid = Datagrid> extends EventTarget implements Ajax<C, G> {
constructor(public client: C) {
if (!client.VERSION || client.VERSION < 2) {
throw new Error("NajaAjax supports Naja 2 and higher" + (client.VERSION ? `(version ${client.VERSION} provided)` : ''))
}
super();
}

onInit() {
this.client.addEventListener('before', (e) => {
return this.dispatch('before', {
params: e.detail
});
})

this.client.uiHandler.addEventListener('interaction', (e) => {
if (!(e.detail.element instanceof HTMLElement)) {
throw new Error("Element is not an instanceof HTMLElement");
}

return this.dispatch('interact', {
...e.detail,
element: e.detail.element as HTMLElement // Naja's event has a type of HTMLElement
})
})


this.client.addEventListener('success', (e) => {
return this.dispatch('success', {
...e.detail,
params: e.detail.request,
payload: e.detail.payload as Payload
});
})

this.client.addEventListener('error', (e) => {
return this.dispatch('error', {
...e.detail,
params: e.detail.request,
response: e.detail.response,
});
})

return this;
}

async request<D = {}, P = DatagridPayload>(args: RequestParams<D>): Promise<P> {
return await this.client.makeRequest(args.method, args.url, args.data) as P;
}

async submitForm<E extends HTMLFormElement = HTMLFormElement, P = Payload>(element: E): Promise<P> {
return await this.client.uiHandler.submitForm(element) as P;
}

dispatch<
K extends string, M extends BaseAjaxEventMap = AjaxEventMap
>(type: K, detail: K extends keyof M ? EventDetail<M[K]> : any, options?: boolean): boolean {
return this.dispatchEvent(new CustomEvent(type, {detail}));
}

declare addEventListener: <K extends keyof M, M extends BaseAjaxEventMap = AjaxEventMap>(
type: K,
listener: EventListener<this, M[K]>,
options?: boolean | AddEventListenerOptions
) => void;

declare removeEventListener: <K extends keyof M, M extends BaseAjaxEventMap = AjaxEventMap>(
type: K,
listener: EventListener<this, M[K]>,
options?: boolean | AddEventListenerOptions
) => void;

declare dispatchEvent: <K extends string, M extends BaseAjaxEventMap = AjaxEventMap>(
event: K extends keyof M ? M[K] : CustomEvent
) => boolean;
}
4 changes: 4 additions & 0 deletions assets/css/datagrid-full.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@import "@fortawesome/fontawesome-free/css/all.css";
@import 'bootstrap/dist/css/bootstrap.css';
@import 'vanillajs-datepicker/css/datepicker-bs5.css';
@import './datagrid.css';
Loading

0 comments on commit dca24b4

Please sign in to comment.