posthtml-component 1.1.1 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{readme.md → README.md} +230 -211
- package/package.json +25 -23
- package/src/index.d.ts +214 -0
- package/src/index.js +4 -4
- package/src/process-attributes.js +3 -3
- package/src/valid-attributes.js +2 -2
- /package/{license → LICENSE} +0 -0
package/{readme.md → README.md}
RENAMED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
[
|
|
2
|
-
[
|
|
3
|
-
[
|
|
1
|
+
[npm]: https://www.npmjs.com/package/posthtml-component
|
|
2
|
+
[npm-version-shield]: https://img.shields.io/npm/v/posthtml-component.svg
|
|
3
|
+
[npm-stats]: http://npm-stat.com/charts.html?package=posthtml-component
|
|
4
|
+
[npm-stats-shield]: https://img.shields.io/npm/dt/posthtml-component.svg
|
|
5
|
+
[github-ci]: https://github.com/posthtml/posthtml-components/actions/workflows/build.yml
|
|
6
|
+
[github-ci-shield]: https://github.com/posthtml/posthtml-components/actions/workflows/build.yml/badge.svg
|
|
7
|
+
[license]: ./LICENSE
|
|
8
|
+
[license-shield]: https://img.shields.io/npm/l/posthtml-component.svg
|
|
4
9
|
|
|
5
10
|
<div align="center">
|
|
6
|
-
<img width="
|
|
7
|
-
<h1>PostHTML Components
|
|
8
|
-
<p>
|
|
11
|
+
<img width="150" height="150" alt="PostHTML" src="https://posthtml.github.io/posthtml/logo.svg">
|
|
12
|
+
<h1>PostHTML Components</h1>
|
|
13
|
+
<p>Laravel Blade-inspired components for PostHTML</p>
|
|
14
|
+
|
|
15
|
+
[![Version][npm-version-shield]][npm]
|
|
16
|
+
[![Build][github-ci-shield]][github-ci]
|
|
17
|
+
[![License][license-shield]][license]
|
|
18
|
+
[![Downloads][npm-stats-shield]][npm-stats]
|
|
9
19
|
</div>
|
|
10
20
|
|
|
11
21
|
## Installation
|
|
@@ -16,9 +26,8 @@ npm i -D posthtml-component
|
|
|
16
26
|
|
|
17
27
|
## Introduction
|
|
18
28
|
|
|
19
|
-
This PostHTML plugin provides an HTML-friendly syntax for
|
|
20
|
-
If you are familiar with Blade, React, Vue or similar, you will find
|
|
21
|
-
See below a basic example, as code is worth a thousand words.
|
|
29
|
+
This PostHTML plugin provides an HTML-friendly syntax for using components in your HTML templates.
|
|
30
|
+
If you are familiar with Blade, React, Vue or similar, you will find the syntax to be familiar, as this plugin is inspired by them.
|
|
22
31
|
|
|
23
32
|
**See also the first [PostHTML Bootstrap UI](https://github.com/thewebartisan7/posthtml-bootstrap-ui) using this plugin and check also the [starter template here](https://github.com/thewebartisan7/posthtml-bootstrap-ui-starter).**
|
|
24
33
|
|
|
@@ -33,7 +42,7 @@ Create the component:
|
|
|
33
42
|
</button>
|
|
34
43
|
```
|
|
35
44
|
|
|
36
|
-
Use
|
|
45
|
+
Use it:
|
|
37
46
|
|
|
38
47
|
``` html
|
|
39
48
|
<!-- src/index.html -->
|
|
@@ -48,14 +57,15 @@ Init PostHTML:
|
|
|
48
57
|
|
|
49
58
|
```js
|
|
50
59
|
// index.js
|
|
51
|
-
const { readFileSync, writeFileSync } = require('fs')
|
|
52
|
-
|
|
53
60
|
const posthtml = require('posthtml')
|
|
54
61
|
const components = require('posthtml-components')
|
|
62
|
+
const { readFileSync, writeFileSync } = require('node:fs')
|
|
55
63
|
|
|
56
|
-
posthtml(
|
|
64
|
+
posthtml([
|
|
65
|
+
components({ root: './src' })
|
|
66
|
+
])
|
|
57
67
|
.process(readFileSync('src/index.html', 'utf8'))
|
|
58
|
-
.then(
|
|
68
|
+
.then(result => writeFileSync('dist/index.html', result.html, 'utf8'))
|
|
59
69
|
```
|
|
60
70
|
|
|
61
71
|
Result:
|
|
@@ -69,11 +79,13 @@ Result:
|
|
|
69
79
|
</html>
|
|
70
80
|
```
|
|
71
81
|
|
|
72
|
-
You
|
|
73
|
-
|
|
82
|
+
You might have noticed that the `src/button.html` component contains `type` and `class` attributes, and that we also passed those attributes when we used it in `src/index.html`.
|
|
83
|
+
|
|
84
|
+
The result is that `type` was overridden, and `class` was merged.
|
|
85
|
+
|
|
86
|
+
By default, `class` and `style` attributes are merged, while all others attribute are overridden. You can also override `class` and `style` attributes by prepending `override:` to the class attribute.
|
|
74
87
|
|
|
75
|
-
|
|
76
|
-
You can also override `class` and `style` attributes by prepending `override:` to the class attribute. Example:
|
|
88
|
+
For example:
|
|
77
89
|
|
|
78
90
|
```html
|
|
79
91
|
<x-button override:class="btn-custom">Submit</x-button>
|
|
@@ -82,67 +94,77 @@ You can also override `class` and `style` attributes by prepending `override:` t
|
|
|
82
94
|
<button type="button" class="btn-custom">Submit</button>
|
|
83
95
|
```
|
|
84
96
|
|
|
85
|
-
All attributes you pass to the component will be added to the first node of your component or to the node with an attribute
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
You can also
|
|
97
|
+
All attributes that you pass to the component will be added to the first node of your component or to the node with an attribute named `attributes`, _only_ if they are not defined as `props` via `<script props>` or if they are not "known attributes" (see
|
|
98
|
+
[valid-attributes.js](https://github.com/thewebartisan7/posthtml-components/blob/main/src/valid-attributes.js)).
|
|
99
|
+
|
|
100
|
+
You can also define which attributes are considered to be valid, via the plugin's options.
|
|
101
|
+
|
|
89
102
|
More details on this in [Attributes](#attributes) section.
|
|
90
103
|
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
### yield tag
|
|
105
|
+
|
|
106
|
+
The `<yield></yield>` tag is where content that you pass to a component will be injected.
|
|
107
|
+
|
|
108
|
+
The plugin configures the PostHTML parser to recognize self-closing tags, so you can also just write is as `<yield />`.
|
|
109
|
+
|
|
110
|
+
For brevity, we will use self-closing tags in the examples.
|
|
111
|
+
|
|
112
|
+
### More examples
|
|
93
113
|
|
|
94
114
|
See also the `docs-src` folder where you can find more examples.
|
|
95
|
-
|
|
115
|
+
|
|
116
|
+
You can clone this repo and run `npm run build` to compile them.
|
|
96
117
|
|
|
97
118
|
## Options
|
|
98
119
|
|
|
99
|
-
|
|
|
100
|
-
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
| **propsScriptAttribute** |
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
|
|
|
123
|
-
|
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
|
|
|
127
|
-
|
|
|
128
|
-
|
|
|
129
|
-
| **
|
|
120
|
+
| Name | Type | Default | Description |
|
|
121
|
+
|--------------------------|-------------------|----------------------------------------------|-----------------------------------------------------------------------------------------|
|
|
122
|
+
| **root** | `String` | `'./'` | Root path for components lookup. |
|
|
123
|
+
| **folders** | `String[]` | `['']` | Array of paths relative to `options.root` or defined namespaces. |
|
|
124
|
+
| **tagPrefix** | `String` | `'x-'` | Tag prefix. |
|
|
125
|
+
| **tag** | `String\|Boolean` | `false` | Component tag. Use with `options.attribute`. Boolean only `false`. |
|
|
126
|
+
| **attribute** | `String` | `'src'` | Component attribute for setting path. |
|
|
127
|
+
| **namespaces** | `String[]` | `[]` | Array of namespace root paths, fallback paths, and custom override paths. |
|
|
128
|
+
| **namespaceSeparator** | `String` | `'::'` | Namespace separator for tag names. |
|
|
129
|
+
| **fileExtension** | `String` | `'html'` | File extension for component files. |
|
|
130
|
+
| **yield** | `String` | `'yield'` | Tag name for injecting main component content. |
|
|
131
|
+
| **slot** | `String` | `'slot'` | Tag name for slots. |
|
|
132
|
+
| **fill** | `String` | `'fill'` | Tag name for filling slots. |
|
|
133
|
+
| **slotSeparator** | `String` | `':'` | Name separator for `<slot>` and `<fill>` tags. |
|
|
134
|
+
| **push** | `String` | `'push'` | Tag name for `<push>`. |
|
|
135
|
+
| **stack** | `String` | `'stack'` | Tag name for `<stack>`. |
|
|
136
|
+
| **propsScriptAttribute** | `String` | `'props'` | Attribute in `<script props>` for retrieving component props. |
|
|
137
|
+
| **propsContext** | `String` | `'props'` | Name of the object inside the script for processing props. |
|
|
138
|
+
| **propsAttribute** | `String` | `'props'` | Attribute to define props as JSON. |
|
|
139
|
+
| **propsSlot** | `String` | `'props'` | Used to retrieve props passed to slot via `$slots.slotName.props`. |
|
|
140
|
+
| **parserOptions** | `Object` | `{recognizeSelfClosing: true}` | Pass options to `posthtml-parser`. |
|
|
141
|
+
| **expressions** | `Object` | `{}` | Pass options to `posthtml-expressions`. |
|
|
142
|
+
| **plugins** | `Array` | `[]` | PostHTML plugins to apply to every parsed component. |
|
|
143
|
+
| **matcher** | `Object` | `[{tag: options.tagPrefix}]` | Array of objects used to match tags. |
|
|
144
|
+
| **attrsParserRules** | `Object` | `{}` | Additional rules for attributes parser plugin. |
|
|
145
|
+
| **strict** | `Boolean` | `true` | Toggle exception throwing. |
|
|
146
|
+
| **mergeCustomizer** | `Function` | `function` | Callback for lodash `mergeWith` to merge `options.expressions.locals` and props. |
|
|
147
|
+
| **utilities** | `Object` | `{merge: _.mergeWith, template: _.template}` | Utility methods passed to `<script props>`. |
|
|
148
|
+
| **elementAttributes** | `Object` | `{}` | Object with tag names and function modifiers of `valid-attributes.js`. |
|
|
149
|
+
| **safelistAttributes** | `String[]` | `['data-*']` | Array of attribute names to add to default valid attributes. |
|
|
150
|
+
| **blocklistAttributes** | `String[]` | `[]` | Array of attribute names to remove from default valid attributes. |
|
|
130
151
|
|
|
131
152
|
## Features
|
|
132
153
|
|
|
133
154
|
### Tag names and x-tags
|
|
134
155
|
|
|
135
156
|
You can use the components in multiple ways, or also a combination of them.
|
|
136
|
-
Like with `posthtml-extend` and `posthtml-modules` you can define a tag name in combination with an attribute name for set the path of the components.
|
|
137
157
|
|
|
138
|
-
|
|
158
|
+
If you to use components as 'includes', you may define a tag and src attribute name.
|
|
139
159
|
|
|
140
|
-
|
|
160
|
+
Using our previous button component example, we can define the tag and attribute names and then use it in this way:
|
|
161
|
+
|
|
162
|
+
```hbs
|
|
141
163
|
<!-- src/index.html -->
|
|
142
164
|
<html>
|
|
143
|
-
<body>
|
|
144
|
-
|
|
145
|
-
</body>
|
|
165
|
+
<body>
|
|
166
|
+
<component src="button.html">Submit</component>
|
|
167
|
+
</body>
|
|
146
168
|
</html>
|
|
147
169
|
```
|
|
148
170
|
|
|
@@ -151,16 +173,20 @@ Init PostHTML:
|
|
|
151
173
|
```js
|
|
152
174
|
// index.js
|
|
153
175
|
|
|
154
|
-
require('posthtml')(
|
|
176
|
+
require('posthtml')(
|
|
177
|
+
require('posthtml-components')({
|
|
178
|
+
root: './src',
|
|
179
|
+
tag: 'component',
|
|
180
|
+
attribute: 'src'
|
|
181
|
+
}))
|
|
155
182
|
.process(/* ... */)
|
|
156
183
|
.then(/* ... */)
|
|
157
184
|
```
|
|
158
185
|
|
|
159
|
-
If you need more control over
|
|
186
|
+
If you need more control over tag matching, you can pass an array of matcher or single object via `options.matcher` like this:
|
|
160
187
|
|
|
161
188
|
```js
|
|
162
189
|
// index.js
|
|
163
|
-
|
|
164
190
|
const options = {
|
|
165
191
|
root: './src',
|
|
166
192
|
matcher: [{tag: 'a-tag'}, {tag: 'another-one'}, {tag: new RegExp(`^app-`, 'i')}]
|
|
@@ -171,13 +197,12 @@ require('posthtml')(require('posthtml-components')(options))
|
|
|
171
197
|
.then(/* ... */)
|
|
172
198
|
```
|
|
173
199
|
|
|
174
|
-
With `posthtml-components` you don't need to specify the path name when you are using `x-tag-name` syntax.
|
|
200
|
+
With `posthtml-components` you don't need to specify the path name when you are using `x-tag-name` syntax.
|
|
175
201
|
|
|
176
202
|
Setup PostHTML:
|
|
177
203
|
|
|
178
204
|
```js
|
|
179
205
|
// index.js
|
|
180
|
-
|
|
181
206
|
const options = {
|
|
182
207
|
root: './src',
|
|
183
208
|
tagPrefix: 'x-'
|
|
@@ -199,21 +224,22 @@ Use:
|
|
|
199
224
|
</html>
|
|
200
225
|
```
|
|
201
226
|
|
|
202
|
-
If your components are in a subfolder then you can use `dot` to access it
|
|
227
|
+
If your components are in a subfolder then you can use `dot` to access it:
|
|
203
228
|
|
|
204
229
|
``` html
|
|
205
|
-
<!--
|
|
230
|
+
<!-- src/components/forms/button.html -->
|
|
206
231
|
<x-forms.button>Submit</x-forms.button>
|
|
207
232
|
```
|
|
208
233
|
|
|
209
|
-
If your components are in a sub-folder with multiple files, then
|
|
210
|
-
|
|
234
|
+
If your components are in a sub-folder with multiple files, then in order to avoid writing out the main file name you can use `index.html` without specifying it.
|
|
235
|
+
|
|
236
|
+
Here's an example:
|
|
211
237
|
|
|
212
238
|
``` html
|
|
213
|
-
<!--
|
|
239
|
+
<!-- src/components/modals/index.html -->
|
|
214
240
|
<x-modal.index>Submit</x-modal.index>
|
|
215
241
|
|
|
216
|
-
<!-- You
|
|
242
|
+
<!-- You may omit "index" part since the file is named "index.html" -->
|
|
217
243
|
<x-modal>Submit</x-modal>
|
|
218
244
|
```
|
|
219
245
|
|
|
@@ -250,9 +276,9 @@ If you don't add this to `process`, PostHTML will use `posthtml-parser` defaults
|
|
|
250
276
|
|
|
251
277
|
### Multiple folders
|
|
252
278
|
|
|
253
|
-
You have full control where
|
|
254
|
-
|
|
255
|
-
|
|
279
|
+
You have full control over where your component files exist. Once you set the base root path of your components, you can then set multiple folders.
|
|
280
|
+
|
|
281
|
+
For example if your root is `./src` and then you have several folders where you have your components, for example `./src/components` and `./src/layouts`, you can set up the plugin like below:
|
|
256
282
|
|
|
257
283
|
```js
|
|
258
284
|
// index.js
|
|
@@ -268,13 +294,13 @@ require('posthtml')(require('posthtml-components')(options))
|
|
|
268
294
|
|
|
269
295
|
### Namespaces
|
|
270
296
|
|
|
271
|
-
With namespaces, you can define a top level root path to your components
|
|
272
|
-
It can be useful for handle custom theme, where you define a specific top level root, with fallback root when component it's not found,
|
|
273
|
-
and a custom root for override, something like a child theme.
|
|
297
|
+
With namespaces, you can define a top level root path to your components.
|
|
274
298
|
|
|
275
|
-
|
|
299
|
+
It can be useful for handling custom themes, where you define a specific top level root with a fallback root for when a component is not found, and a custom root for overriding.
|
|
276
300
|
|
|
277
|
-
|
|
301
|
+
This makes it possible to create folder structures like this:
|
|
302
|
+
|
|
303
|
+
- `src` (root folder)
|
|
278
304
|
- `components` (folder for components like modal, button, etc.)
|
|
279
305
|
- `layouts` (folder for layout components like base layout, header, footer, etc.)
|
|
280
306
|
- `theme-dark` (namespace folder for theme-dark)
|
|
@@ -296,18 +322,18 @@ And the options would be like:
|
|
|
296
322
|
```js
|
|
297
323
|
// index.js
|
|
298
324
|
const options = {
|
|
299
|
-
//
|
|
325
|
+
// Root for component without namespace
|
|
300
326
|
root: './src',
|
|
301
327
|
// Folders is always appended in 'root' or any defined namespace's folders (base, fallback or custom)
|
|
302
328
|
folders: ['components', 'layouts'],
|
|
303
329
|
namespaces: [{
|
|
304
|
-
// Namespace name will be
|
|
330
|
+
// Namespace name will be prepended to tag name (example <x-theme-dark::button>)
|
|
305
331
|
name: 'theme-dark',
|
|
306
|
-
//
|
|
332
|
+
// Root of the namespace
|
|
307
333
|
root: './src/theme-dark',
|
|
308
|
-
// Fallback root when a component
|
|
334
|
+
// Fallback root when a component is not found in namespace
|
|
309
335
|
fallback: './src',
|
|
310
|
-
// Custom root for
|
|
336
|
+
// Custom root for overriding, the lookup happens here first
|
|
311
337
|
custom: './src/custom/theme-dark'
|
|
312
338
|
}, {
|
|
313
339
|
// Light theme
|
|
@@ -323,7 +349,7 @@ const options = {
|
|
|
323
349
|
|
|
324
350
|
Use the component namespace:
|
|
325
351
|
|
|
326
|
-
```
|
|
352
|
+
```xml
|
|
327
353
|
<!-- src/index.html -->
|
|
328
354
|
<html>
|
|
329
355
|
<body>
|
|
@@ -333,33 +359,30 @@ Use the component namespace:
|
|
|
333
359
|
</html>
|
|
334
360
|
```
|
|
335
361
|
|
|
336
|
-
Of course, you can change this folder structure as you prefer according to your project requirements.
|
|
337
|
-
|
|
338
362
|
### Slots
|
|
339
363
|
|
|
340
|
-
|
|
341
|
-
Find below a simple example.
|
|
364
|
+
Components may define slots that can be filled with content when used.
|
|
342
365
|
|
|
343
|
-
|
|
366
|
+
For example:
|
|
344
367
|
|
|
345
|
-
```
|
|
368
|
+
```xml
|
|
346
369
|
<!-- src/modal.html -->
|
|
347
370
|
<div class="modal">
|
|
348
371
|
<div class="modal-header">
|
|
349
|
-
<slot:header
|
|
372
|
+
<slot:header />
|
|
350
373
|
</div>
|
|
351
374
|
<div class="modal-body">
|
|
352
|
-
<slot:body
|
|
375
|
+
<slot:body />
|
|
353
376
|
</div>
|
|
354
377
|
<div class="modal-footer">
|
|
355
|
-
<slot:footer
|
|
378
|
+
<slot:footer />
|
|
356
379
|
</div>
|
|
357
380
|
</div>
|
|
358
381
|
```
|
|
359
382
|
|
|
360
383
|
Use the component:
|
|
361
384
|
|
|
362
|
-
```
|
|
385
|
+
```xml
|
|
363
386
|
<!-- src/index.html -->
|
|
364
387
|
<x-modal>
|
|
365
388
|
<fill:header>Header content</fill:header>
|
|
@@ -385,11 +408,11 @@ Result:
|
|
|
385
408
|
</div>
|
|
386
409
|
```
|
|
387
410
|
|
|
388
|
-
By default, the content is replaced, but you can also prepend or append the content, or keep the default content by not filling the slot.
|
|
411
|
+
By default, the slot content is replaced, but you can also prepend or append the content, or keep the default content by not filling the slot.
|
|
389
412
|
|
|
390
413
|
Add some default content in the component:
|
|
391
414
|
|
|
392
|
-
```
|
|
415
|
+
```xml
|
|
393
416
|
<!-- src/modal.html -->
|
|
394
417
|
<div class="modal">
|
|
395
418
|
<div class="modal-header">
|
|
@@ -404,7 +427,7 @@ Add some default content in the component:
|
|
|
404
427
|
</div>
|
|
405
428
|
```
|
|
406
429
|
|
|
407
|
-
```
|
|
430
|
+
```xml
|
|
408
431
|
<!-- src/index.html -->
|
|
409
432
|
<x-modal>
|
|
410
433
|
<fill:body prepend>Prepend body</fill:body>
|
|
@@ -431,42 +454,41 @@ Result:
|
|
|
431
454
|
|
|
432
455
|
### Stacks
|
|
433
456
|
|
|
434
|
-
You
|
|
457
|
+
You may push content to named stacks which can be rendered somewhere else, like in another component. This can be particularly useful for specifying any JavaScript or CSS required by your components.
|
|
435
458
|
|
|
436
|
-
First
|
|
459
|
+
First, add a `<stack>` tag to your HTML:
|
|
437
460
|
|
|
438
|
-
```
|
|
461
|
+
```diff
|
|
439
462
|
<!-- src/index.html -->
|
|
440
463
|
<html>
|
|
441
|
-
<head>
|
|
442
|
-
<stack name="styles"
|
|
443
|
-
</head>
|
|
464
|
+
<head>
|
|
465
|
+
+ <stack name="styles" />
|
|
466
|
+
</head>
|
|
444
467
|
<body>
|
|
445
|
-
|
|
446
|
-
<
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
</x-modal>
|
|
468
|
+
<x-modal>
|
|
469
|
+
<fill:header>Header content</fill:header>
|
|
470
|
+
<fill:body>Body content</fill:body>
|
|
471
|
+
<fill:footer>Footer content</fill:footer>
|
|
472
|
+
</x-modal>
|
|
451
473
|
|
|
452
|
-
<stack name="scripts"
|
|
474
|
+
+ <stack name="scripts" />
|
|
453
475
|
</body>
|
|
454
476
|
</html>
|
|
455
477
|
```
|
|
456
478
|
|
|
457
|
-
Then in modal components
|
|
479
|
+
Then, in modal components or any other child components, you can push content to this stack:
|
|
458
480
|
|
|
459
|
-
```
|
|
481
|
+
```xml
|
|
460
482
|
<!-- src/modal.html -->
|
|
461
483
|
<div class="modal">
|
|
462
484
|
<div class="modal-header">
|
|
463
|
-
<slot:header
|
|
485
|
+
<slot:header />
|
|
464
486
|
</div>
|
|
465
487
|
<div class="modal-body">
|
|
466
|
-
<slot:body
|
|
488
|
+
<slot:body />
|
|
467
489
|
</div>
|
|
468
490
|
<div class="modal-footer">
|
|
469
|
-
<slot:footer
|
|
491
|
+
<slot:footer />
|
|
470
492
|
</div>
|
|
471
493
|
</div>
|
|
472
494
|
|
|
@@ -504,7 +526,9 @@ The output will be:
|
|
|
504
526
|
</html>
|
|
505
527
|
```
|
|
506
528
|
|
|
507
|
-
The `once` attribute allows you to push content only once per rendering cycle.
|
|
529
|
+
The `once` attribute allows you to push content only once per rendering cycle.
|
|
530
|
+
|
|
531
|
+
For example, if you are rendering a given component within a loop, you may wish to only push the JavaScript and CSS the first time the component is rendered.
|
|
508
532
|
|
|
509
533
|
Example.
|
|
510
534
|
|
|
@@ -525,8 +549,7 @@ Example.
|
|
|
525
549
|
</push>
|
|
526
550
|
```
|
|
527
551
|
|
|
528
|
-
By default, the content is pushed in the stack in the given order.
|
|
529
|
-
If you would like to prepend content onto the beginning of a stack, you should use the `prepend` attribute:
|
|
552
|
+
By default, the content is pushed in the stack in the given order. If you would like to prepend content onto the beginning of a stack, you may use the `prepend` attribute:
|
|
530
553
|
|
|
531
554
|
```html
|
|
532
555
|
<push name="scripts">
|
|
@@ -535,7 +558,6 @@ If you would like to prepend content onto the beginning of a stack, you should u
|
|
|
535
558
|
</push>
|
|
536
559
|
|
|
537
560
|
<!-- Later... -->
|
|
538
|
-
|
|
539
561
|
<push name="scripts" prepend>
|
|
540
562
|
<!-- This will be first -->
|
|
541
563
|
<script src="/example-2.js"></script>
|
|
@@ -544,28 +566,26 @@ If you would like to prepend content onto the beginning of a stack, you should u
|
|
|
544
566
|
|
|
545
567
|
### Props
|
|
546
568
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
Let's see how it works with a few examples starting with a basic one.
|
|
569
|
+
`props` can be passed to components in HTML attributes. To use them in a component, they must be defined in the component's `<script props>` tag.
|
|
550
570
|
|
|
551
|
-
|
|
571
|
+
For example:
|
|
552
572
|
|
|
553
573
|
```html
|
|
554
|
-
<!-- src/
|
|
574
|
+
<!-- src/alert.html -->
|
|
555
575
|
<script props>
|
|
556
576
|
module.exports = {
|
|
557
|
-
|
|
577
|
+
title: props.title || 'Default title'
|
|
558
578
|
}
|
|
559
579
|
</script>
|
|
560
580
|
<div>
|
|
561
|
-
{{
|
|
581
|
+
{{ title }}
|
|
562
582
|
</div>
|
|
563
583
|
```
|
|
564
584
|
|
|
565
585
|
Use:
|
|
566
586
|
|
|
567
587
|
```html
|
|
568
|
-
<x-
|
|
588
|
+
<x-alert title="Hello world!" />
|
|
569
589
|
```
|
|
570
590
|
|
|
571
591
|
The output will be:
|
|
@@ -576,25 +596,23 @@ The output will be:
|
|
|
576
596
|
</div>
|
|
577
597
|
```
|
|
578
598
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
Use component without passing prop:
|
|
599
|
+
If no `title` attribute is passed to the component, the default value will be used.
|
|
582
600
|
|
|
583
601
|
```html
|
|
584
|
-
<x-my-
|
|
602
|
+
<x-my-alert />
|
|
585
603
|
```
|
|
586
604
|
|
|
587
605
|
The output will be:
|
|
588
606
|
|
|
589
607
|
```html
|
|
590
608
|
<div>
|
|
591
|
-
Default
|
|
609
|
+
Default title
|
|
592
610
|
</div>
|
|
593
611
|
```
|
|
594
612
|
|
|
595
|
-
|
|
613
|
+
Inside `<script props>` you have access to passed props via an object named `props`.
|
|
596
614
|
|
|
597
|
-
|
|
615
|
+
Here's an example of what you can do with it:
|
|
598
616
|
|
|
599
617
|
```html
|
|
600
618
|
<!-- src/modal.html -->
|
|
@@ -618,7 +636,12 @@ Create the component:
|
|
|
618
636
|
Use:
|
|
619
637
|
|
|
620
638
|
```html
|
|
621
|
-
<x-modal
|
|
639
|
+
<x-modal
|
|
640
|
+
size="xl"
|
|
641
|
+
title="My modal title"
|
|
642
|
+
items='["third", "fourth"]'
|
|
643
|
+
class="modal-custom"
|
|
644
|
+
/>
|
|
622
645
|
```
|
|
623
646
|
|
|
624
647
|
The output will be:
|
|
@@ -637,12 +660,11 @@ The output will be:
|
|
|
637
660
|
</div>
|
|
638
661
|
```
|
|
639
662
|
|
|
640
|
-
|
|
663
|
+
Notice how the `class` attribute that we passed to the component is merged with `class` attribute value of the first node inside of it.
|
|
641
664
|
|
|
642
|
-
You can change how attributes are merged with global props defined via options by passing a callback function
|
|
665
|
+
You can change how attributes are merged with global props defined via options, by passing a callback function.
|
|
643
666
|
|
|
644
667
|
By default, all props are scoped to the component, and are not available to nested components. You can however change this accordingly to your need.
|
|
645
|
-
Let's see below example.
|
|
646
668
|
|
|
647
669
|
Create a component:
|
|
648
670
|
|
|
@@ -650,78 +672,79 @@ Create a component:
|
|
|
650
672
|
<!-- src/child.html -->
|
|
651
673
|
<script props>
|
|
652
674
|
module.exports = {
|
|
653
|
-
|
|
675
|
+
title: props.title || 'Default title'
|
|
654
676
|
}
|
|
655
677
|
</script>
|
|
656
678
|
<div>
|
|
657
|
-
Prop in child: {{
|
|
679
|
+
Prop in child: {{ title }}
|
|
658
680
|
</div>
|
|
659
681
|
```
|
|
660
682
|
|
|
661
|
-
Create
|
|
662
|
-
|
|
683
|
+
Create a `<x-parent>` component that uses `<x-child>`:
|
|
663
684
|
|
|
664
685
|
```html
|
|
665
686
|
<!-- src/parent.html -->
|
|
666
687
|
<script props>
|
|
667
688
|
module.exports = {
|
|
668
|
-
|
|
689
|
+
title: props.title || 'Default title'
|
|
669
690
|
}
|
|
670
691
|
</script>
|
|
671
692
|
<div>
|
|
672
|
-
Prop in parent: {{
|
|
673
|
-
<x-child
|
|
693
|
+
Prop in parent: {{ title }}
|
|
694
|
+
<x-child />
|
|
674
695
|
</div>
|
|
675
696
|
```
|
|
676
697
|
|
|
677
|
-
Use:
|
|
698
|
+
Use it:
|
|
678
699
|
|
|
679
700
|
```html
|
|
680
|
-
<x-parent
|
|
701
|
+
<x-parent title="My title" />
|
|
681
702
|
```
|
|
682
703
|
|
|
683
704
|
The output will be:
|
|
684
705
|
|
|
685
706
|
```html
|
|
686
707
|
<div>
|
|
687
|
-
Prop in parent: My
|
|
708
|
+
Prop in parent: My title
|
|
688
709
|
<div>
|
|
689
|
-
Prop in child: Default
|
|
710
|
+
Prop in child: Default title
|
|
690
711
|
</div>
|
|
691
712
|
</div>
|
|
692
713
|
```
|
|
693
714
|
|
|
694
|
-
As you can see `
|
|
715
|
+
As you can see, `title` in `<x-child>` component renders the default value and not the one set via `<x-parent>`.
|
|
695
716
|
|
|
717
|
+
To change this, we must prepend `aware:` to the attribute name in order to pass the props to nested components.
|
|
696
718
|
|
|
697
719
|
```html
|
|
698
|
-
<x-parent aware:
|
|
720
|
+
<x-parent aware:title="My title" />
|
|
699
721
|
```
|
|
700
722
|
|
|
701
723
|
The output now will be:
|
|
702
724
|
|
|
703
725
|
```html
|
|
704
726
|
<div>
|
|
705
|
-
Prop in parent: My
|
|
727
|
+
Prop in parent: My title
|
|
706
728
|
<div>
|
|
707
|
-
Prop in child: My
|
|
729
|
+
Prop in child: My title
|
|
708
730
|
</div>
|
|
709
731
|
</div>
|
|
710
732
|
```
|
|
711
733
|
|
|
712
734
|
### Attributes
|
|
713
735
|
|
|
714
|
-
You can pass any attributes to your components and
|
|
715
|
-
or to the node with an attribute named `attributes`.
|
|
716
|
-
[fallthrough attribute](https://vuejs.org/guide/components/attrs.html), or with Laravel Blade is
|
|
717
|
-
[component-attributes](https://laravel.com/docs/10.x/blade#component-attributes).
|
|
736
|
+
You can pass any attributes to your components and they will be added to the first node of your component,
|
|
737
|
+
or to the node with an attribute named `attributes`.
|
|
718
738
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
or not defined as `props` in the `<script props>`.
|
|
739
|
+
If you are familiar with Vue.js, this is the same as so-called
|
|
740
|
+
[fallthrough attribute](https://vuejs.org/guide/components/attrs.html). Or, with Laravel Blade, it's
|
|
741
|
+
[component-attributes](https://laravel.com/docs/11.x/blade#component-attributes).
|
|
723
742
|
|
|
724
|
-
|
|
743
|
+
By default, `class` and `style` are merged with existing `class` and `style` attribute. All other attributes are overridden by default.
|
|
744
|
+
|
|
745
|
+
If you pass an attribute that is defined as a `prop`, it will not be added to the component's node.
|
|
746
|
+
|
|
747
|
+
Here's an example:
|
|
725
748
|
|
|
726
749
|
```html
|
|
727
750
|
<!-- src/button.html -->
|
|
@@ -739,7 +762,7 @@ Use the component:
|
|
|
739
762
|
|
|
740
763
|
```html
|
|
741
764
|
<!-- src/index.html -->
|
|
742
|
-
<x-button type="submit" class="btn-primary" label="My button"
|
|
765
|
+
<x-button type="submit" class="btn-primary" label="My button" />
|
|
743
766
|
```
|
|
744
767
|
|
|
745
768
|
Result:
|
|
@@ -749,13 +772,11 @@ Result:
|
|
|
749
772
|
<button type="submit" class="btn btn-primary">My button</button>
|
|
750
773
|
```
|
|
751
774
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
As said early, `class` and `style` are merged by default, if you want to override them, just prepend `override:` to the attribute name:
|
|
775
|
+
If you need to override `class` and `style` attribute values (instead of merging them), just prepend `override:` to the attribute name:
|
|
755
776
|
|
|
756
777
|
```html
|
|
757
778
|
<!-- src/index.html -->
|
|
758
|
-
<x-button type="submit" override:class="btn-custom" label="My button"
|
|
779
|
+
<x-button type="submit" override:class="btn-custom" label="My button" />
|
|
759
780
|
```
|
|
760
781
|
|
|
761
782
|
Result:
|
|
@@ -765,7 +786,7 @@ Result:
|
|
|
765
786
|
<button type="submit" class="btn-custom">My button</button>
|
|
766
787
|
```
|
|
767
788
|
|
|
768
|
-
If you want
|
|
789
|
+
If you want the attributes to be passed to a certain node, use the `attributes` attribute:
|
|
769
790
|
|
|
770
791
|
```html
|
|
771
792
|
<!-- src/my-component.html -->
|
|
@@ -780,7 +801,7 @@ Use the component:
|
|
|
780
801
|
|
|
781
802
|
```html
|
|
782
803
|
<!-- src/index.html -->
|
|
783
|
-
<x-my-component class="my-class"
|
|
804
|
+
<x-my-component class="my-class" />
|
|
784
805
|
```
|
|
785
806
|
|
|
786
807
|
Result:
|
|
@@ -794,11 +815,11 @@ Result:
|
|
|
794
815
|
</div>
|
|
795
816
|
```
|
|
796
817
|
|
|
797
|
-
You can add custom rules how attributes are parsed
|
|
818
|
+
You can add custom rules to define how attributes are parsed - we use [posthtml-attrs-parser](https://github.com/posthtml/posthtml-attrs-parser) to handle them.
|
|
798
819
|
|
|
799
820
|
### Advanced attributes configurations
|
|
800
821
|
|
|
801
|
-
If default configurations for valid attributes are not right for you,
|
|
822
|
+
If default configurations for valid attributes are not right for you, you may configure them as explained below.
|
|
802
823
|
|
|
803
824
|
```js
|
|
804
825
|
// index.js
|
|
@@ -808,8 +829,7 @@ const posthtml = require('posthtml')
|
|
|
808
829
|
const components = require('posthtml-components')
|
|
809
830
|
|
|
810
831
|
const options = {
|
|
811
|
-
root: './src',
|
|
812
|
-
|
|
832
|
+
root: './src',
|
|
813
833
|
// Add attributes to specific tag or override defaults
|
|
814
834
|
elementAttributes: {
|
|
815
835
|
DIV: (defaultAttributes) => {
|
|
@@ -833,24 +853,27 @@ const options = {
|
|
|
833
853
|
],
|
|
834
854
|
|
|
835
855
|
// Remove attributes from all tags that support it
|
|
836
|
-
|
|
856
|
+
blocklistAttributes: [
|
|
837
857
|
'role'
|
|
838
858
|
]
|
|
839
859
|
}
|
|
840
860
|
|
|
841
861
|
posthtml(components(options))
|
|
842
862
|
.process(readFileSync('src/index.html', 'utf8'))
|
|
843
|
-
.then(
|
|
863
|
+
.then(result => writeFileSync('dist/index.html', result.html, 'utf8'))
|
|
844
864
|
```
|
|
845
865
|
|
|
846
866
|
## Examples
|
|
847
867
|
|
|
848
868
|
You can work with `<slot>` and `<fill>` or you can create component for each block of your component, and you can also support both of them.
|
|
849
|
-
|
|
869
|
+
|
|
870
|
+
You can find an example of this inside `docs-src/components/modal`. Following is a short explanation of both approaches.
|
|
850
871
|
|
|
851
872
|
### Using slots
|
|
852
873
|
|
|
853
|
-
Let's suppose we want to create a component for [bootstrap modal](https://getbootstrap.com/docs/5.2/components/modal/).
|
|
874
|
+
Let's suppose we want to create a component for [bootstrap modal](https://getbootstrap.com/docs/5.2/components/modal/).
|
|
875
|
+
|
|
876
|
+
The code required is:
|
|
854
877
|
|
|
855
878
|
```html
|
|
856
879
|
<!-- Modal HTML -->
|
|
@@ -874,30 +897,33 @@ Let's suppose we want to create a component for [bootstrap modal](https://getboo
|
|
|
874
897
|
```
|
|
875
898
|
|
|
876
899
|
There is almost three block of code: the header, the body and the footer.
|
|
877
|
-
So we could create our component with three slot like below:
|
|
878
900
|
|
|
879
|
-
|
|
901
|
+
So we could create a component with three slots:
|
|
902
|
+
|
|
903
|
+
```diff
|
|
880
904
|
<!-- Modal component -->
|
|
881
905
|
<div class="modal fade" tabindex="-1" aria-hidden="true">
|
|
882
906
|
<div class="modal-dialog">
|
|
883
907
|
<div class="modal-content">
|
|
884
908
|
<div class="modal-header">
|
|
885
|
-
<
|
|
909
|
+
- <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
|
|
910
|
+
+ <slot:header />
|
|
886
911
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
887
912
|
</div>
|
|
888
913
|
<div class="modal-body">
|
|
889
|
-
<slot:body
|
|
914
|
+
+ <slot:body />
|
|
890
915
|
</div>
|
|
891
916
|
<div class="modal-footer">
|
|
892
917
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
893
|
-
<slot:footer
|
|
918
|
+
+ <slot:footer />
|
|
919
|
+
- <button type="button" class="btn btn-primary">Save changes</button>
|
|
894
920
|
</div>
|
|
895
921
|
</div>
|
|
896
922
|
</div>
|
|
897
923
|
</div>
|
|
898
924
|
```
|
|
899
925
|
|
|
900
|
-
|
|
926
|
+
We can then use it like this:
|
|
901
927
|
|
|
902
928
|
```html
|
|
903
929
|
<x-modal
|
|
@@ -905,30 +931,31 @@ In this case we can use it like:
|
|
|
905
931
|
aria-labelledby="exampleModalLabel"
|
|
906
932
|
>
|
|
907
933
|
<slot:header>
|
|
908
|
-
|
|
934
|
+
<h5 class="modal-title" id="exampleModalLabel">My modal</h5>
|
|
909
935
|
</slot:header>
|
|
910
936
|
|
|
911
937
|
<slot:body>
|
|
912
|
-
|
|
938
|
+
Modal body content goes here...
|
|
913
939
|
</slot:body>
|
|
914
940
|
|
|
915
941
|
<slot:footer close="false">
|
|
916
|
-
|
|
942
|
+
<button type="button" class="btn btn-primary">Confirm</button>
|
|
917
943
|
</slot:footer>
|
|
918
944
|
</x-modal>
|
|
919
945
|
```
|
|
920
946
|
|
|
921
947
|
### Splitting component in small component
|
|
922
948
|
|
|
923
|
-
Another
|
|
924
|
-
|
|
949
|
+
Another approach is to split the component in smaller components, passing attributes to each of them.
|
|
950
|
+
|
|
951
|
+
So we create a main component and then three different smaller components:
|
|
925
952
|
|
|
926
953
|
```html
|
|
927
954
|
<!-- Main modal component -->
|
|
928
955
|
<div class="modal fade" tabindex="-1" aria-hidden="true">
|
|
929
956
|
<div class="modal-dialog">
|
|
930
957
|
<div class="modal-content">
|
|
931
|
-
<yield
|
|
958
|
+
<yield />
|
|
932
959
|
</div>
|
|
933
960
|
</div>
|
|
934
961
|
</div>
|
|
@@ -937,25 +964,25 @@ So we create the component with a main component and then three different small
|
|
|
937
964
|
```html
|
|
938
965
|
<!-- Header modal component -->
|
|
939
966
|
<div class="modal-header">
|
|
940
|
-
<yield
|
|
967
|
+
<yield />
|
|
941
968
|
</div>
|
|
942
969
|
```
|
|
943
970
|
|
|
944
971
|
```html
|
|
945
972
|
<!-- Body modal component -->
|
|
946
973
|
<div class="modal-body">
|
|
947
|
-
<yield
|
|
974
|
+
<yield />
|
|
948
975
|
</div>
|
|
949
976
|
```
|
|
950
977
|
|
|
951
978
|
```html
|
|
952
979
|
<!-- Footer modal component -->
|
|
953
980
|
<div class="modal-footer">
|
|
954
|
-
<yield
|
|
981
|
+
<yield />
|
|
955
982
|
</div>
|
|
956
983
|
```
|
|
957
984
|
|
|
958
|
-
And then you can use it like
|
|
985
|
+
And then you can use it like this:
|
|
959
986
|
|
|
960
987
|
```html
|
|
961
988
|
<x-modal
|
|
@@ -980,7 +1007,7 @@ As said in this way you can pass attributes to each of them, without defining pr
|
|
|
980
1007
|
|
|
981
1008
|
### Combine slots and small component
|
|
982
1009
|
|
|
983
|
-
You can also combine both
|
|
1010
|
+
You can also combine both approaches, and then use them with slots or with small components:
|
|
984
1011
|
|
|
985
1012
|
```html
|
|
986
1013
|
|
|
@@ -996,26 +1023,27 @@ You can also combine both way, and then use them with slots or with small compon
|
|
|
996
1023
|
<div class="modal-content">
|
|
997
1024
|
<if condition="$slots.header?.filled">
|
|
998
1025
|
<x-modal.header>
|
|
999
|
-
<slot:header
|
|
1026
|
+
<slot:header />
|
|
1000
1027
|
</x-modal.header>
|
|
1001
1028
|
</if>
|
|
1002
1029
|
<if condition="$slots.body?.filled">
|
|
1003
1030
|
<x-modal.body>
|
|
1004
|
-
<slot:body
|
|
1031
|
+
<slot:body />
|
|
1005
1032
|
</x-modal.body>
|
|
1006
1033
|
</if>
|
|
1007
1034
|
<if condition="$slots.footer?.filled">
|
|
1008
1035
|
<x-modal.footer close="{{ $slots.footer?.props.close }}">
|
|
1009
|
-
<slot:footer
|
|
1036
|
+
<slot:footer />
|
|
1010
1037
|
</x-modal.footer>
|
|
1011
1038
|
</if>
|
|
1012
|
-
<yield
|
|
1039
|
+
<yield />
|
|
1013
1040
|
</div>
|
|
1014
1041
|
</div><!-- /.modal-dialog -->
|
|
1015
1042
|
</div><!-- /.modal -->
|
|
1016
1043
|
```
|
|
1017
1044
|
|
|
1018
1045
|
Now you can use your component with slots or with small components.
|
|
1046
|
+
|
|
1019
1047
|
As you may notice, by using slots, you already can use also your small components, and so you can also pass props
|
|
1020
1048
|
via `$slots` which has all the `props` passed via slot, and as well check if slot is filled.
|
|
1021
1049
|
|
|
@@ -1028,16 +1056,7 @@ If you are migrating from `posthtml-extend` and/or `posthtml-modules` please to
|
|
|
1028
1056
|
|
|
1029
1057
|
See [PostHTML Guidelines](https://github.com/posthtml/posthtml/tree/master/docs) and [contribution guide](CONTRIBUTING.md).
|
|
1030
1058
|
|
|
1031
|
-
[npm]: https://img.shields.io/npm/v/posthtml-component.svg
|
|
1032
|
-
[npm-url]: https://www.npmjs.com/package/posthtml-component
|
|
1033
|
-
|
|
1034
|
-
[style]: https://img.shields.io/badge/code_style-XO-5ed9c7.svg
|
|
1035
|
-
[style-url]: https://github.com/sindresorhus/xo
|
|
1036
|
-
|
|
1037
|
-
[cover]: https://coveralls.io/repos/thewebartisan7/posthtml-components/badge.svg?branch=main
|
|
1038
|
-
[cover-badge]: https://coveralls.io/r/thewebartisan7/posthtml-components?branch=main
|
|
1039
|
-
|
|
1040
1059
|
## Credits
|
|
1041
1060
|
|
|
1042
|
-
Thanks to all PostHTML contributors and especially to `posthtml-extend` and `posthtml-modules` contributors, as part of code
|
|
1061
|
+
Thanks to all PostHTML contributors and especially to `posthtml-extend` and `posthtml-modules` contributors, as part of code is ~~stolen~~ inspired from these plugins.
|
|
1043
1062
|
Huge thanks also to Laravel Blade template engine.
|