wallace 0.16.0 → 0.17.1

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/lib/component.js CHANGED
@@ -1,10 +1,14 @@
1
1
  const throwAway = document.createElement("template");
2
2
  const NO_LOOKUP = "__";
3
3
 
4
+ const defaultSetFunction = function (props, /* #INCLUDE-IF: allowCtrl */ ctrl) {
5
+ this.props = props;
6
+ /* #INCLUDE-IF: allowCtrl */ this.ctrl = ctrl;
7
+ };
8
+
4
9
  const ComponentPrototype = {
5
10
  render: function (props, /* #INCLUDE-IF: allowCtrl */ ctrl) {
6
- this.props = props;
7
- /* #INCLUDE-IF: allowCtrl */ this.ctrl = ctrl;
11
+ this.set(props, /* #INCLUDE-IF: allowCtrl */ ctrl);
8
12
  this.update();
9
13
  },
10
14
 
@@ -57,7 +61,7 @@ const ComponentPrototype = {
57
61
  detacher = displayToggle.d;
58
62
  if (query !== undefined) {
59
63
  lookupTrue = !!lookups[query](props, this);
60
- shouldBeVisible = displayToggle.r ? lookupTrue : !lookupTrue;
64
+ shouldBeVisible = displayToggle.r ? !lookupTrue : lookupTrue;
61
65
  }
62
66
  if (detacher) {
63
67
  detacher.apply(element, shouldBeVisible, elements, stash);
@@ -152,12 +156,14 @@ export const defineComponent = (
152
156
  watches,
153
157
  queries,
154
158
  contructor,
159
+ setFunction,
155
160
  /* #INCLUDE-IF: allowDismount */ dismountKeys,
156
161
  inheritFrom
157
162
  ) => {
158
163
  const ComponentDefinition = initConstructor(contructor, inheritFrom || ComponentBase);
159
164
  const proto = ComponentDefinition.prototype;
160
165
  throwAway.innerHTML = html;
166
+ proto.set = setFunction || defaultSetFunction;
161
167
  proto._w = watches;
162
168
  proto._q = queries;
163
169
  proto._t = throwAway.content.firstChild;
package/lib/router.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- The Router is a component which mounts other components based on hash (URL bit after #).
2
+ The Router is a component which mounts other components based on URL hash.
3
3
 
4
4
  It currently expects the route to be made of chunks separated by / which are either
5
5
  text or placeholders:
@@ -24,10 +24,31 @@ export const Router = () => <div></div>;
24
24
 
25
25
  Object.assign(Router.prototype, {
26
26
  render(props, /* #INCLUDE-IF: allowCtrl */ ctrl) {
27
- const defaultError = (error, router) => (router.el.innerHTML = error.message);
28
- this.error = props.error || defaultError;
27
+ if (wallaceConfig.flags.allowBase) {
28
+ } else {
29
+ throw new Error(
30
+ "Flag `allowBase` must be set to true in the config for this feature."
31
+ );
32
+ }
33
+ if (wallaceConfig.flags.allowDismount) {
34
+ } else {
35
+ throw new Error(
36
+ "Flag `allowDismount` must be set to true in the config for this feature."
37
+ );
38
+ }
39
+ this._alive = true;
40
+ this.error =
41
+ props.error ||
42
+ (error => {
43
+ throw error;
44
+ });
29
45
  this.current = null;
30
- events.forEach(e => window.addEventListener(e, () => this.onHashChange()));
46
+ this.handlers = events.map(e => {
47
+ const handler = () => this.onHashChange();
48
+ window.addEventListener(e, handler);
49
+ return { e, handler };
50
+ });
51
+
31
52
  if (props.atts) {
32
53
  Object.keys(props.atts).forEach(k => {
33
54
  this.el.setAttribute(k, props.atts[k]);
@@ -49,6 +70,7 @@ Object.assign(Router.prototype, {
49
70
  routeData,
50
71
  /* #INCLUDE-IF: allowCtrl */ this.ctrl
51
72
  );
73
+ if (!this._alive) return;
52
74
  this.current && this.current.cleanup();
53
75
  this.mount(component);
54
76
  this.current = route;
@@ -56,6 +78,7 @@ Object.assign(Router.prototype, {
56
78
  }
57
79
  i++;
58
80
  }
81
+ if (!this._alive) return;
59
82
  throw new Error(`Router unable to match path "${path}"`);
60
83
  } catch (error) {
61
84
  this.error(error, this);
@@ -64,6 +87,19 @@ Object.assign(Router.prototype, {
64
87
  mount(component) {
65
88
  this.el.textContent = "";
66
89
  this.el.appendChild(component.el);
90
+ },
91
+ /* #INCLUDE-IF: allowDismount */
92
+ dismount() {
93
+ this._alive = false;
94
+ if (this.current) {
95
+ this.current.cleanup();
96
+ }
97
+ this.base.dismount.call(this);
98
+ if (this._handlers) {
99
+ this._handlers.forEach(({ e, handler }) => {
100
+ window.removeEventListener(e, handler);
101
+ });
102
+ }
67
103
  }
68
104
  });
69
105
 
@@ -108,10 +144,10 @@ Route.prototype = {
108
144
  return this.component;
109
145
  },
110
146
  /**
111
- * Allows user to delete component or perform other cleanup.
147
+ * Allows user to dismount component or perform other cleanup.
112
148
  */
113
149
  cleanup() {
114
- this._cleanup(this);
150
+ this._cleanup(this.component);
115
151
  }
116
152
  };
117
153
 
package/lib/types.d.ts CHANGED
@@ -346,7 +346,6 @@ So long as the rendered component has all its stubs defined somewhere, it will w
346
346
 
347
347
  Notes:
348
348
 
349
- - Stubs receive the same props and controller as their containing component.
350
349
  - Stubs are separate components, so cannot access methods on the containing component
351
350
  through `self` (use the controller for that kind of thing).
352
351
 
@@ -657,6 +656,7 @@ declare module "wallace" {
657
656
  ref: { [key: string]: HTMLElement };
658
657
  part: { [key: string]: Part };
659
658
  base: Component<Props, Controller>;
659
+ dismount(): void;
660
660
  } & Component<Props, Controller> &
661
661
  Methods;
662
662
 
@@ -795,24 +795,33 @@ declare module "wallace" {
795
795
  export function route<Props>(
796
796
  path: string,
797
797
  componentDef: ComponentFunction<Props>,
798
- converter: RouteConverter<Props>
798
+ converter?: RouteConverter<Props>,
799
+ cleanup?: RouteCleanup<Props>
799
800
  ): Route<Props>;
800
801
 
801
802
  type RouteConverter<Props> = (routedata: RouteData) => Props;
802
-
803
- export type Route<Props> = [string, ComponentFunction<Props>, RouteConverter<Props>?];
803
+ type RouteCleanup<Props> = (component: ComponentInstance<Props>) => void;
804
+
805
+ export type Route<Props> = [
806
+ string,
807
+ ComponentFunction<Props>,
808
+ RouteConverter<Props>?,
809
+ RouteCleanup<Props>?
810
+ ];
804
811
  export type RouterProps = {
805
- routes: readonly Route<unknown>[];
806
- atts?: Record<string, unknown>;
812
+ routes: readonly Route<any>[];
813
+ atts?: Record<string, any>;
807
814
  error?: (error: Error, router: Router) => void;
808
815
  };
809
816
 
810
- export class Router extends Component {
811
- static nest?({ props }: { props?: RouterProps }): JSX.Element;
817
+ export type Router = ComponentFunction<RouterProps> & {
812
818
  mount(component: Component<any>): void;
813
- }
819
+ };
820
+
821
+ export const Router: Router;
814
822
  }
815
823
 
824
+ type OptionalExpression<T> = T | boolean;
816
825
  type MustBeExpression = Exclude<any, string>;
817
826
 
818
827
  /**
@@ -841,6 +850,37 @@ interface DirectiveAttributes extends AllDomEvents {
841
850
  */
842
851
  apply?: MustBeExpression;
843
852
 
853
+ /**
854
+ * ## Wallace directive: assign
855
+ *
856
+ * Assigns the component instance to a value during `render`, typically on the props
857
+ * or the controller.
858
+ *
859
+ * You can either use an expression:
860
+ *
861
+ * ```
862
+ * const MyComponent = ({ id }, { ctrl }) => (
863
+ * <div assign={ctrl.register[id]}>
864
+ * </div>
865
+ * );
866
+ * ```
867
+ *
868
+ * Or use a qualifier, which is read as being a field on the props:
869
+ *
870
+ * ```
871
+ * const MyComponent = () => (
872
+ * <div assign:c>
873
+ * </div>
874
+ * );
875
+ * ```
876
+ *
877
+ * Be careful not to assign to a watched property which updates this component or a
878
+ * parent, as that will create an infinite loop.
879
+ *
880
+ * May only be used on the root element. Modifies the `set` method.
881
+ */
882
+ assign?: ComponentInstance;
883
+
844
884
  /**
845
885
  * ## Wallace directive: bind
846
886
  *
@@ -936,6 +976,50 @@ interface DirectiveAttributes extends AllDomEvents {
936
976
  */
937
977
  fixed?: string;
938
978
 
979
+ /**
980
+ * ## Wallace directive: help
981
+ *
982
+ * Does nothing other than show this tooltip.
983
+ *
984
+ * ### Directives:
985
+ *
986
+ * - `apply` runs a callback to modify an element.
987
+ * - `assign` assigns the component instance to a value.
988
+ * - `bind` updates a value when an input is changed.
989
+ * - `class:xyz` defines a set of classes to be toggled.
990
+ * - `css` shorthand for `fixed:class`.
991
+ * - `ctrl` specifies ctrl for nested/repeated components.
992
+ * - `event` changes the event which `bind` reacts to.
993
+ * - `fixed:xyz` sets a attribute from an expression at definition.
994
+ * - `hide` sets an element or component's hidden property.
995
+ * - `html` Set the element's `innnerHTML` property.
996
+ * - `if` excludes an element from the DOM.
997
+ * - `key` specifies a key for repeated components.
998
+ * - `on[EventName]` creates an event handler (note the code is copied).
999
+ * - `part:xyz` saves a reference to part of a component so it can be updated.
1000
+ * - `props` specifies props for stubs, nested or repeated components.
1001
+ * - `ref:xyz` saves a reference to an element or nested component.
1002
+ * - `show` sets and element or component's hidden property.
1003
+ * - `style:xyz` sets a specific style property.
1004
+ * - `toggle:xyz` toggles `xyz` as defined by `class:xyz` on same element, or class
1005
+ * `xyz`.
1006
+ * - `unique` can be set on components which are only used once for better performance.
1007
+ * - `watch` watches the props or the controller.
1008
+ *
1009
+ * See more by hovering on a specific directive.
1010
+ * Qualifiers like `class:danger` break the tool tip. Try `class x:danger`.
1011
+ *
1012
+ * ### Nesting syntax:
1013
+ *
1014
+ * ```
1015
+ * <MyComponent props={singleProps} />
1016
+ * <MyComponent.repeat props={arrayOfProps} />
1017
+ * <MyComponent.repeat props={arrayOfProps} key="id"/>
1018
+ * <MyComponent.repeat props={arrayOfProps} key={(i) => i.id}/>
1019
+ * ```
1020
+ */
1021
+ help?: boolean;
1022
+
939
1023
  /** ## Wallace directive: hide
940
1024
  *
941
1025
  * Set the element's `hidden` property and if true, does not render dynamic elements
@@ -1053,14 +1137,57 @@ interface DirectiveAttributes extends AllDomEvents {
1053
1137
  * ## Wallace directive: unique
1054
1138
  *
1055
1139
  * Performance optimisation that can be applied to a component which is only used once.
1140
+ *
1141
+ * May only be used on the root element.
1056
1142
  */
1057
1143
  unique?: boolean;
1144
+
1145
+ /**
1146
+ * ## Wallace directive: watch
1147
+ *
1148
+ * Wraps the props in a `watch` call which updates the component by overriding the
1149
+ * `set` method:
1150
+ *
1151
+ * ```
1152
+ * function set(props, ctrl) {
1153
+ * this.props = watch(props, () => this.update());
1154
+ * this.ctrl = ctrl;
1155
+ * }
1156
+ * ```
1157
+ *
1158
+ * You can provide a different callback to `watch`:
1159
+ *
1160
+ * ```
1161
+ * const MyComponent = () => (
1162
+ * <div watch={(target, key, value) => foo()}></div>
1163
+ * );
1164
+ * ```
1165
+ *
1166
+ * For more complex use cases, import the `watch` function and use it in an overriden
1167
+ * `render` method.
1168
+ *
1169
+ * May only be used on the root element. Modifies the `set` method.
1170
+ */
1171
+ watch?: OptionalExpression<CallableFunction>;
1058
1172
  }
1059
1173
 
1060
1174
  // This makes this a module, which is needed to declare global, which is needed to make
1061
1175
  // LibraryManagedAttributes work.
1062
1176
  export {};
1063
1177
 
1178
+ type NativeIntrinsicElements = {
1179
+ [K in keyof HTMLElementTagNameMap]: DirectiveAttributes & {
1180
+ style?: string;
1181
+ class?: string;
1182
+ [attr: string]: any;
1183
+ };
1184
+ };
1185
+
1186
+ // WARNING - check that your changes here don't break standard html autocomplete
1187
+ // in JSX. The following should autocomplete button and allow style:
1188
+ //
1189
+ // <button style="width:20px" >
1190
+
1064
1191
  declare global {
1065
1192
  namespace JSX {
1066
1193
  // This allows <Foo props={x} if={y} />
@@ -1069,43 +1196,7 @@ declare global {
1069
1196
  type LibraryManagedAttributes<C, P> =
1070
1197
  C extends ComponentFunction<infer Props, any, any, any> ? Wrapper<Props> : P;
1071
1198
 
1072
- interface IntrinsicElements {
1073
- /**
1074
- * Nesting syntax:
1075
- * ```
1076
- * <MyComponent props={singleProps} />
1077
- * <MyComponent.repeat props={arrayOfProps} />
1078
- * <MyComponent.repeat props={arrayOfProps} key="id"/>
1079
- * <MyComponent.repeat props={arrayOfProps} key={(i) => i.id}/>
1080
- * ```
1081
- *
1082
- * Available Wallace directives:
1083
- *
1084
- * - `apply` runs a callback to modify an element.
1085
- * - `bind` updates a value when an input is changed.
1086
- * - `class:xyz` defines a set of classes to be toggled.
1087
- * - `css` shorthand for `fixed:class`.
1088
- * - `ctrl` specifies ctrl for nested/repeated components.
1089
- * - `event` changes the event which `bind` reacts to.
1090
- * - `fixed:xyz` sets a attribute from an expression at definition.
1091
- * - `hide` sets an element or component's hidden property.
1092
- * - `html` Set the element's `innnerHTML` property.
1093
- * - `if` excludes an element from the DOM.
1094
- * - `key` specifies a key for repeated components.
1095
- * - `on[EventName]` creates an event handler (note the code is copied).
1096
- * - `part:xyz` saves a reference to part of a component so it can be updated.
1097
- * - `props` specifies props for stubs, nested or repeated components.
1098
- * - `ref:xyz` saves a reference to an element or nested component.
1099
- * - `show` sets and element or component's hidden property.
1100
- * - `style:xyz` sets a specific style property.
1101
- * - `toggle:xyz` toggles `xyz` as defined by `class:xyz` on same element, or class
1102
- * `xyz`.
1103
- * - `unique` can be set on components which are only used once for better performance.
1104
- *
1105
- * You will get more details by hovering on the directive itself, but unfortunetely
1106
- * the tool tip won't display when you use a qualifier, like `class:danger`. To see
1107
- * it you cantemporarily change it to something `class x:danger`.
1108
- */
1199
+ interface IntrinsicElements extends NativeIntrinsicElements {
1109
1200
  [elemName: string]: DirectiveAttributes & Record<string, any>;
1110
1201
  }
1111
1202
  interface Element {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wallace",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "author": "Andrew Buchan",
5
5
  "description": "An insanely small, fast, intuitive and extendable front-end framework",
6
6
  "homepage": "https://wallace.js.org",
@@ -15,8 +15,8 @@
15
15
  "test": "jest --clearCache && jest"
16
16
  },
17
17
  "dependencies": {
18
- "babel-plugin-wallace": "^0.16.0",
18
+ "babel-plugin-wallace": "^0.17.1",
19
19
  "browserify": "^17.0.1"
20
20
  },
21
- "gitHead": "6e98e0eba99aa0acb6842bd4f918c31748ce3a07"
21
+ "gitHead": "c114de400a01385d31891dd6475d8858e15e70e1"
22
22
  }