@joystick.js/ui-canary 0.0.0-canary.27 → 0.0.0-canary.270

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.
Files changed (231) hide show
  1. package/build.js +15 -0
  2. package/dist/index.js +20 -20
  3. package/increment_version.js +3 -0
  4. package/index.html +7 -0
  5. package/package.json +9 -13
  6. package/src/accounts/authenticated.js +8 -0
  7. package/src/accounts/index.js +21 -0
  8. package/src/accounts/login.js +7 -0
  9. package/src/accounts/logout.js +8 -0
  10. package/src/accounts/recover_password.js +7 -0
  11. package/src/accounts/request.js +70 -0
  12. package/src/accounts/reset_password.js +7 -0
  13. package/src/accounts/signup.js +7 -0
  14. package/src/accounts/user.js +8 -0
  15. package/src/api/get.js +73 -0
  16. package/src/api/index.js +10 -0
  17. package/src/api/set.js +83 -0
  18. package/src/attach_joystick_to_window.js +31 -0
  19. package/src/cache/class.js +98 -0
  20. package/src/cache/index.js +5 -0
  21. package/src/component/class.js +360 -0
  22. package/src/component/css/compile.js +108 -0
  23. package/src/component/data/compile.js +44 -0
  24. package/src/component/data/fetch.js +12 -0
  25. package/src/component/data/load_data_from_window.js +14 -0
  26. package/src/component/dom/is_valid_attribute_name.js +11 -0
  27. package/src/component/events/attach_event_listener_for_element.js +32 -0
  28. package/src/component/events/serialize.js +12 -0
  29. package/src/component/index.js +17 -0
  30. package/src/component/methods/compile.js +28 -0
  31. package/src/component/props/compile.js +21 -0
  32. package/src/component/props/compile_default.js +21 -0
  33. package/src/component/register_component_options.js +44 -0
  34. package/src/component/render_methods/component.js +53 -0
  35. package/src/component/render_methods/component_ssr.js +22 -0
  36. package/src/component/render_methods/each.js +19 -0
  37. package/src/component/render_methods/i18n.js +50 -0
  38. package/src/component/render_methods/index.js +19 -0
  39. package/src/component/render_methods/when.js +20 -0
  40. package/src/component/state/compile.js +21 -0
  41. package/src/component/url/compile.js +39 -0
  42. package/src/component/validate_component_options/has_required_options.js +9 -0
  43. package/src/component/validate_component_options/index.js +16 -0
  44. package/src/component/validate_component_options/required_options.js +5 -0
  45. package/src/component/virtual_dom/diff.js +61 -0
  46. package/src/component/virtual_dom/diff_attributes.js +37 -0
  47. package/src/component/virtual_dom/diff_children.js +74 -0
  48. package/src/component/virtual_dom/element_patch_functions.js +31 -0
  49. package/src/component/virtual_dom/map_patch_functions_to_nodes.js +14 -0
  50. package/src/component/virtual_dom/render_virtual_dom_to_dom.js +37 -0
  51. package/src/external/get.js +9 -0
  52. package/src/external/track.js +10 -0
  53. package/src/forms/validate.js +306 -0
  54. package/src/forms/validators/credit_card.js +19 -0
  55. package/src/forms/validators/custom.js +5 -0
  56. package/src/forms/validators/email.js +12 -0
  57. package/src/forms/validators/equals.js +5 -0
  58. package/src/forms/validators/index.js +43 -0
  59. package/src/forms/validators/matches.js +5 -0
  60. package/src/forms/validators/max_length.js +5 -0
  61. package/src/forms/validators/min_length.js +5 -0
  62. package/src/forms/validators/phone.js +14 -0
  63. package/src/forms/validators/postal_code.js +204 -0
  64. package/src/forms/validators/regex.js +5 -0
  65. package/src/forms/validators/required.js +13 -0
  66. package/src/forms/validators/semver.js +14 -0
  67. package/src/forms/validators/slug.js +12 -0
  68. package/src/forms/validators/strong_password.js +12 -0
  69. package/src/forms/validators/url.js +12 -0
  70. package/src/forms/validators/vat.js +54 -0
  71. package/src/index.js +55 -0
  72. package/src/lib/constants.js +13 -0
  73. package/src/lib/debounce.js +9 -0
  74. package/src/lib/generate_cookie_header.js +7 -0
  75. package/src/lib/generate_id.js +15 -0
  76. package/src/lib/get_joystick_environment.js +13 -0
  77. package/src/lib/log_request_errors.js +15 -0
  78. package/src/lib/nested_object_diff.js +164 -0
  79. package/src/lib/parse_json.js +12 -0
  80. package/src/lib/throw_framework_error.js +5 -0
  81. package/src/lib/types.js +59 -0
  82. package/src/mount/index.js +45 -0
  83. package/src/test/create_file.js +10 -0
  84. package/src/test/index.js +13 -0
  85. package/src/test/track_function_call.js +16 -0
  86. package/src/tree/add_node_to_tree.js +6 -0
  87. package/src/tree/clean_up.js +9 -0
  88. package/src/tree/get_child_instance_ids.js +7 -0
  89. package/src/tree/get_children_from_tree.js +25 -0
  90. package/src/tree/get_css_from_tree.js +43 -0
  91. package/src/tree/get_event_listeners_for_nodes.js +15 -0
  92. package/src/tree/get_node_from_tree.js +7 -0
  93. package/src/tree/get_parent_from_tree.js +8 -0
  94. package/src/tree/jobs/index.js +210 -0
  95. package/src/tree/jobs/run.js +11 -0
  96. package/src/tree/replace_child_in_vdom.js +18 -0
  97. package/src/upload/index.js +72 -0
  98. package/src/websockets/client.js +125 -0
  99. package/src/websockets/register_on_component.js +27 -0
  100. package/test.js +145 -0
  101. package/README.md +0 -8
  102. package/_package.json +0 -26
  103. package/canary.js +0 -12
  104. package/dist/accounts/authenticated.js +0 -1
  105. package/dist/accounts/index.js +0 -1
  106. package/dist/accounts/login.js +0 -1
  107. package/dist/accounts/logout.js +0 -1
  108. package/dist/accounts/recoverPassword.js +0 -1
  109. package/dist/accounts/request.js +0 -1
  110. package/dist/accounts/resetPassword.js +0 -1
  111. package/dist/accounts/signup.js +0 -1
  112. package/dist/accounts/user.js +0 -1
  113. package/dist/api/get.js +0 -1
  114. package/dist/api/index.js +0 -1
  115. package/dist/api/set.js +0 -1
  116. package/dist/attachJoystickToWindow.js +0 -1
  117. package/dist/cache/class.js +0 -1
  118. package/dist/cache/index.js +0 -1
  119. package/dist/component/class/css/compile.js +0 -21
  120. package/dist/component/class/css/prefix.js +0 -1
  121. package/dist/component/class/data/compile.js +0 -1
  122. package/dist/component/class/data/fetch.js +0 -1
  123. package/dist/component/class/data/findComponentDataFromSSR.js +0 -1
  124. package/dist/component/class/data/loadDataFromWindow.js +0 -1
  125. package/dist/component/class/events/_registerListeners.js +0 -1
  126. package/dist/component/class/events/_unregisterListeners.js +0 -1
  127. package/dist/component/class/events/registerListeners.js +0 -1
  128. package/dist/component/class/events/serialize.js +0 -1
  129. package/dist/component/class/events/unregisterListeners.js +0 -1
  130. package/dist/component/class/index.js +0 -21
  131. package/dist/component/class/lifecycle/compile.js +0 -1
  132. package/dist/component/class/methods/compile.js +0 -1
  133. package/dist/component/class/options/allowedComponentOptions.js +0 -1
  134. package/dist/component/class/options/allowedDOMEvents.js +0 -1
  135. package/dist/component/class/options/allowedLifecycleMethods.js +0 -1
  136. package/dist/component/class/options/defaultLifecycleMethods.js +0 -1
  137. package/dist/component/class/options/hasAllRequiredOptions.js +0 -1
  138. package/dist/component/class/options/registerOptions.js +0 -21
  139. package/dist/component/class/options/requiredOptions.js +0 -1
  140. package/dist/component/class/options/validateOptions.js +0 -1
  141. package/dist/component/class/options/validators/css.js +0 -1
  142. package/dist/component/class/options/validators/events.js +0 -1
  143. package/dist/component/class/options/validators/index.js +0 -1
  144. package/dist/component/class/options/validators/lifecycle.js +0 -1
  145. package/dist/component/class/options/validators/methods.js +0 -1
  146. package/dist/component/class/options/validators/name.js +0 -1
  147. package/dist/component/class/options/validators/render.js +0 -1
  148. package/dist/component/class/options/validators/websockets.js +0 -1
  149. package/dist/component/class/options/validators/wrapper.js +0 -1
  150. package/dist/component/class/props/compile.js +0 -1
  151. package/dist/component/class/props/compileDefault.js +0 -1
  152. package/dist/component/class/render/clearTimersOnChildren.js +0 -1
  153. package/dist/component/class/render/forMount.js +0 -1
  154. package/dist/component/class/render/getExistingPropsMap.js +0 -1
  155. package/dist/component/class/render/getExistingStateMap.js +0 -1
  156. package/dist/component/class/render/getUpdatedDOM.js +0 -1
  157. package/dist/component/class/render/sanitizeHTML.js +0 -1
  158. package/dist/component/class/render/toHTML.js +0 -1
  159. package/dist/component/class/render/wrapHTML.js +0 -1
  160. package/dist/component/class/renderMethods/compile.js +0 -1
  161. package/dist/component/class/renderMethods/component.js +0 -1
  162. package/dist/component/class/renderMethods/dragDrop/attachEventListeners.js +0 -1
  163. package/dist/component/class/renderMethods/dragDrop/concept.js +0 -0
  164. package/dist/component/class/renderMethods/dragDrop/draggable.js +0 -1
  165. package/dist/component/class/renderMethods/dragDrop/droppable.js +0 -1
  166. package/dist/component/class/renderMethods/dragDrop/index.js +0 -1
  167. package/dist/component/class/renderMethods/dragDrop/isBefore.js +0 -1
  168. package/dist/component/class/renderMethods/each.js +0 -1
  169. package/dist/component/class/renderMethods/i18n.js +0 -1
  170. package/dist/component/class/renderMethods/index.js +0 -1
  171. package/dist/component/class/renderMethods/when.js +0 -1
  172. package/dist/component/class/state/compile.js +0 -1
  173. package/dist/component/class/url/compile.js +0 -1
  174. package/dist/component/class/virtualDOM/build.js +0 -1
  175. package/dist/component/class/virtualDOM/buildTree.js +0 -1
  176. package/dist/component/class/virtualDOM/diff/attributes.js +0 -1
  177. package/dist/component/class/virtualDOM/diff/children.js +0 -1
  178. package/dist/component/class/virtualDOM/diff/elementPatchFunctions.js +0 -1
  179. package/dist/component/class/virtualDOM/diff/index.js +0 -1
  180. package/dist/component/class/virtualDOM/diff/mapPatchFunctionsToNodes.js +0 -1
  181. package/dist/component/class/virtualDOM/renderTreeToDOM.js +0 -1
  182. package/dist/component/index.js +0 -21
  183. package/dist/component/renderComponentToHTML.js +0 -1
  184. package/dist/component/tree/addChildToParent.js +0 -1
  185. package/dist/component/tree/clearChildrenOnParent.js +0 -1
  186. package/dist/component/tree/findComponentInTreeByField.js +0 -1
  187. package/dist/component/tree/replaceChildInVDOMTree.js +0 -1
  188. package/dist/component/tree/updateParentInstanceInTree.js +0 -1
  189. package/dist/css/getCSSFromTree.js +0 -21
  190. package/dist/css/update.js +0 -21
  191. package/dist/forms/validate.js +0 -1
  192. package/dist/forms/validators/creditCard.js +0 -1
  193. package/dist/forms/validators/email.js +0 -1
  194. package/dist/forms/validators/equals.js +0 -1
  195. package/dist/forms/validators/index.js +0 -1
  196. package/dist/forms/validators/matches.js +0 -1
  197. package/dist/forms/validators/maxLength.js +0 -1
  198. package/dist/forms/validators/minLength.js +0 -1
  199. package/dist/forms/validators/phone.js +0 -1
  200. package/dist/forms/validators/postalCode.js +0 -1
  201. package/dist/forms/validators/required.js +0 -1
  202. package/dist/forms/validators/semVer.js +0 -1
  203. package/dist/forms/validators/slug.js +0 -1
  204. package/dist/forms/validators/strongPassword.js +0 -1
  205. package/dist/forms/validators/types.js +0 -1
  206. package/dist/forms/validators/url.js +0 -1
  207. package/dist/forms/validators/vat.js +0 -1
  208. package/dist/html/index.js +0 -1
  209. package/dist/lib/addToQueue.js +0 -1
  210. package/dist/lib/assignOptionsToInstance.js +0 -1
  211. package/dist/lib/constants.js +0 -1
  212. package/dist/lib/debounce.js +0 -1
  213. package/dist/lib/generateId.js +0 -1
  214. package/dist/lib/isNode.js +0 -1
  215. package/dist/lib/isValidAttributeName.js +0 -1
  216. package/dist/lib/logRequestErrors.js +0 -1
  217. package/dist/lib/parseJSON.js +0 -1
  218. package/dist/lib/processQueue.js +0 -1
  219. package/dist/lib/queueArray.js +0 -1
  220. package/dist/lib/serializeEvents.js +0 -1
  221. package/dist/lib/stringContainsHTML.js +0 -1
  222. package/dist/lib/throttle.js +0 -1
  223. package/dist/lib/throwFrameworkError.js +0 -1
  224. package/dist/lib/types.js +0 -1
  225. package/dist/lib/windowIsUndefined.js +0 -1
  226. package/dist/mount/appendToTarget.js +0 -1
  227. package/dist/mount/index.js +0 -1
  228. package/dist/mount/initializeJoystickComponentTree.js +0 -1
  229. package/dist/overrideTimers.js +0 -1
  230. package/dist/upload/index.js +0 -1
  231. package/dist/websockets/client.js +0 -1
