@base-framework/base 3.0.112 → 3.0.114

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 CHANGED
@@ -2,15 +2,42 @@
2
2
 
3
3
  ## Framework Overview
4
4
 
5
- Our goal with Base is to solve many client-side rendering issues. Base focuses on reusability, scalability, and performance.
5
+ Our goal with Base is to solve many client-side and Server-side rendering issues. Base focuses on reusability, scalability, and performance.
6
6
 
7
7
  Base has a core that supports adding and removing events, custom events, data-tracking, element class and attribute modifying, object and type utils, etc.
8
8
 
9
9
  The framework is modular and has additional modules to help with ajax, HTML, layouts, data, data-binding, states, dates, routing, components, atoms, etc.
10
10
 
11
+ You can learn more about how to use Base in the wiki documentation. [Base Wiki](https://github.com/chrisdurfee/base/wiki)
12
+
13
+ ## Base Converter
14
+
15
+ There is a GPT created with ChatGPT that can convert code from other frameworks to Base. The GPT is new so it still has some issues but it is a good start. [Base Converter](https://chatgpt.com/g/g-uNL6KKeCo-base-converter/)
16
+
17
+
11
18
  ## Layouts
12
19
 
13
- Base framework uses components to render an application. Base creates and renders components using native JavaScript. Layouts are scaffolded using JavaScript object literals. Because the layouts are rendered client-side using native JavaScript, the framework does not require a compiling or build process.
20
+ Base uses components to render an application. Base creates and renders components using native JavaScript. Layouts are scaffolded using JavaScript object literals. Because the layouts are rendered client-side or server-side using native JavaScript, the framework does not require a compiling or build process.
21
+
22
+ Layouts are reuable and can be used in multiple components and pages. Layouts can be nested and composed to create complex layouts.
23
+
24
+ ```javascript
25
+
26
+ // Layouts use normal JavaScript object literals to create the layout.
27
+ { class: 'name' } === <div class="name"></div>
28
+
29
+ // Input example
30
+ { tag: 'input', type: 'text', value: 'name' } === <input type="text" value="name" />
31
+
32
+ // Nested layout example
33
+ { class: 'name', nest: [ { class: 'child' } ] } === <div class="name"><div class="child"></div></div>
34
+
35
+ ```
36
+
37
+ Base use atoms and components to create reusable layouts.
38
+
39
+ Learn more: [Base Layouts](https://github.com/chrisdurfee/base/wiki/Layout)
40
+
14
41
 
15
42
  ## Components and Atoms
16
43
 
@@ -40,12 +67,125 @@ export class Page extends Component
40
67
 
41
68
  Components have lifecycle methods for actions during creation, setup, and destruction.
42
69
 
70
+ learn more: [Base Components](https://github.com/chrisdurfee/base/wiki/Components)
71
+
72
+ ## Atoms
43
73
  Atoms are the building blocks for reusable layouts, allowing common design patterns and elements to be shared between multiple components and other atoms.
44
74
 
75
+ ## Atom Types
76
+ Atoms can be instantiated using various methodologies:
77
+
78
+ ### Function Atoms
79
+ These atoms are instantiated with either standard functions or arrow functions, equipped with a props object to transfer properties to the atoms.
80
+
81
+ ```typescript
82
+ const Div = (props, children) => ({
83
+ ...props,
84
+ children
85
+ });
86
+ ```
87
+
88
+ ### Atom Callbacks
89
+ Atoms may be created using the Atom function, which accepts a callback function as its sole parameter. The callback function is passed a props object and children array and returns an object containing the atom's layout.
90
+
91
+ ```typescript
92
+ import { Atom } from '@base-framework/base';
93
+
94
+ const Button = Atom((props, children) => ({
95
+ tag: 'button',
96
+ ...props,
97
+ children
98
+ }));
99
+ ```
100
+
101
+ #### Atom Nesting
102
+ Atoms should use composition to nest other atoms. This is achieved by passing the children array to the atoms args.
103
+
104
+ ```typescript
105
+ import { Atom } from '@base-framework/base';
106
+
107
+ const SecondaryButton = Atom((props, children) => (Button({
108
+ ...props,
109
+ class: 'secondary-btn',
110
+ children
111
+ }));
112
+ ```
113
+
114
+ ## Adding Event Listeners
115
+ Event listener callbacks within atoms accept two parameters: the originating event object and the "parent" component object in which the atom resides.
116
+
117
+ ## Utilization of Atoms
118
+ To leverage an atom, invoke its function and pass the requisite values via a props and children.
119
+
120
+ ```javascript
121
+ const Div = (props, children) => ({
122
+ ...props,
123
+ children
124
+ });
125
+
126
+ Div({ class: 'text' }, 'text');
127
+ ```
128
+
129
+ Atoms created with the Base Atom callback function support passing optional params including props or children to the atom. The props object should always be first but if the atom does not require props, the children array or string can be passed as the first argument.
130
+
131
+ ```javascript
132
+ import { Atom } from '@base-framework/base';
133
+
134
+ const Div = Atom((props, children) => ({
135
+ ...props,
136
+ tag: 'div',
137
+ children
138
+ }));
139
+
140
+ // props only
141
+ Div({class: 'text'});
142
+
143
+ // text child only
144
+ Div('test');
145
+
146
+ // array child only
147
+ Div([
148
+ Div('test')
149
+ ]);
150
+
151
+ // props and text child
152
+ Div({class: 'text'}, 'test');
153
+
154
+ // props and array children
155
+ Div({class: 'text'}, [
156
+ Div('test'),
157
+ Div('test')
158
+ ]);
159
+ ```
160
+
161
+ Learn more: [Base Atoms](https://github.com/chrisdurfee/base/wiki/Atoms)
162
+
163
+ Base has a package that has already created most of the HTML Atoms needed for rendering layouts. This package can be installed via npm.
164
+
165
+ ```bash
166
+ npm install @base-framework/atoms
167
+ ```
168
+
169
+ Here is the repository for the atoms package: [Base Atoms](https://github.com/chrisdurfee/atoms). Like Base, the atoms package is open-source and free to use.
170
+
171
+ ## Organisms
172
+
173
+ Base has a package that has some special organisms that can make building complex layouts quicker. This package can be installed via npm.
174
+
175
+ ```bash
176
+ npm install @base-framework/organisms
177
+ ```
178
+
179
+ Here is the repository for the organisms package: [Base Organisms](https://github.com/chrisdurfee/organisms). The organisms package is open-source and free to use.
180
+
181
+
45
182
  ## Element Directives
46
183
 
47
184
  Elements created by Base have access to custom directives, enabling more functionalities than standard HTML elements. These include caching, adding states, binding and watching data, re-rendering contents, declarative routing and switching, array mapping and binding, event listeners, and more.
48
185
 
186
+ Learn more: [Base Element Directives](https://github.com/chrisdurfee/base/wiki/Directives)
187
+
188
+
49
189
  ## Data Binding, Watching, and Linking
50
190
 
51
191
  Base supports "bindables" for creating and using data, supporting both shallow and deep nested data. Bindables can be one-way or two-way bindings.
@@ -55,13 +195,249 @@ Types of bindables include:
55
195
  - **SimpleData**: A shallow data object.
56
196
  - **Models**: Child of Data, with default attributes and server resource connectivity.
57
197
 
198
+ ```javascript
199
+ import { Atom } from '@base-framework/base';
200
+
201
+ /**
202
+ * If a parent component has a Data object with a "count" property.
203
+ *
204
+ * This will create an input with the value bound of the "count"
205
+ * property. This bind is Bi-directional binding to the
206
+ * "count" property of the parent component's Data object.
207
+ */
208
+ Input({ bind: 'count' })
209
+
210
+ /**
211
+ * Elements can watch for changes in data and re-render when the data changes.
212
+ */
213
+ Div({class: '[[className]]'})
214
+
215
+ // Multi-attribute watching
216
+ A({href: '/account/user/[[userId]]'}, '[[userName]] - [[age]]')
217
+
218
+ // Multi-data watcher
219
+ Div({class: ['[[propName]] [[otherPropName]]', [data, otherData]]})
220
+ ```
221
+
222
+ Base Data objects are bindable. There are a few types of data objects:
223
+
224
+ ```javascript
225
+ import { Data, SimpleData, Model } from '@base-framework/base';
226
+
227
+ /**
228
+ * Data object
229
+ *
230
+ * This can store deep nested data including arrays and objects.
231
+ */
232
+ const data = new Data({
233
+ name: {
234
+ first: 'Bruce',
235
+ last: 'Wayne'
236
+ },
237
+ address: {
238
+ street: '123 Gotham St',
239
+ city: 'Gotham',
240
+ state: 'NY'
241
+ },
242
+ phones: ['555-555-5555', '555-555-5556'],
243
+ age: 21
244
+ });
245
+
246
+ /**
247
+ * SimpleData object
248
+ *
249
+ * This can store shallow data.
250
+ */
251
+ const simpleData = new SimpleData({ name: 'batman' });
252
+
253
+ /**
254
+ * The data objects have are proxies and can be used like objects.
255
+ */
256
+
257
+ // Retrieval
258
+ let prop = data.name.first;
259
+ // or
260
+ let name = data.get('name.first');
261
+
262
+ // Assignment
263
+ simpleData.name = 'batman';
264
+ // or
265
+ data.set('name', 'batman');
266
+
267
+ // Pushing to an array
268
+ data.push('phones', '555-555-5557');
269
+
270
+ // changing an item in an array
271
+ data.set('phones[0]', '555-555-5558');
272
+
273
+ // Batch updates using an object
274
+ data.set({
275
+ name: 'batman',
276
+ age: 21
277
+ });
278
+
279
+ // Deletion
280
+ data.delete('name');
281
+
282
+ /**
283
+ * Model object
284
+ *
285
+ * This can store default attributes and server resource
286
+ * connectivity.
287
+ *
288
+ * The model's service already has methods for adding,
289
+ * updating, deleting, and fetching data from the
290
+ * server.
291
+ */
292
+ const UserModel = Model.extend({
293
+ defaults: {
294
+ name: {
295
+ first: 'Bruce',
296
+ last: 'Wayne'
297
+ },
298
+ address: {
299
+ street: '123 Gotham St',
300
+ city: 'Gotham',
301
+ state: 'NY'
302
+ },
303
+ age: 21
304
+ },
305
+ url: '/api/user',
306
+
307
+ xhr: {
308
+
309
+ /**
310
+ * Custom methods can be added to the model service.
311
+ *
312
+ * @param {object} instanceParams
313
+ * @param {function} callback
314
+ * @returns {object|bool} The xhr object
315
+ */
316
+ customMethod(instanceParams, callback)
317
+ {
318
+ if (!this.isValid())
319
+ {
320
+ return false;
321
+ }
322
+
323
+ let params = 'op=customParam' +
324
+ '&' + this.setupObjectData();
325
+
326
+ // this will be added to the base url of the model
327
+ const URL = "/custom/url";
328
+ return this._post(URL, params, instanceParams, callBack);
329
+ }
330
+ }
331
+ });
332
+
333
+ const model = new UserModel();
334
+
335
+ /**
336
+ * The model can be used to fetch data from the server.
337
+ *
338
+ * The response will be set to the model.
339
+ */
340
+ model.xhr.get({}, (response) => {
341
+
342
+ });
343
+
344
+ // post
345
+ model.xhr.add({}, (response) => {
346
+
347
+ });
348
+
349
+ // put
350
+ model.xhr.update({}, (response) => {
351
+
352
+ });
353
+
354
+ // delete
355
+ model.xhr.delete({}, (response) => {
356
+
357
+ });
358
+
359
+ ```
360
+ The component also supports the state property. This is another bindable type extended from the SimpleData object so it only supports shallow data.
361
+
362
+ ```javascript
363
+
364
+ /**
365
+ * This will return the state propreties and values that will be
366
+ * use to create the "state" object.
367
+ *
368
+ * @returns {object}
369
+ */
370
+ setupStates()
371
+ {
372
+ return {
373
+ count: 0,
374
+ loading: false
375
+ };
376
+ }
377
+
378
+ ```
379
+
380
+ Each component can have one "data" proeprty that can be set using the "setData" method.
381
+
382
+ If a component has been created using the "route" or "switch" directive, it will receive a "route" property that will contain the route data which is also bindable.
383
+
384
+ The data objects push changes to the elements that are bound to the data. This allows for re-rendering of the elements when the data changes.
385
+
386
+ Here is an example of how to use data binding in a component:
387
+
388
+ ```javascript
389
+ import { Div } from '@base-framework/atoms';
390
+ import { Component } from '@base-framework/base';
391
+
392
+ /**
393
+ * Timer
394
+ *
395
+ * This will create a timer component that uses state watching.
396
+ *
397
+ * @class
398
+ */
399
+ export class Timer extends Component
400
+ {
401
+ /**
402
+ * This will render the timer.
403
+ *
404
+ * @returns {object}
405
+ */
406
+ render()
407
+ {
408
+ // This will rerender the div text content when the count state changes.
409
+ return Div('[[count]]');
410
+ }
411
+
412
+ /**
413
+ * This will setup the states.
414
+ *
415
+ * @returns {object}
416
+ */
417
+ setupStates()
418
+ {
419
+ return { count: 0 };
420
+ }
421
+
422
+ afterSetup()
423
+ {
424
+ const ONE_SECOND = 1000;
425
+ window.setInterval(() => this.state.increment('count'), ONE_SECOND);
426
+ }
427
+ }
428
+ ```
429
+
430
+ Learn more: [Base Data Binding](https://github.com/chrisdurfee/base/wiki/Directives#binding-to-data)
431
+
432
+
58
433
  ## Performance
59
434
 
60
435
  Components are static by default, rendering only once per instance. They become dynamic when bound to bindable data sources, allowing for content re-rendering, value changes, function calls, and class additions on data change.
61
436
 
437
+
62
438
  ## Getting Started
63
439
 
64
- To begin using the Base Framework, follow these steps:
440
+ To begin using Base framework in a client-side or server-side rendered project, follow these steps:
65
441
 
66
442
  1. **Clone the repository**:
67
443
  ```bash
@@ -84,7 +460,6 @@ import { base } from '@base-framework/base';
84
460
 
85
461
  ## Usage
86
462
 
87
- Basic Component Creation
88
463
  Create a new component:
89
464
 
90
465
  ```javascript
@@ -152,13 +527,190 @@ const container = document.body;
152
527
  Builder.render(page, container);
153
528
  ```
154
529
 
155
- ## Example Project Using Base Framework
530
+ To allow more reusability, you create static elements as atoms and organisms. Atoms and organisms should use composition. These can be used in multiple components and layouts.
531
+
532
+ ```javascript
533
+ import { Atom } from '@base-framework/base';
534
+
535
+ const Button = Atom((props, children) => ({
536
+ tag: 'button',
537
+ ...props,
538
+ children
539
+ }));
540
+
541
+ const SecondaryButton = Atom((props, children) => (Button({
542
+ ...props,
543
+ class: 'secondary-btn',
544
+ children
545
+ }));
546
+
547
+ ```
548
+
549
+ Atoms can be added to components and other atoms:
550
+
551
+ ```javascript
552
+
553
+ import { Component } from '@base-framework/base';
554
+ import { Div } from '@base-framework/atoms';
555
+ import { SecondaryButton } from './atoms/button.js';
556
+
557
+ export class Page extends Component
558
+ {
559
+ render()
560
+ {
561
+ return Div([
562
+ SecondaryButton({
563
+ /**
564
+ * This will add a click event listener to the button.
565
+ *
566
+ * @param {Event} event The event object
567
+ * @param {Component} parent The parent component object
568
+ * @returns {void}
569
+ */
570
+ click(event, parent) =>
571
+ {
572
+ // Code to access the parent component
573
+ }
574
+ })
575
+ ]);
576
+ }
577
+ }
578
+ ```
579
+
580
+ ## Code Splitting
581
+
582
+ Base supports code splitting, allowing you to load components and modules on demand. This can help reduce the initial load time of your application. You can import atoms or components on demand using the Import module.
583
+
584
+ ```javascript
585
+ import { Import } from "@base-framework/base";
586
+ import { A, Div, H1, Header } from "@base-framework/atoms";
587
+
588
+ // Without using Vite
589
+
590
+ /**
591
+ * This will create an import buttons.
592
+ *
593
+ * @returns {object}
594
+ */
595
+ const ImportButtons = () => (
596
+ Div([
597
+ Header([
598
+ H1('Aside')
599
+ ]),
600
+ Div({ class: 'card' }, [
601
+
602
+ // Importing the buttons module on demand
603
+ Import('../../../../../../components/atoms/import-buttons.js')
604
+ ])
605
+ ])
606
+ );
607
+
608
+ // With Vite
609
+
610
+ /**
611
+ * This will create an import buttons.
612
+ *
613
+ * @returns {object}
614
+ */
615
+ const ImportButtons = () => (
616
+ Div([
617
+ Header([
618
+ H1('Aside')
619
+ ]),
620
+ Div({ class: 'card' }, [
621
+
622
+ /**
623
+ * This will import the buttons module on demand. The import function
624
+ * needs to be used to add the module to the vite build.
625
+ */
626
+ Import({ src: import('../../../../../../components/atoms/import-buttons.js') })
627
+ ])
628
+ ])
629
+ );
630
+ ```
631
+
632
+ ## Example Todo App
633
+
634
+ Here is an example of a todo app using Base:
635
+
636
+ ```javascript
637
+ import { Button, Div, Form, H1, Input, Li, Ul } from "@base-framework/atoms";
638
+ import { Builder, Data } from "@base-framework/base";
639
+
640
+ /**
641
+ * This will create a to-do app.
642
+ *
643
+ * @returns {object}
644
+ */
645
+ export function ToDoApp()
646
+ {
647
+ /**
648
+ * This will set up the data store for the to-do app.
649
+ */
650
+ const data = new Data({ items: [] });
651
+
652
+ /**
653
+ * This will handle the form submission for adding a new to-do item.
654
+ *
655
+ * @param {object} event
656
+ */
657
+ const handleSubmit = (event) =>
658
+ {
659
+ event.preventDefault();
660
+ const form = event.target;
661
+ const input = form.querySelector('input');
662
+
663
+ // add the new to-do item to the array of items
664
+ data.push('items', input.value);
665
+ input.value = '';
666
+ };
667
+
668
+ /**
669
+ * This will handle removing a to-do item from the list.
670
+ *
671
+ * @param {number} index
672
+ * @returns {boolean}
673
+ */
674
+ const handleRemove = (index) => data.splice('items', index);
675
+
676
+ return Div([
677
+ H1('To-Do App'),
678
+ Form({ submit: handleSubmit }, [
679
+ Input({ placeholder: 'Add a new item' }),
680
+ Button({ type: 'submit' }, 'Add')
681
+ ]),
682
+ Ul({
683
+ for: [data, 'items', (item, index) => Li([
684
+ Span(item),
685
+ Button({ click: () => handleRemove(index) }, 'Remove')
686
+ ])]
687
+ })
688
+ ]);
689
+ }
690
+
691
+ /**
692
+ * This will render the to-do app to the body of the document.
693
+ */
694
+ Builder.render(ToDoApp(), document.body);
695
+ ```
696
+
697
+
698
+ ## Example Projects Using Base
699
+
700
+ [Base Platform Example](https://github.com/chrisdurfee/next-app-shell)
701
+
702
+ [Base App Example](https://github.com/chrisdurfee/base-update)
703
+
704
+ [Base Website Example](https://github.com/chrisdurfee/life)
705
+
706
+ [Base Server Example](https://github.com/chrisdurfee/base-server)
707
+
708
+ [Base Game Example](https://github.com/chrisdurfee/multisplode)
156
709
 
157
- [Base Framework Example](https://github.com/chrisdurfee/next-app-shell)
158
710
 
159
711
  ## Contributing
160
712
 
161
- Contributions to Base Framework are welcome. Follow these steps to contribute:
713
+ Contributions to Base are welcome. Follow these steps to contribute:
162
714
 
163
715
  - Fork the repository.
164
716
  - Create a new branch for your feature or bug fix.
@@ -168,7 +720,7 @@ Contributions to Base Framework are welcome. Follow these steps to contribute:
168
720
 
169
721
  ## License
170
722
 
171
- Base Framework is licensed under the MIT License. See the LICENSE file for details.
723
+ Base is licensed under the MIT License. See the LICENSE file for details.
172
724
 
173
725
  ## Contact
174
726