brew-js-react 0.4.7 → 0.5.0

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/hooks.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import { createElement, useEffect, useRef, useState } from "react";
2
2
  import { ViewStateProvider, useObservableProperty, useUpdateTrigger } from "zeta-dom-react";
3
- import { definePrototype, extend, kv, setImmediateOnce, throwNotFunction, watch } from "./include/zeta-dom/util.js";
4
- import { bind } from "./include/zeta-dom/domUtil.js";
3
+ import { definePrototype, delay, each, extend, kv, setImmediateOnce, throwNotFunction, watch } from "./include/zeta-dom/util.js";
5
4
  import { ZetaEventContainer } from "./include/zeta-dom/events.js";
6
5
  import { app } from "./app.js";
7
- import { useViewContainerState } from "./view.js";
6
+ import { useViewContext } from "./view.js";
8
7
 
9
8
  const emitter = new ZetaEventContainer();
10
9
 
@@ -12,19 +11,18 @@ function getCurrentStates() {
12
11
  return app.historyStorage.current;
13
12
  }
14
13
 
15
- function ViewState(key, value) {
14
+ function ViewState(key, dispose) {
16
15
  this.key = key;
17
- this.value = value;
16
+ this.store = getCurrentStates();
17
+ this.dispose = dispose;
18
18
  }
19
19
 
20
20
  definePrototype(ViewState, {
21
21
  get: function () {
22
- return this.value;
22
+ return this.store.get(this.key);
23
23
  },
24
- set: function (value) {
25
- var self = this;
26
- self.value = value;
27
- self.store.set(self.key, value);
24
+ set: function (value, snapshot) {
25
+ this.store = updatePersistedValue(this.store, this.key, value, snapshot);
28
26
  },
29
27
  onPopState: function (callback) {
30
28
  throwNotFunction(callback);
@@ -34,6 +32,40 @@ definePrototype(ViewState, {
34
32
  }
35
33
  });
36
34
 
35
+ function updatePersistedValue(cur, key, value, snapshot) {
36
+ if (cur.get(key) !== value) {
37
+ if (snapshot && cur.has(key)) {
38
+ app.snapshot();
39
+ cur = getCurrentStates();
40
+ }
41
+ cur.set(key, value);
42
+ }
43
+ return cur;
44
+ }
45
+
46
+ function updateViewState(state, key, store) {
47
+ var newValue = state.get();
48
+ if (key !== state.key || (store !== state.store && store.has(key))) {
49
+ newValue = store.get(key);
50
+ emitter.emit('popstate', state, { newValue });
51
+ }
52
+ state.key = key;
53
+ state.store = store;
54
+ store.set(key, newValue);
55
+ }
56
+
57
+ function useViewContextWithEffect(callback, deps) {
58
+ const container = useViewContext();
59
+ useEffect(function () {
60
+ return app.on('beforepageload popstate', function () {
61
+ if (container.active) {
62
+ callback(getCurrentStates());
63
+ }
64
+ });
65
+ }, deps);
66
+ return container;
67
+ }
68
+
37
69
  export function useAppReady() {
38
70
  return useAppReadyState().ready;
39
71
  }
@@ -47,7 +79,7 @@ export function useAppReadyState() {
47
79
  }
48
80
 
49
81
  export function useRouteParam(name, defaultValue) {
50
- const container = useViewContainerState();
82
+ const container = useViewContext();
51
83
  const params = container.page.params;
52
84
  const route = app.route;
53
85
  const value = params[name] || '';
@@ -82,51 +114,39 @@ export function useRouteParam(name, defaultValue) {
82
114
  }
83
115
 
84
116
  export function useRouteState(key, defaultValue, snapshotOnUpdate) {
85
- var container = useViewContainerState();
86
117
  var cur = getCurrentStates();
87
- var state = useState(cur.has(key) ? cur.get(key) : defaultValue);
88
- if (container.active && cur.get(key) !== state[0]) {
89
- if (snapshotOnUpdate && cur.has(key)) {
90
- app.snapshot();
91
- cur = getCurrentStates();
92
- }
93
- cur.set(key, state[0]);
118
+ var state = useState(cur && cur.has(key) ? cur.get(key) : defaultValue);
119
+ var container = useViewContextWithEffect(function (cur) {
120
+ state[1](function (oldValue) {
121
+ return cur.has(key) ? cur.get(key) : (cur.set(key, oldValue), oldValue);
122
+ });
123
+ }, [key]);
124
+ if (!cur) {
125
+ // delay app ready to ensure that beforepageload event can be caught
126
+ app.beforeInit(delay(1));
127
+ } else if (container.active) {
128
+ updatePersistedValue(cur, key, state[0], snapshotOnUpdate);
94
129
  }
95
- useEffect(function () {
96
- if (snapshotOnUpdate) {
97
- return bind(window, 'popstate', function () {
98
- if (container.active) {
99
- var cur = getCurrentStates();
100
- state[1](cur.has(key) ? cur.get(key) : defaultValue);
101
- }
102
- });
103
- }
104
- }, [container, snapshotOnUpdate]);
105
130
  return state;
106
131
  }
107
132
 
108
133
  export function ViewStateContainer(props) {
109
- const container = useViewContainerState();
134
+ const cache = useState({})[0];
135
+ const container = useViewContextWithEffect(function (cur) {
136
+ each(cache, function (i, v) {
137
+ updateViewState(v, v.key, cur);
138
+ });
139
+ }, []);
110
140
  const provider = useState(function () {
111
- const cache = {};
112
141
  return {
113
142
  getState: function (uniqueId, key) {
114
- var cur = getCurrentStates();
115
- var value = cur.get(key);
116
- var state = cache[uniqueId] || (cache[uniqueId] = new ViewState(key, value));
117
- if (container.active) {
118
- var store = state.store;
119
- if (store && ((store !== cur && cur.has(key)) || key !== state.key)) {
120
- emitter.emit('popstate', state, {
121
- newValue: value
122
- });
123
- state.value = value;
124
- state.key = key;
125
- }
126
- state.store = cur;
127
- cur.set(key, state.value);
143
+ var state = cache[uniqueId];
144
+ if (state && container.active) {
145
+ updateViewState(state, key, getCurrentStates());
128
146
  }
129
- return state;
147
+ return state || (cache[uniqueId] = new ViewState(key, function () {
148
+ delete cache[uniqueId];
149
+ }));
130
150
  }
131
151
  };
132
152
  })[0];
package/mixin.d.ts CHANGED
@@ -3,6 +3,7 @@ import AnimateMixin from "./mixins/AnimateMixin";
3
3
  import AnimateSequenceItemMixin from "./mixins/AnimateSequenceItemMixin";
4
4
  import AnimateSequenceMixin from "./mixins/AnimateSequenceMixin";
5
5
  import ClassNameMixin from "./mixins/ClassNameMixin";
6
+ import ClassNameToggleMixin from "./mixins/ClassNameToggleMixin";
6
7
  import FlyoutMixin, { FlyoutMixinOptions } from "./mixins/FlyoutMixin";
7
8
  import FlyoutToggleMixin from "./mixins/FlyoutToggleMixin";
8
9
  import FocusStateMixin from "./mixins/FocusStateMixin";
@@ -10,13 +11,13 @@ import LoadingStateMixin from "./mixins/LoadingStateMixin";
10
11
  import StatefulMixin, { MixinRef } from "./mixins/StatefulMixin";
11
12
  import ScrollableMixin, { ScrollableMixinOptions } from "./mixins/ScrollableMixin";
12
13
  import ScrollIntoViewMixin from "./mixins/ScrollIntoViewMixin";
14
+ import UnmanagedClassNameMixin from "./mixins/UnmanagedClassNameMixin";
13
15
 
14
16
  export * from "./mixins/Mixin";
15
17
  export * from "./mixins/AnimateMixin";
16
18
  export * from "./mixins/AnimateSequenceItemMixin";
17
19
  export * from "./mixins/AnimateSequenceMixin";
18
20
  export * from "./mixins/ClassNameMixin";
19
- export * from "./mixins/ErrorHandlerMixin";
20
21
  export * from "./mixins/FlyoutMixin";
21
22
  export * from "./mixins/FocusStateMixin";
22
23
  export * from "./mixins/LoadingStateMixin";
@@ -29,31 +30,84 @@ export {
29
30
  AnimateSequenceMixin,
30
31
  AnimateSequenceItemMixin,
31
32
  ClassNameMixin,
33
+ ClassNameToggleMixin,
32
34
  FlyoutMixin,
33
35
  FlyoutToggleMixin,
34
36
  FocusStateMixin,
35
37
  LoadingStateMixin,
36
38
  StatefulMixin,
37
39
  ScrollableMixin,
38
- ScrollIntoViewMixin
40
+ ScrollIntoViewMixin,
41
+ UnmanagedClassNameMixin,
39
42
  }
40
43
 
44
+ /**
45
+ * Returns a mixin that enables scrollable plugin on the applied element.
46
+ * @param options A dictionary specifying options.
47
+ */
41
48
  export function useScrollableMixin(options?: ScrollableMixinOptions): ScrollableMixin;
42
49
 
50
+ /**
51
+ * Returns a mixin that allows bringing specific element into view.
52
+ */
43
53
  export function useScrollIntoViewMixin(): ScrollIntoViewMixin;
44
54
 
55
+ /**
56
+ * Returns a mixin that provides methods controlling the applied element as a flyout.
57
+ * @param options A dictionary specifying options.
58
+ */
45
59
  export function useFlyoutMixin(options?: FlyoutMixinOptions): FlyoutMixin;
46
60
 
61
+ /**
62
+ * Returns a mixin that enables intro and/or outro animation for applied elements.
63
+ */
47
64
  export function useAnimateMixin(): AnimateMixin;
48
65
 
66
+ /**
67
+ * Returns a mixin that enables animation sequence for descendant elements.
68
+ * If selector is not specified, it requires applying {@link AnimateSequenceMixin.item} mixin to target React elements.
69
+ * @param selector A CSS selector matching elements to be animated in sequence.
70
+ */
49
71
  export function useAnimateSequenceMixin(selector?: string): AnimateSequenceMixin;
50
72
 
73
+ /**
74
+ * Returns a mixin that adds `focused` and `focused-*` CSS classes to applied elements when being focused.
75
+ */
51
76
  export function useFocusStateMixin(): FocusStateMixin;
52
77
 
78
+ /**
79
+ * Returns a mixin that adds `loading` CSS class to applied elements, when asynchronous operation is notified
80
+ * through `notifyAsync` for any descendent elements. The `asyncStart` and `asyncEnd` events are also enabled on the applied elements.
81
+ */
53
82
  export function useLoadingStateMixin(): LoadingStateMixin;
54
83
 
84
+ /**
85
+ * Returns a mixin that keeps track and preserves the presence of specified CSS classes on applied elements
86
+ * while the host component is re-rendered.
87
+ */
88
+ export function useUnmanagedClassNameMixin(): UnmanagedClassNameMixin;
89
+
90
+ /**
91
+ * Returns a mixin that controls the presence of specified CSS classes on applied elements
92
+ * without re-rendering the host component.
93
+ * @param dict A dictionary specifying CSS classes to be initially added.
94
+ */
95
+ export function useClassNameToggleMixin<T extends Zeta.Dictionary<boolean>>(dict: T): ClassNameToggleMixin<T>;
96
+
97
+ /**
98
+ * Creates a mixin of the specified type within the lifetime of current component.
99
+ * @param mixin Constructor of the mixin type.
100
+ */
55
101
  export function useMixin<T extends typeof Mixin>(mixin: T): InstanceType<T>;
56
102
 
103
+ /**
104
+ * Uses mixin passed from parent component.
105
+ * @param mixin A {@link MixinRef} object.
106
+ */
57
107
  export function useMixinRef<T extends StatefulMixin>(mixin: MixinRef<T>): T;
58
108
 
109
+ /**
110
+ * Uses mixin passed from parent component.
111
+ * @param mixin A {@link MixinRef} object.
112
+ */
59
113
  export function useMixinRef<T extends StatefulMixin>(mixin: MixinRef<T> | undefined): T | undefined;
package/mixin.js CHANGED
@@ -5,6 +5,7 @@ import AnimateMixin from "./mixins/AnimateMixin.js";
5
5
  import AnimateSequenceItemMixin from "./mixins/AnimateSequenceItemMixin.js";
6
6
  import AnimateSequenceMixin from "./mixins/AnimateSequenceMixin.js";
7
7
  import ClassNameMixin from "./mixins/ClassNameMixin.js";
8
+ import ClassNameToggleMixin from "./mixins/ClassNameToggleMixin.js";
8
9
  import FlyoutMixin from "./mixins/FlyoutMixin.js";
9
10
  import FlyoutToggleMixin from "./mixins/FlyoutToggleMixin.js";
10
11
  import FocusStateMixin from "./mixins/FocusStateMixin.js";
@@ -12,6 +13,7 @@ import LoadingStateMixin from "./mixins/LoadingStateMixin.js";
12
13
  import StatefulMixin from "./mixins/StatefulMixin.js";
13
14
  import ScrollableMixin from "./mixins/ScrollableMixin.js";
14
15
  import ScrollIntoViewMixin from "./mixins/ScrollIntoViewMixin.js";
16
+ import UnmanagedClassNameMixin from "./mixins/UnmanagedClassNameMixin.js";
15
17
 
16
18
  function extendSelf(options) {
17
19
  extend(this, options);
@@ -27,11 +29,13 @@ function createUseFunction(ctor) {
27
29
 
28
30
  export const useAnimateMixin = createUseFunction(AnimateMixin);
29
31
  export const useAnimateSequenceMixin = createUseFunction(AnimateSequenceMixin);
32
+ export const useClassNameToggleMixin = createUseFunction(ClassNameToggleMixin);
30
33
  export const useFlyoutMixin = createUseFunction(FlyoutMixin);
31
34
  export const useFocusStateMixin = createUseFunction(FocusStateMixin);
32
35
  export const useLoadingStateMixin = createUseFunction(LoadingStateMixin);
33
36
  export const useScrollableMixin = createUseFunction(ScrollableMixin);
34
37
  export const useScrollIntoViewMixin = createUseFunction(ScrollIntoViewMixin);
38
+ export const useUnmanagedClassNameMixin = createUseFunction(UnmanagedClassNameMixin);
35
39
 
36
40
  export function useMixin(ctor) {
37
41
  return useSingleton(function () {
@@ -49,11 +53,13 @@ export {
49
53
  AnimateSequenceItemMixin,
50
54
  AnimateSequenceMixin,
51
55
  ClassNameMixin,
56
+ ClassNameToggleMixin,
52
57
  FlyoutMixin,
53
58
  FlyoutToggleMixin,
54
59
  FocusStateMixin,
55
60
  LoadingStateMixin,
56
61
  StatefulMixin,
57
62
  ScrollableMixin,
58
- ScrollIntoViewMixin
63
+ ScrollIntoViewMixin,
64
+ UnmanagedClassNameMixin,
59
65
  }
@@ -1,4 +1,6 @@
1
1
  import ClassNameMixin from "./ClassNameMixin";
2
+ import Mixin from "./Mixin";
3
+ import { useAnimateMixin } from "../mixin";
2
4
 
3
5
  /**
4
6
  * To use predefined animation effects, import `brew-js/styles`.
@@ -28,6 +30,11 @@ export type AnimationEffect = Zeta.HintedString<'fade-in' | 'slide-down' | 'slid
28
30
  */
29
31
  export type AnimationTrigger = Zeta.HintedString<'show' | 'open'>;
30
32
 
33
+ /**
34
+ * Enables intro and/or outro animation for applied elements.
35
+ *
36
+ * Mixin should be created using {@link useAnimateMixin} and applied to element by {@link Mixin.use}.
37
+ */
31
38
  export default class AnimateMixin extends ClassNameMixin {
32
39
  /**
33
40
  * Specifies animation effects and trigger for rendered elements.
@@ -1,5 +1,11 @@
1
+ import AnimateSequenceMixin from "./AnimateSequenceMixin";
1
2
  import ClassNameMixin from "./ClassNameMixin";
2
3
 
4
+ /**
5
+ * Marks element to be a target of this animate sequence.
6
+ *
7
+ * Instances of this mixin is exposed by {@link AnimateSequenceMixin.item}.
8
+ */
3
9
  export default class AnimateSequenceItemMixin extends ClassNameMixin {
4
10
  constructor(className: string);
5
11
  }
@@ -1,4 +1,4 @@
1
- import { definePrototype } from "../include/zeta-dom/util.js";
1
+ import { definePrototype, extend } from "../include/zeta-dom/util.js";
2
2
  import ClassNameMixin from "./ClassNameMixin.js";
3
3
 
4
4
  const AnimateSequenceItemMixinSuper = ClassNameMixin.prototype;
@@ -9,6 +9,11 @@ export default function AnimateSequenceItemMixin(className) {
9
9
  }
10
10
 
11
11
  definePrototype(AnimateSequenceItemMixin, ClassNameMixin, {
12
+ getCustomAttributes: function () {
13
+ return extend({}, AnimateSequenceItemMixinSuper.getCustomAttributes.call(this), {
14
+ 'is-animate-sequence': ''
15
+ });
16
+ },
12
17
  getClassNames: function () {
13
18
  return [this.className].concat(AnimateSequenceItemMixinSuper.getClassNames.call(this));
14
19
  }
@@ -1,6 +1,16 @@
1
1
  import AnimateMixin from "./AnimateMixin";
2
2
  import AnimateSequenceItemMixin from "./AnimateSequenceItemMixin";
3
+ import Mixin from "./Mixin";
4
+ import { useAnimateSequenceMixin } from "../mixin";
3
5
 
6
+ /**
7
+ * Enables animation sequence for descendant elements.
8
+ *
9
+ * Mixin should be created using {@link useAnimateSequenceMixin} and applied to element by {@link Mixin.use}.
10
+ */
4
11
  export default class AnimateSequenceMixin extends AnimateMixin {
12
+ /**
13
+ * Returns a mixin that marks element to be a target of this animate sequence.
14
+ */
5
15
  readonly item: AnimateSequenceItemMixin;
6
16
  }
@@ -1,3 +1,5 @@
1
+ import $ from "../include/external/jquery.js";
2
+ import { watchElements } from "../include/zeta-dom/observe.js";
1
3
  import { definePrototype, extend } from "../include/zeta-dom/util.js";
2
4
  import AnimateMixin from "./AnimateMixin.js";
3
5
  import AnimateSequenceItemMixin from "./AnimateSequenceItemMixin.js";
@@ -17,10 +19,6 @@ definePrototype(AnimateSequenceMixin, AnimateMixin, {
17
19
  this.selector = options;
18
20
  return this;
19
21
  },
20
- reset: function () {
21
- this.item.reset();
22
- return AnimateSequenceMixinSuper.reset.call(this);
23
- },
24
22
  getCustomAttributes: function () {
25
23
  var self = this;
26
24
  return extend({}, AnimateSequenceMixinSuper.getCustomAttributes.call(self), {
@@ -29,9 +27,13 @@ definePrototype(AnimateSequenceMixin, AnimateMixin, {
29
27
  'animate-sequence': self.selector || '.' + self.className
30
28
  });
31
29
  },
32
- clone: function () {
33
- return extend(AnimateSequenceMixinSuper.clone.call(this), {
34
- item: this.item.ref.getMixin()
35
- });
30
+ initElement: function (element, state) {
31
+ var self = this;
32
+ AnimateSequenceMixinSuper.initElement.call(self, element, state);
33
+ if (self.selector) {
34
+ self.onDispose(watchElements(element, self.selector, function (addedNodes) {
35
+ $(addedNodes).attr('is-animate-sequence', '');
36
+ }));
37
+ }
36
38
  }
37
39
  });
@@ -4,12 +4,21 @@ export interface ClassNameMixinState extends MixinState {
4
4
  classNames: Record<string, boolean>;
5
5
  }
6
6
 
7
+ /**
8
+ * Base class for implementing mixins that requires certain CSS class names to be
9
+ * preserved after component update.
10
+ */
7
11
  export default class ClassNameMixin extends StatefulMixin<ClassNameMixinState> {
8
12
  /**
9
13
  * @param classNames Specifies a list of class names to be obeserved.
10
14
  */
11
15
  constructor(classNames: string[] = []);
12
16
 
17
+ /**
18
+ * Override to perform actions when observed class names has changed.
19
+ * @param element DOM element which its class names has changed.
20
+ * @param prevState Previous states representing the presence of each class name.
21
+ * @param state Current states representing the presence of each class name.
22
+ */
13
23
  protected onClassNameUpdated(element: HTMLElement, prevState: Record<string, boolean>, state: Record<string, boolean>): void;
14
24
  }
15
-
@@ -1,24 +1,16 @@
1
- import { definePrototype, each, equal, extend, setImmediate } from "../include/zeta-dom/util.js";
2
- import { containsOrEquals } from "../include/zeta-dom/domUtil.js";
3
- import dom from "../include/zeta-dom/dom.js";
1
+ import { definePrototype, each, equal, extend, fill } from "../include/zeta-dom/util.js";
2
+ import { setClass } from "../include/zeta-dom/domUtil.js";
4
3
  import { watchOwnAttributes } from "../include/zeta-dom/observe.js";
5
4
  import StatefulMixin from "./StatefulMixin.js";
6
5
 
7
- const ClassNameMixinSuper = StatefulMixin.prototype;
8
-
9
- function checkState(self, element, state, isAsync) {
6
+ function checkState(self, element, state, fireEvent) {
10
7
  var classNames = state.classNames;
11
8
  var prev = extend({}, classNames);
12
- each(self.classNames, function (i, v) {
13
- classNames[v] = element.classList.contains(v);
9
+ each(classNames, function (i) {
10
+ classNames[i] = element.classList.contains(i);
14
11
  });
15
- if (!equal(prev, classNames)) {
16
- var cb = self.onClassNameUpdated.bind(self, element, prev, extend({}, classNames));
17
- if (isAsync) {
18
- setImmediate(cb);
19
- } else {
20
- cb();
21
- }
12
+ if (fireEvent && !equal(prev, classNames)) {
13
+ self.onClassNameUpdated(element, prev, extend({}, classNames));
22
14
  }
23
15
  }
24
16
 
@@ -28,29 +20,22 @@ export default function ClassNameMixin(classNames) {
28
20
  }
29
21
 
30
22
  definePrototype(ClassNameMixin, StatefulMixin, {
31
- getClassNames: function () {
32
- return [this.state.classNames];
33
- },
34
- getRef: function () {
35
- var self = this;
36
- var element = self.state.element;
37
- if (element && containsOrEquals(dom.root, element)) {
38
- checkState(self, element, self.state, true);
39
- }
40
- return ClassNameMixinSuper.getRef.call(this);
41
- },
42
23
  initState: function () {
43
24
  return {
44
25
  element: null,
45
- classNames: {}
26
+ classNames: fill(this.classNames, false)
46
27
  };
47
28
  },
48
29
  initElement: function (element, state) {
49
30
  var self = this;
31
+ checkState(self, element, state);
50
32
  watchOwnAttributes(element, 'class', function () {
51
- checkState(self, element, state);
33
+ checkState(self, element, state, true);
52
34
  });
53
35
  },
36
+ onLayoutEffect: function (element, state) {
37
+ setClass(element, state.classNames);
38
+ },
54
39
  onClassNameUpdated: function (element, prevState, state) {
55
40
  }
56
41
  });
@@ -0,0 +1,24 @@
1
+ import Mixin from "./Mixin";
2
+ import StatefulMixin from "./StatefulMixin";
3
+ import { useClassNameToggleMixin } from "../mixin";
4
+
5
+ /**
6
+ * Controls the presence of specified CSS classes on applied elements
7
+ * without re-rendering the host component.
8
+ *
9
+ * Mixin should be created using {@link useClassNameToggleMixin} and applied to element by {@link Mixin.use}.
10
+ */
11
+ export default class ClassNameToggleMixin<T extends Zeta.Dictionary<boolean>> extends StatefulMixin {
12
+ /**
13
+ * Toggles specified CSS classes on elements.
14
+ * @param name A CSS class.
15
+ * @param value A boolean that the CSS class is added to elements when `true`; or removed from elements when `false`.
16
+ */
17
+ set(name: Zeta.HintedStringKeyOf<T>, value: boolean): void;
18
+
19
+ /**
20
+ * Toggles multiple CSS classes on elements.
21
+ * @param values A dictionary which each entry represents whether the CSS class will be added to or remove from elements.
22
+ */
23
+ set(values: { [P in Zeta.HintedStringKeyOf<T>]?: boolean }): void;
24
+ }
@@ -0,0 +1,23 @@
1
+ import { definePrototype, each, extend, isPlainObject, kv } from "../include/zeta-dom/util.js";
2
+ import { setClass } from "../include/zeta-dom/domUtil.js";
3
+ import StatefulMixin from "./StatefulMixin.js";
4
+
5
+ export default function ClassNameToggleMixin() {
6
+ StatefulMixin.call(this);
7
+ }
8
+
9
+ definePrototype(ClassNameToggleMixin, StatefulMixin, {
10
+ withOptions: function (classes) {
11
+ this.classes = extend({}, classes);
12
+ },
13
+ getClassNames: function () {
14
+ return this.classes;
15
+ },
16
+ set: function (name, value) {
17
+ value = isPlainObject(name) || kv(name, value);
18
+ each(this.elements(), function (i, v) {
19
+ setClass(v, value);
20
+ });
21
+ extend(this.classes, value);
22
+ }
23
+ });
@@ -1,6 +1,8 @@
1
1
  import ClassNameMixin from "./ClassNameMixin";
2
2
  import FlyoutToggleMixin from "./FlyoutToggleMixin";
3
+ import Mixin from "./Mixin";
3
4
  import { AnimationEffect } from "./AnimateMixin";
5
+ import { useFlyoutMixin } from "../mixin";
4
6
 
5
7
  export interface FlyoutMixinOptions {
6
8
  /**
@@ -17,6 +19,11 @@ export interface FlyoutMixinOptions {
17
19
  swipeToDismiss?: 'up' | 'down' | 'left' | 'right';
18
20
  }
19
21
 
22
+ /**
23
+ * Provides methods controlling the applied element as a flyout.
24
+ *
25
+ * Mixin should be created using {@link useFlyoutMixin} and applied to element by {@link Mixin.use}.
26
+ */
20
27
  export default class FlyoutMixin extends ClassNameMixin {
21
28
  /**
22
29
  * Gets whether the flyout is open.
@@ -1,4 +1,4 @@
1
- import { defineAliasProperty, definePrototype, each, extend, makeArray } from "../include/zeta-dom/util.js";
1
+ import { definePrototype, extend, makeArray } from "../include/zeta-dom/util.js";
2
2
  import { closeFlyout, openFlyout } from "../include/brew-js/domAction.js";
3
3
  import { app } from "../app.js";
4
4
  import ClassNameMixin from "./ClassNameMixin.js";
@@ -6,7 +6,6 @@ import FlyoutToggleMixin from "./FlyoutToggleMixin.js";
6
6
 
7
7
  const FlyoutMixinSuper = ClassNameMixin.prototype;
8
8
  const valueMap = new WeakMap();
9
- const aliasProps = 'animating isFlyoutOpened modal tabThrough visible'.split(' ');
10
9
 
11
10
  export default function FlyoutMixin() {
12
11
  var self = this;
@@ -24,10 +23,6 @@ export default function FlyoutMixin() {
24
23
  }
25
24
 
26
25
  definePrototype(FlyoutMixin, ClassNameMixin, {
27
- reset: function () {
28
- this.toggle.reset();
29
- return FlyoutMixinSuper.reset.call(this);
30
- },
31
26
  next: function () {
32
27
  this.effects = undefined;
33
28
  return FlyoutMixinSuper.next.call(this);
@@ -85,16 +80,6 @@ definePrototype(FlyoutMixin, ClassNameMixin, {
85
80
  },
86
81
  }, true));
87
82
  },
88
- clone: function () {
89
- var self = this;
90
- var mixin = extend(FlyoutMixinSuper.clone.call(self), {
91
- toggle: self.toggle.ref.getMixin()
92
- });
93
- each(aliasProps, function (i, v) {
94
- defineAliasProperty(mixin, v, self);
95
- });
96
- return mixin;
97
- },
98
83
  onClassNameUpdated: function (element, prevState, state) {
99
84
  var self = this;
100
85
  self.visible = state.open;
@@ -1,5 +1,11 @@
1
1
  import ClassNameMixin from "./ClassNameMixin";
2
+ import FlyoutMixin from "./FlyoutMixin";
2
3
 
4
+ /**
5
+ * Enables applied element as toggle buttons for the associated flyout.
6
+ *
7
+ * Instances of this mixin is exposed by {@link FlyoutMixin.toggle}.
8
+ */
3
9
  export default class FlyoutToggleMixin extends ClassNameMixin {
4
10
  /**
5
11
  * Opens the associated flyout.
@@ -1,6 +1,13 @@
1
1
  import React from "react";
2
2
  import ClassNameMixin from "./ClassNameMixin";
3
+ import Mixin from "./Mixin";
4
+ import { useFocusStateMixin } from "../mixin";
3
5
 
6
+ /**
7
+ * Adds `focused` and `focused-*` CSS classes to applied elements when being focused.
8
+ *
9
+ * Mixin should be created using {@link useFocusStateMixin} and applied to element by {@link Mixin.use}.
10
+ */
4
11
  export default class FocusStateMixin extends ClassNameMixin {
5
12
  /**
6
13
  * Sets focus state based on another element.
@@ -38,13 +38,7 @@ definePrototype(FocusStateMixin, StatefulMixin, {
38
38
  }
39
39
  }));
40
40
  },
41
- getClassNames: function () {
42
- var classes = {};
43
- var focused = this.state.focused;
44
- if (focused) {
45
- classes.focused = true;
46
- classes['focused-' + focused] = true;
47
- }
48
- return [classes];
41
+ onLayoutEffect: function (element, state) {
42
+ setClass(element, 'focused', state.focused);
49
43
  }
50
44
  });