@@ -0,0 +1,44 @@
1
+ import api from '../../api/index.js';
2
+ import fetch_data from './fetch.js';
3
+ import generate_id from "../../lib/generate_id.js";
4
+ import track_function_call from "../../test/track_function_call.js";
5
+ import run_tree_job from '../../tree/jobs/run.js';
6
+
7
+ const compile = (data_from_window = {}, request_from_window = {}, component_instance = {}) => {
8
+ return {
9
+ ...data_from_window,
10
+ refetch: async (input = {}) => {
11
+ track_function_call(`ui.${component_instance?.options?.test?.name || generate_id()}.data.refetch`, [
12
+ input
13
+ ]);
14
+
15
+ const data = await fetch_data(
16
+ api,
17
+ request_from_window,
18
+ input,
19
+ component_instance,
20
+ );
21
+
22
+ component_instance.data = compile(data, request_from_window, component_instance);
23
+
24
+ // NOTE: Make this conditional to avoid errors when refetching data in tests.
25
+ if (window.__joystick_data__[component_instance?.id]) {
26
+ // NOTE: Keep data on window up to date so if a parent re-renders a child that's refetched it's data since
27
+ // mount, the data rendered isn't stale.
28
+ window.__joystick_data__[component_instance?.id] = atob(data);
29
+ }
30
+
31
+ if (!window?.__joystick_test__) {
32
+ component_instance.rerender({
33
+ after_refetch_data_rerender: () => {
34
+ run_tree_job('lifecycle.onRefetchData', { root_instance_id: component_instance?.instance_id });
35
+ },
36
+ });
37
+ }
38
+
39
+ return component_instance.data;
40
+ },
41
+ };
42
+ }
43
+
44
+ export default compile;
@@ -0,0 +1,12 @@
1
+ import types from "../../lib/types.js";
2
+
3
+ const fetch = async (api = {}, req = {}, input = {}, component_instance = {}) => {
4
+ if (component_instance?.options?.data && types.is_function(component_instance.options.data)) {
5
+ const data = await component_instance.options.data(api, req, input, component_instance);
6
+ return Promise.resolve(data);
7
+ }
8
+
9
+ return Promise.resolve();
10
+ };
11
+
12
+ export default fetch;
@@ -0,0 +1,14 @@
1
+ import compile_data from "./compile.js";
2
+
3
+ const load_data_from_window = (component_instance = {}) => {
4
+ if (typeof window !== 'undefined') {
5
+ const decrypted_data_from_window = window.__joystick_data__ ? JSON.parse(atob(window.__joystick_data__)) : {};
6
+ const data_from_window = (decrypted_data_from_window && decrypted_data_from_window[component_instance?.id]) || null;
7
+ const request_from_window = window.__joystick_request__ || {};
8
+ return compile_data(data_from_window, request_from_window, component_instance);
9
+ }
10
+
11
+ return component_instance?.data || {};
12
+ };
13
+
14
+ export default load_data_from_window;
@@ -0,0 +1,11 @@
1
+ const is_valid_attribute_name = (name = '') => {
2
+ try {
3
+ const element = document.createElement('div');
4
+ element.setAttribute(name, 'Test');
5
+ return true;
6
+ } catch {
7
+ return false;
8
+ }
9
+ };
10
+
11
+ export default is_valid_attribute_name;
@@ -0,0 +1,32 @@
1
+ import generate_id from "../../lib/generate_id.js";
2
+ import track_function_call from "../../test/track_function_call.js";
3
+
4
+ const attach_event_listener_for_element = (event_listener = {}, element = {}, render_methods = {}) => {
5
+ const handler = function event_handler(DOMEvent) {
6
+ event_listener.handler(DOMEvent, {
7
+ ...(event_listener?.instance || {}),
8
+ set_state: event_listener?.instance?.setState.bind(event_listener?.instance),
9
+ setState: event_listener?.instance?.setState.bind(event_listener?.instance),
10
+ ...(render_methods || {}),
11
+ });
12
+
13
+ track_function_call(`ui.${event_listener?.instance?.options?.test?.name || generate_id()}.events.${event_listener.type}.selector_${event_listener?.selector}`, [
14
+ DOMEvent,
15
+ {
16
+ ...(event_listener?.instance || {}),
17
+ set_state: event_listener?.instance?.setState.bind(event_listener?.instance),
18
+ setState: event_listener?.instance?.setState.bind(event_listener?.instance),
19
+ ...(render_methods || {}),
20
+ }
21
+ ]);
22
+ };
23
+
24
+ element.addEventListener(event_listener?.type, handler);
25
+
26
+ event_listener.instance._event_listeners = [
27
+ ...(event_listener?.instance?._event_listeners || []),
28
+ { selector: event_listener?.selector, type: event_listener?.type, element, handler },
29
+ ];
30
+ };
31
+
32
+ export default attach_event_listener_for_element;
@@ -0,0 +1,12 @@
1
+ const serialize = (events = {}) => {
2
+ return Object.entries(events).map(([eventSelector, eventHandler]) => {
3
+ const [type, ...selector] = eventSelector.split(" ");
4
+ return {
5
+ type: type.toLowerCase(),
6
+ selector: selector.join(' '),
7
+ handler: eventHandler,
8
+ };
9
+ });
10
+ };
11
+
12
+ export default serialize;
@@ -0,0 +1,17 @@
1
+ import Component from "./class.js";
2
+ import throw_framework_error from "../lib/throw_framework_error.js";
3
+
4
+ const component = (definition = {}) => {
5
+ try {
6
+ return (render_options = {}) => {
7
+ return new Component({
8
+ ...definition,
9
+ ...render_options,
10
+ })
11
+ };
12
+ } catch (error) {
13
+ throw_framework_error('component', error);
14
+ }
15
+ };
16
+
17
+ export default component;
@@ -0,0 +1,28 @@
1
+ import generate_id from "../../lib/generate_id.js";
2
+ import track_function_call from "../../test/track_function_call.js";
3
+
4
+ const compile = (component_instance = {}, methods = {}) => {
5
+ return Object.entries(methods).reduce(
6
+ (methods = {}, [method_name, method_function]) => {
7
+ methods[method_name] = (...args) => {
8
+ track_function_call(`ui.${component_instance?.options?.test?.name || generate_id()}.methods.${method_name}`, [...args, {
9
+ ...component_instance,
10
+ set_state: component_instance.setState.bind(component_instance),
11
+ setState: component_instance.setState.bind(component_instance),
12
+ ...(component_instance.compile_render_methods({}, {}, typeof window === 'undefined' ? [] : null) || {}),
13
+ }]);
14
+
15
+ return method_function(...args, {
16
+ ...component_instance,
17
+ set_state: component_instance.setState.bind(component_instance),
18
+ setState: component_instance.setState.bind(component_instance),
19
+ ...(component_instance.compile_render_methods({}, {}, typeof window === 'undefined' ? [] : null) || {}),
20
+ });
21
+ };
22
+ return methods;
23
+ },
24
+ {}
25
+ );
26
+ };
27
+
28
+ export default compile;
@@ -0,0 +1,21 @@
1
+ import types from "../../lib/types.js";
2
+
3
+ const compile = (default_props_from_options = {}, props_from_options = {}) => {
4
+ // NOTE: Combine props and defaultProps key names and filter to uniques only.
5
+ const props = [...Object.keys(props_from_options), ...Object.keys(default_props_from_options)]
6
+ .filter((value, index, self) => {
7
+ return self.indexOf(value) === index;
8
+ });
9
+
10
+ return props.reduce((compiled_props, prop_name) => {
11
+ const prop = props_from_options[prop_name];
12
+ const prop_default = default_props_from_options[prop_name] || null;
13
+ const prop_has_value = !types.is_undefined(prop) && !types.is_null(prop);
14
+
15
+ compiled_props[prop_name] = prop_has_value ? prop : prop_default;
16
+
17
+ return compiled_props;
18
+ }, {});
19
+ };
20
+
21
+ export default compile;
@@ -0,0 +1,21 @@
1
+ import types from "../../lib/types";
2
+
3
+ const compile = (component_instance = {}, default_props = {}) => {
4
+ const compiled_default_props = default_props(component_instance);
5
+
6
+ if (compiled_default_props && types.is_object(compiled_default_props) && !types.is_array(compiled_default_props)) {
7
+ return Object.assign({}, compiled_default_props);
8
+ }
9
+
10
+ return {};
11
+ };
12
+
13
+ const compile_default = (component_instance = {}, default_props = {}) => {
14
+ if (types.is_function(default_props)) {
15
+ return compile(component_instance, default_props);
16
+ }
17
+
18
+ return Object.assign({}, default_props);
19
+ };
20
+
21
+ export default compile_default;
@@ -0,0 +1,44 @@
1
+ import compile_default_props from './props/compile_default.js';
2
+ import compile_methods from './methods/compile.js';
3
+ import compile_props from './props/compile.js';
4
+ import compile_state from './state/compile.js';
5
+ import compile_url from './url/compile.js';
6
+ import generate_id from '../lib/generate_id.js';
7
+ import load_data_from_window from './data/load_data_from_window.js';
8
+ import validate_form from '../forms/validate.js';
9
+ import register_websockets_on_component from '../websockets/register_on_component.js';
10
+ import types from '../lib/types.js';
11
+
12
+ const register_component_options = (component_instance = {}, component_options = {}) => {
13
+ // NOTE: Set this first as we reference internally in some of the functions below.
14
+ component_instance.id = component_options?._componentId || null;
15
+
16
+ component_instance.css = component_options?.css;
17
+ component_instance.data = typeof window !== 'undefined' ? load_data_from_window(component_instance) : {};
18
+ component_instance.defaultProps = compile_default_props(component_instance, component_options?.defaultProps || {});
19
+ component_instance.default_props = compile_default_props(component_instance, component_options?.default_props || {});
20
+ component_instance.dom = {};
21
+ component_instance.DOMNode = null;
22
+ component_instance.events = component_options?.events;
23
+ component_instance.instance_id = generate_id(8);
24
+ component_instance.lifecycle = component_options?.lifecycle || {};
25
+ component_instance.methods = compile_methods(component_instance, component_options?.methods || {});
26
+ component_instance.options = component_options;
27
+ component_instance.props = compile_props(component_options?.defaultProps || component_options?.default_props, component_options?.props || {});
28
+ component_instance.state = compile_state(component_instance, component_options?.state || {});
29
+ component_instance.test = component_options?.test;
30
+ component_instance.timers = [];
31
+ component_instance.translations = component_options?.translations || {};
32
+ component_instance.url = typeof window !== 'undefined' ? compile_url(window.__joystick_url__) : compile_url(component_options?.url);
33
+ component_instance.user = typeof window !== 'undefined' ? window.__joystick_user__ : component_options?.user;
34
+ component_instance.validateForm = validate_form;
35
+ component_instance.validate_form = validate_form;
36
+ component_instance.virtual_dom = {};
37
+ component_instance.wrapper = {};
38
+
39
+ if (typeof window !== 'undefined' && component_options?.websockets && types.is_function(component_options?.websockets)) {
40
+ register_websockets_on_component(component_options, component_instance);
41
+ }
42
+ };
43
+
44
+ export default register_component_options;
@@ -0,0 +1,53 @@
1
+ import add_node_to_tree from '../../tree/add_node_to_tree.js';
2
+ import get_node_from_tree from "../../tree/get_node_from_tree.js";
3
+ import nested_object_diff from '../../lib/nested_object_diff.js';
4
+ import types from '../../lib/types.js';
5
+
6
+ const component = function component(Component = {}, props = {}) {
7
+ // NOTE: We bind the component() render method to the parent component instance
8
+ // inside of the component class definition.
9
+ const parent = this;
10
+ const component_instance = Component({
11
+ parent,
12
+ props,
13
+ translations: parent?.translations,
14
+ url: parent?.url,
15
+ });
16
+
17
+ component_instance.parent = parent;
18
+
19
+ const existing_component_on_parent = parent?.existing_children[component_instance?.id];
20
+ const new_component_on_parent = parent?.new_children[component_instance?.id];
21
+ const existing_instance_id_on_parent = existing_component_on_parent && existing_component_on_parent[new_component_on_parent?.length || 0];
22
+ const existing_node_in_tree = get_node_from_tree(existing_instance_id_on_parent);
23
+
24
+ if (existing_node_in_tree?.state) {
25
+ component_instance.state = existing_node_in_tree?.state;
26
+ }
27
+
28
+ if (types.is_function(component_instance?.lifecycle?.onUpdateProps) || types.is_function(component_instance?.lifecycle?.on_update_props)) {
29
+ const has_different_props = nested_object_diff(existing_node_in_tree?.props, props);
30
+ if (has_different_props) {
31
+ (component_instance.lifecycle.onUpdateProps || component_instance.lifecycle.on_update_props)(existing_node_in_tree?.props, props, component_instance);
32
+ }
33
+ }
34
+
35
+ const new_children = {};
36
+ const existing_children = {};
37
+ const component_html = component_instance.render_to_html(new_children, existing_children);
38
+ const html = component_instance.replace_when_tags(component_html);
39
+ const dom = component_instance.render_html_to_dom(html);
40
+ const virtual_dom = component_instance.render_dom_to_virtual_dom(dom);
41
+
42
+ component_instance.dom = dom;
43
+ component_instance.DOMNode = dom;
44
+ component_instance.virtual_dom = virtual_dom;
45
+ component_instance.children = new_children;
46
+
47
+ add_node_to_tree(component_instance);
48
+ parent.track_child(component_instance);
49
+
50
+ return html;
51
+ };
52
+
53
+ export default component;
@@ -0,0 +1,22 @@
1
+ import add_node_to_tree from '../../tree/add_node_to_tree.js';
2
+
3
+ const component_ssr = function component_ssr(Component = {}, props = {}) {
4
+ // NOTE: We bind the component_ssr() render method to the parent component_ssr instance
5
+ // inside of the component_ssr class definition.
6
+
7
+ const parent = this;
8
+ const component_instance = Component({
9
+ parent,
10
+ props,
11
+ translations: parent?.translations,
12
+ url: parent?.url,
13
+ });
14
+
15
+ component_instance.parent = parent;
16
+
17
+ add_node_to_tree(component_instance, this.ssr_tree);
18
+
19
+ return `{{${component_instance.id}:${component_instance.instance_id}}}`;
20
+ };
21
+
22
+ export default component_ssr;
@@ -0,0 +1,19 @@
1
+ import types from "../../lib/types.js";
2
+
3
+ const each = function each(items = [], callback = null) {
4
+ if (!types.is_function(callback)) {
5
+ return '';
6
+ }
7
+
8
+ if (items && types.is_array(items)) {
9
+ return items
10
+ .map((item, itemIndex) => {
11
+ return callback(item, itemIndex);
12
+ })
13
+ .join("");
14
+ }
15
+
16
+ return '';
17
+ };
18
+
19
+ export default each;
@@ -0,0 +1,50 @@
1
+ const replace_placeholders_in_string = (string = '', replacements_as_array = []) => {
2
+ return replacements_as_array.reduce((translation, [replacement_key, replacement_value]) => {
3
+ return translation.replace(`{{${replacement_key}}}`, replacement_value);
4
+ }, string);
5
+ };
6
+
7
+ const handle_translation_replacement = (translation = '', replacements_as_array = []) => {
8
+ const translation_is_string = typeof translation === 'string';
9
+ const translation_is_array = Array.isArray(translation);
10
+ const translation_is_object = typeof translation === 'object' && !translation_is_array;
11
+
12
+ if (translation_is_string) {
13
+ return replace_placeholders_in_string(translation, replacements_as_array);
14
+ }
15
+
16
+ if (translation_is_array) {
17
+ return translation?.map((translation_in_array) => {
18
+ return handle_translation_replacement(translation_in_array, replacements_as_array);
19
+ });
20
+ }
21
+
22
+ if (translation_is_object) {
23
+ return Object.entries(translation)?.reduce((object_with_replacements = {}, [key, value]) => {
24
+ object_with_replacements[key] = handle_translation_replacement(value, replacements_as_array);
25
+ return object_with_replacements;
26
+ }, {});
27
+ }
28
+ };
29
+
30
+ const get_translation_at_path = (key = '', translations = {}) => {
31
+ return key?.split('.').reduce((object_to_traverse, key_to_match)=> {
32
+ return (object_to_traverse && object_to_traverse[key_to_match]) || '';
33
+ }, translations);
34
+ };
35
+
36
+ const i18n = function(key = '', replacements = {}) {
37
+ const translations = typeof window !== 'undefined' ? window.__joystick_i18n__ : this.translations;
38
+ const is_dot_notation_path = key?.includes('.');
39
+ const translation = is_dot_notation_path ? get_translation_at_path(key, translations) : translations[key];
40
+
41
+ if (!translations || !translation) {
42
+ return '';
43
+ }
44
+
45
+ const replacements_as_array = Object.entries(replacements);
46
+
47
+ return handle_translation_replacement(translation, replacements_as_array);
48
+ };
49
+
50
+ export default i18n;
@@ -0,0 +1,19 @@
1
+ import component from './component.js';
2
+ import component_ssr from './component_ssr.js';
3
+ import each from './each.js';
4
+ import i18n from './i18n.js';
5
+ import when from './when.js';
6
+
7
+ const render_methods = {
8
+ c: component,
9
+ component,
10
+ component_ssr,
11
+ e: each,
12
+ each,
13
+ i: i18n,
14
+ i18n,
15
+ w: when,
16
+ when,
17
+ };
18
+
19
+ export default render_methods;
@@ -0,0 +1,20 @@
1
+ import types from "../../lib/types.js";
2
+
3
+ const when = function when(test = false, html_to_render = '') {
4
+ if (types.is_function(html_to_render)) {
5
+ return test ? html_to_render() : '<when> </when>';
6
+ }
7
+
8
+ if (test) {
9
+ // NOTE: Run a trim() to avoid whitespace/newlines in HTML passed to <when>
10
+ // generating unnecessary text nodes that leave <when> tags in the render.
11
+ return html_to_render.trim();
12
+ }
13
+
14
+ // NOTE: Return default as a text node containing false (we replace this dynamically when
15
+ // building the DOM so it never renders in the browser). Blank space here is INTENTIONAL
16
+ // to ensure a text node is returned when the <when></when> tag is dynamically replaced.
17
+ return '<when> </when>';
18
+ };
19
+
20
+ export default when;
@@ -0,0 +1,21 @@
1
+ import types from '../../lib/types.js';
2
+
3
+ const run_state_function = (component_instance = {}, state = null) => {
4
+ const compiled_state = state(component_instance);
5
+
6
+ if (compiled_state && types.is_object(compiled_state) && !types.is_array(compiled_state)) {
7
+ return Object.assign({}, compiled_state);
8
+ }
9
+
10
+ return {};
11
+ };
12
+
13
+ const compile_state = (component_instance = {}, state = {}) => {
14
+ if (types.is_function(state)) {
15
+ return run_state_function(component_instance, state);
16
+ }
17
+
18
+ return Object.assign({}, state);
19
+ };
20
+
21
+ export default compile_state;
@@ -0,0 +1,39 @@
1
+ import types from "../../lib/types.js";
2
+
3
+ const is_active = (path = "", url = {}) => {
4
+ if (types.is_string(path) && url?.route !== "*") {
5
+ return (
6
+ path ===
7
+ (typeof location !== "undefined" ? location.pathname : url.path)
8
+ );
9
+ }
10
+
11
+ return false;
12
+ };
13
+
14
+ const compile = (url = {}) => {
15
+ return {
16
+ ...url,
17
+ query: {
18
+ ...(url?.query || {}),
19
+ set: (parameterName = "", parameterValue = "") => {
20
+ if (typeof window !== "undefined") {
21
+ const url = new URL(window.location);
22
+ url.searchParams.append(parameterName, parameterValue);
23
+ window.history.pushState({}, "", url);
24
+ }
25
+ },
26
+ unset: (parameterName = "") => {
27
+ if (typeof window !== "undefined") {
28
+ const url = new URL(window.location);
29
+ url.searchParams.delete(parameterName);
30
+ window.history.pushState({}, "", url);
31
+ }
32
+ },
33
+ },
34
+ is_active: (path = '') => is_active(path, url),
35
+ isActive: (path = '') => is_active(path, url),
36
+ };
37
+ };
38
+
39
+ export default compile;
@@ -0,0 +1,9 @@
1
+ import required_options from './required_options.js';
2
+
3
+ const has_required_options = (component_options = {}) => {
4
+ return required_options.every((required_options) => {
5
+ return Object.keys(component_options).includes(required_options);
6
+ });
7
+ };
8
+
9
+ export default has_required_options;
@@ -0,0 +1,16 @@
1
+ import has_required_options from "./has_required_options.js";
2
+ import required_options from "./required_options.js";
3
+ import throw_framework_error from "../../lib/throw_framework_error.js";
4
+
5
+ const validate_component_options = (component_options = {}) => {
6
+ const missing_required_options = has_required_options(component_options);
7
+
8
+ if (missing_required_options) {
9
+ throw_framework_error(
10
+ 'joystick.validate_component_options',
11
+ new Error(`Component options must include:\n\n${required_options.join("\n")}`)
12
+ );
13
+ }
14
+ };
15
+
16
+ export default validate_component_options;
@@ -0,0 +1,5 @@
1
+ const required_options = [
2
+ 'render',
3
+ ];
4
+
5
+ export default required_options;
@@ -0,0 +1,61 @@
1
+ import diff_attributes from './diff_attributes.js';
2
+ import diff_children from './diff_children.js';
3
+ import element_patch_functions from './element_patch_functions.js';
4
+ import render_virtual_dom_to_dom from './render_virtual_dom_to_dom.js';
5
+
6
+ const get_replace_node_patch = (new_virtual_node) => {
7
+ return (node) => {
8
+ const new_dom_node = new_virtual_node ? render_virtual_dom_to_dom(new_virtual_node) : null;
9
+
10
+ if (node) {
11
+ node.replaceWith(new_dom_node);
12
+ }
13
+
14
+ return new_dom_node;
15
+ };
16
+ };
17
+
18
+ const get_remove_node_patch = () => {
19
+ return (node) => {
20
+ if (node) {
21
+ node.remove();
22
+ }
23
+
24
+ return undefined;
25
+ };
26
+ };
27
+
28
+ const diff = (old_virtual_node = undefined, new_virtual_node = undefined) => {
29
+ if (old_virtual_node === undefined || new_virtual_node === undefined) {
30
+ return get_remove_node_patch();
31
+ }
32
+
33
+ const is_patching_string = typeof old_virtual_node === 'string' || typeof new_virtual_node === "string";
34
+
35
+ if (is_patching_string) {
36
+ return get_replace_node_patch(new_virtual_node, old_virtual_node);
37
+ }
38
+
39
+ const node_tag_name_changed = old_virtual_node.tag_name !== new_virtual_node.tag_name;
40
+
41
+ if (node_tag_name_changed) {
42
+ return get_replace_node_patch(new_virtual_node);
43
+ }
44
+
45
+ if (new_virtual_node.tag_name === 'select') {
46
+ return element_patch_functions.select(old_virtual_node, new_virtual_node);
47
+ }
48
+
49
+ if (['pre', 'code'].includes(new_virtual_node.tag_name)) {
50
+ return get_replace_node_patch(new_virtual_node, old_virtual_node);
51
+ }
52
+
53
+ return (node) => {
54
+ diff_attributes(old_virtual_node.attributes, new_virtual_node.attributes)(node);
55
+ diff_children(old_virtual_node.children, new_virtual_node.children)(node);
56
+
57
+ return node;
58
+ };
59
+ };
60
+
61
+ export default diff;
@@ -0,0 +1,37 @@
1
+ import is_valid_attribute_name from '../dom/is_valid_attribute_name.js';
2
+
3
+ const diff_attributes = (old_attributes = {}, new_attributes = {}) => {
4
+ const patches = [];
5
+
6
+ for (const [attribute_key, attribute_value] of Object.entries(new_attributes)) {
7
+ patches.push((node) => {
8
+ if (node && node.setAttribute && is_valid_attribute_name(attribute_key)) {
9
+ node.setAttribute(attribute_key, attribute_value);
10
+ }
11
+
12
+ return node;
13
+ });
14
+ }
15
+
16
+ for (const attribute_key in old_attributes) {
17
+ if (!(attribute_key in new_attributes)) {
18
+ patches.push((node) => {
19
+ if (node && node.removeAttribute) {
20
+ node.removeAttribute(attribute_key);
21
+ }
22
+
23
+ return node;
24
+ });
25
+ }
26
+ }
27
+
28
+ return (node) => {
29
+ for (const patch of patches) {
30
+ if (patch && typeof patch === "function") {
31
+ patch(node);
32
+ }
33
+ }
34
+ };
35
+ };
36
+
37
+ export default diff_attributes;