bigpipe-util 0.2.7 → 0.2.10

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/CHANGELOG.md CHANGED
@@ -4,10 +4,21 @@ All notable changes to `bigpipe-util` will be documented in this file.
4
4
 
5
5
  Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
6
6
 
7
+ ## v0.2.10 - 2025-05-15
8
+ - Added `onAfterLoad.js`, `waitForLoad.js`
9
+
10
+ ### Added
11
+ - Added `timeout` to open dialog with a delay in method `showFromModel`
12
+
13
+ ## v0.2.8 - 2025-05-02
14
+
15
+ ### Added
16
+ - Added `timeout` to open dialog with a delay in method `showFromModel`
17
+
7
18
  ## v0.2.7 - 2025-03-22
8
19
 
9
20
  ### Added
10
- - Added `timeout` to open dialog with a delay
21
+ - Added `timeout` to open dialog with a delay in method `render`
11
22
 
12
23
  ## v0.2.6 - 2025-01-16
13
24
 
package/README.md CHANGED
@@ -3,30 +3,30 @@
3
3
  This library currently implements small part of [Facebook BigPipe][blog] so far, but the advantage is to efficiently insert/replace content and work with the DOM. It is also possible to easily call JavaScript modules from PHP.
4
4
 
5
5
 
6
- ## Demo App
6
+ ## 👀 Demo App
7
7
  Try the app with [live demo](http://bigpipe.xf.cz).
8
8
 
9
- ## Full documentation
9
+ ## 📕 Full documentation
10
10
  https://richarddobron.github.io/bigpipe-php/
11
11
 
12
- ## Requirements
12
+ ## â„šī¸ Requirements
13
13
  * PHP 7.1 or higher
14
14
  * Webpack
15
15
 
16
- ## Installation
16
+ ## đŸ“Ļ Installation
17
+ Follow these steps to install and set up:
17
18
 
18
- These steps are required:
19
- 1. Install composer package:
19
+ ### 1. Install composer package:
20
20
  ```shell
21
21
  $ composer require richarddobron/bigpipe
22
22
  ```
23
23
 
24
- 2. Install npm package:
24
+ ### 2. Install npm package:
25
25
  ```shell
26
26
  $ npm install bigpipe-util
27
27
  ```
28
28
 
29
- 3. Add this lines to /path/to/resources/js/app.js:
29
+ ### 3. Add the following to /path/to/resources/js/app.js:
30
30
  ```javascript
31
31
  import Primer from 'bigpipe-util/src/Primer';
32
32
 
@@ -39,19 +39,10 @@ window.require = (modulePath) => {
39
39
  };
40
40
  ```
41
41
 
42
- 4. Create file /path/to/resources/js/ServerJS.js
43
- - this step is optional, but if you skip it, use this in next step:
44
- ```require("bigpipe-util/ServerJS")```
45
- ```javascript
46
- import ServerJSImpl from 'bigpipe-util/src/ServerJS';
47
- export default class ServerJS extends ServerJSImpl {
48
- }
49
- ```
50
-
51
- 5. Add this lines to page footer:
42
+ ### 4. Add these lines to the page footer:
52
43
  ```html
53
44
  <script>
54
- (new (require("ServerJS"))).handle(<?=json_encode(\dobron\BigPipe\BigPipe::jsmods())?>);
45
+ (new (require("bigpipe-util/ServerJS"))).handle(<?=json_encode(\dobron\BigPipe\BigPipe::jsmods())?>);
55
46
  </script>
56
47
  ```
57
48
 
@@ -85,16 +76,16 @@ if (OH_NOES_WE_NEED_TO_CANCEL_RIGHT_NOW_OR_ELSE) {
85
76
  }
86
77
  ```
87
78
 
88
- # What all can be Ajaxifed?
79
+ # âšĄī¸ What all can be Ajaxifed?
89
80
 
90
- ## Links
81
+ ## 🔗 Links
91
82
  ```html
92
83
  <a href="#"
93
84
  ajaxify="/ajax/remove.php"
94
85
  rel="async">Remove Item</a>
95
86
  ```
96
87
 
97
- ## Forms
88
+ ## 📝 Forms
98
89
  ```html
99
90
  <form action="/submit.php"
100
91
  method="POST"
@@ -104,31 +95,30 @@ if (OH_NOES_WE_NEED_TO_CANCEL_RIGHT_NOW_OR_ELSE) {
104
95
  </form>
105
96
  ```
106
97
 
107
- ## Dialogs
98
+ ## đŸ’Ŧ Dialogs
108
99
  ```html
109
100
  <a href="#"
110
101
  ajaxify="/ajax/modal.php"
111
102
  rel="dialog">Open Modal</a>
112
103
  ```
113
104
 
114
- ## Inspiration
105
+ ## 🌟 Inspiration
115
106
 
116
- BigPipe is inspired by the concept behind Facebook's BigPipe. For more details
107
+ BigPipe is inspired by Facebook's BigPipe. For more details
117
108
  read their blog post: [Pipelining web pages for high performance][blog].
118
109
 
119
- ## Motivation
110
+ ## 💡 Motivation
120
111
 
121
112
  There is a large number of PHP projects for which moving to modern frameworks like Laravel Livewire, React, Vue.js (and many more!) could be very challenging.
122
113
 
123
114
  The purpose of this library is to rapidly reduce the continuously repetitive code to work with the DOM and improve the communication barrier between PHP and JavaScript.
124
115
 
125
- ## Credits
116
+ ## 🤝 Contributing
126
117
 
127
- - [Richard Dobroň][link-author]
118
+ We welcome contributions! If you'd like to help improve this project, feel free to open an issue or submit a pull request.
128
119
 
129
- ## License
120
+ ## 📜 License
130
121
 
131
122
  The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
132
123
 
133
- [link-author]: https://github.com/richardDobron
134
124
  [blog]: https://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bigpipe-util",
3
3
  "description": "This library currently implements small part of Facebook BigPipe so far, but the advantage is to efficiently insert/replace content and work with the DOM. It is also possible to easily call JavaScript modules from PHP.",
4
- "version": "0.2.7",
4
+ "version": "0.2.10",
5
5
  "keywords": [
6
6
  "bigpipe",
7
7
  "xhr",
package/src/Primer.js CHANGED
@@ -111,7 +111,7 @@ export default function Primer() {
111
111
  }
112
112
  }
113
113
  })
114
- .setHandler(function (response) {
114
+ .setHandler(function () {
115
115
  eventTarget.classList.remove("async-saving");
116
116
 
117
117
  activeControls.forEach(function (control) {
@@ -3,11 +3,7 @@ import DOM from "../core/DOM";
3
3
  export default class AsyncDOM {
4
4
  invoke(domOps, element) {
5
5
  for (let i = 0; i < domOps.length; ++i) {
6
- let domOp = domOps[i],
7
- type = domOp[0],
8
- selector = domOp[1],
9
- enableTarget = domOp[2],
10
- content = domOp[3];
6
+ let [type, selector, enableTarget, content] = domOps[i];
11
7
 
12
8
  let node = enableTarget && element || null;
13
9
 
@@ -8,7 +8,7 @@ let dialogId = 1;
8
8
  let zIndex;
9
9
  let originalBodyPad = null;
10
10
 
11
- Modal.prototype._handleKeydownEvent = function(event) {
11
+ Modal.prototype._handleKeydownEvent = function (event) {
12
12
  if (event.which === 27 && this._options.keyboard) {
13
13
  const currentModal = stack[stack.length - 1];
14
14
  if (currentModal.el.isEqualNode(this.el)) {
@@ -42,43 +42,40 @@ export default class Dialog {
42
42
 
43
43
  dialogId++;
44
44
 
45
- const renderDialog = () => {
46
- const dialog = this._show({
47
- el: document.getElementById(this.id),
48
- animate: options.animate !== null && options.animate !== undefined ? options.animate : false,
49
- keyboard: options.keyboard !== null && options.keyboard !== undefined ? options.keyboard : true,
50
- backdrop: options.backdrop !== null && options.backdrop !== undefined ? options.backdrop : true,
51
- transition: options.transition !== null && options.transition !== undefined ? options.transition : 0,
52
- backdropTransition: options.backdropTransition !== null && options.backdropTransition !== undefined ? options.backdropTransition : 0,
53
- }, options.controller, args);
54
-
55
- stack.push(dialog);
56
-
57
- return dialog;
58
- };
59
-
60
- if (options.timeout && typeof options.timeout === 'number') {
61
- return new Promise(resolve => setTimeout(() => resolve(renderDialog()), options.timeout));
62
- }
63
-
64
- return renderDialog();
45
+ return this._maybeWithTimeout(options.timeout, () => this._show({
46
+ el: document.getElementById(this.id),
47
+ animate: this._getOption(options, 'animate', false),
48
+ keyboard: this._getOption(options, 'keyboard', true),
49
+ backdrop: this._getOption(options, 'backdrop', true),
50
+ transition: this._getOption(options, 'transition', 0),
51
+ backdropTransition: this._getOption(options, 'backdropTransition', 0)
52
+ }, options.controller, args));
65
53
  }
66
54
 
67
55
  showFromModel(model, args) {
68
- const dialog = this._show({
56
+ return this._maybeWithTimeout(model.timeout, () => this._show({
69
57
  title: model.title || '',
70
58
  content: model.body,
71
59
  footer: model.footer || false,
72
- animate: model.animate !== null && model.animate !== undefined ? model.animate : false,
73
- keyboard: model.keyboard !== null && model.keyboard !== undefined ? model.keyboard : true,
74
- backdrop: model.backdrop !== null && model.backdrop !== undefined ? model.backdrop : true,
75
- transition: model.transition !== null && model.transition !== undefined ? model.transition : 0,
76
- backdropTransition: model.backdropTransition !== null && model.backdropTransition !== undefined ? model.backdropTransition : 0,
77
- }, model.controller, args);
60
+ animate: this._getOption(model, 'animate', false),
61
+ keyboard: this._getOption(model, 'keyboard', true),
62
+ backdrop: this._getOption(model, 'backdrop', true),
63
+ transition: this._getOption(model, 'transition', 0),
64
+ backdropTransition: this._getOption(model, 'backdropTransition', 0)
65
+ }, model.controller, args));
66
+ }
78
67
 
79
- stack.push(dialog);
68
+ _maybeWithTimeout(timeout, callback) {
69
+ if (typeof timeout === 'number') {
70
+ return new Promise(resolve => setTimeout(() => resolve(callback()), timeout));
71
+ }
72
+ return callback();
73
+ }
80
74
 
81
- return dialog;
75
+ _getOption(obj, key, defaultVal) {
76
+ return obj[key] !== null && obj[key] !== undefined
77
+ ? obj[key]
78
+ : defaultVal;
82
79
  }
83
80
 
84
81
  _show(options, controller, args) {
@@ -87,6 +84,7 @@ export default class Dialog {
87
84
  }
88
85
 
89
86
  const _modal = new Modal(options);
87
+ stack.push(_modal);
90
88
 
91
89
  if (controller) {
92
90
  if (typeof controller === 'string') {
@@ -124,7 +122,7 @@ export default class Dialog {
124
122
  let shown = false;
125
123
 
126
124
  backdrops.forEach((backdrop, index) => {
127
- if (shown || index > 0 && index === backdrops.length - 1) {
125
+ if (shown || (index > 0 && index === backdrops.length - 1)) {
128
126
  backdrop.style.display = 'none';
129
127
  } else {
130
128
  backdrop.style.zIndex = zIndex - 1;
@@ -134,9 +132,10 @@ export default class Dialog {
134
132
  });
135
133
  }
136
134
 
137
- _makeDialog(content, dialogClassName) {
135
+ _makeDialog(content, className = '') {
138
136
  this.id = `js_${dialogId.toString(16)}`;
139
- return `<div id="${this.id}" class="modal fade ${dialogClassName || ''}" tabindex="-1" role="dialog">
137
+ return `
138
+ <div id="${this.id}" class="modal fade ${className}" tabindex="-1" role="dialog">
140
139
  ${content}
141
140
  </div>`;
142
141
  }
@@ -0,0 +1,13 @@
1
+ import EventListener from 'fbjs/lib/EventListener';
2
+
3
+ export default function onAfterLoad(callback) {
4
+ if (document.readyState === 'complete') {
5
+ setTimeout(callback, 0);
6
+ } else {
7
+ const event = EventListener.capture(window, 'load', () => {
8
+ event.remove();
9
+
10
+ callback();
11
+ });
12
+ }
13
+ }
@@ -0,0 +1,57 @@
1
+ import onAfterLoad from "./onAfterLoad";
2
+
3
+ let loadingCounter = 0;
4
+ let originalCursor = '';
5
+
6
+ export default function waitForLoad(elementOnClicked, event, callback = () => {
7
+ }) {
8
+ const boundCallback = callback.bind(elementOnClicked, event);
9
+ const body = document.body;
10
+
11
+ if (document.readyState === 'complete') {
12
+ return boundCallback();
13
+ }
14
+
15
+ const type = (event || window.event).type;
16
+ loadingCounter++;
17
+
18
+ if (loadingCounter === 1) {
19
+ originalCursor = body.style.cursor;
20
+ body.style.cursor = 'progress';
21
+ }
22
+
23
+ switch (type) {
24
+ case 'load':
25
+ case 'focus':
26
+ onAfterLoad(boundCallback);
27
+ return;
28
+
29
+ case 'click':
30
+ elementOnClicked.style.cursor = 'progress';
31
+
32
+ onAfterLoad(() => {
33
+ loadingCounter--;
34
+
35
+ if (loadingCounter === 0) {
36
+ body.style.cursor = originalCursor;
37
+ }
38
+
39
+ elementOnClicked.style.cursor = '';
40
+
41
+ const result = boundCallback();
42
+ const href = elementOnClicked.getAttribute('href');
43
+
44
+ if (
45
+ elementOnClicked.tagName.toLowerCase() === 'a' &&
46
+ result !== false &&
47
+ href
48
+ ) {
49
+ window.location.href = href;
50
+ } else {
51
+ elementOnClicked.click();
52
+ }
53
+ });
54
+ }
55
+
56
+ return false;
57
+ }