docks_theme_api 1.0.2
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.
- checksums.yaml +15 -0
- data/.babelrc +4 -0
- data/.editorconfig +8 -0
- data/.eslintrc +115 -0
- data/.gitignore +24 -0
- data/.rubocop.yml +20 -0
- data/.travis.yml +16 -0
- data/Gemfile +4 -0
- data/README.md +5 -0
- data/Rakefile +3 -0
- data/assets/images/icons.svg +63 -0
- data/assets/scripts/coffeescript/pattern_library_helpers.coffee +8 -0
- data/assets/scripts/javascript/pattern_library_helpers.js +11 -0
- data/assets/scripts/pattern_library.js +10380 -0
- data/assets/scripts/pattern_library_demo.js +0 -0
- data/assets/styles/less/pattern-library-helpers.less +103 -0
- data/assets/styles/pattern-library-demo.css +1882 -0
- data/assets/styles/pattern-library.css +1882 -0
- data/assets/styles/sass/pattern-library-helpers.sass +90 -0
- data/assets/styles/scss/pattern-library-helpers.scss +99 -0
- data/assets/styles/stylus/pattern-library-helpers.styl +90 -0
- data/assets/templates/erb/demo.erb +26 -0
- data/assets/templates/erb/layouts/demo.erb +17 -0
- data/assets/templates/erb/layouts/pattern.erb +76 -0
- data/assets/templates/erb/partials/sidebar.erb +124 -0
- data/assets/templates/erb/partials/symbols/class.erb +1 -0
- data/assets/templates/erb/partials/symbols/demo.erb +40 -0
- data/assets/templates/erb/partials/symbols/factory.erb +70 -0
- data/assets/templates/erb/partials/symbols/function.erb +103 -0
- data/assets/templates/erb/partials/symbols/mixin.erb +62 -0
- data/assets/templates/erb/partials/symbols/variable.erb +59 -0
- data/assets/templates/erb/pattern.erb +102 -0
- data/assets/templates/haml/demo.haml +14 -0
- data/assets/templates/haml/layouts/demo.haml +6 -0
- data/assets/templates/haml/layouts/pattern.haml +38 -0
- data/assets/templates/haml/partials/sidebar.haml +68 -0
- data/assets/templates/haml/partials/symbols/class.haml +1 -0
- data/assets/templates/haml/partials/symbols/demo.haml +23 -0
- data/assets/templates/haml/partials/symbols/factory.haml +38 -0
- data/assets/templates/haml/partials/symbols/function.haml +54 -0
- data/assets/templates/haml/partials/symbols/mixin.haml +31 -0
- data/assets/templates/haml/partials/symbols/variable.haml +22 -0
- data/assets/templates/haml/pattern.haml +54 -0
- data/assets/templates/slim/demo.slim +24 -0
- data/assets/templates/slim/layouts/demo.slim +5 -0
- data/assets/templates/slim/layouts/pattern.slim +48 -0
- data/assets/templates/slim/partials/sidebar.slim +112 -0
- data/assets/templates/slim/partials/symbols/class.slim +1 -0
- data/assets/templates/slim/partials/symbols/demo.slim +30 -0
- data/assets/templates/slim/partials/symbols/factory.slim +57 -0
- data/assets/templates/slim/partials/symbols/function.slim +81 -0
- data/assets/templates/slim/partials/symbols/mixin.slim +45 -0
- data/assets/templates/slim/partials/symbols/variable.slim +35 -0
- data/assets/templates/slim/pattern.slim +63 -0
- data/docks_config.rb +32 -0
- data/docks_theme_api.gemspec +37 -0
- data/gulpfile.js +88 -0
- data/karma.conf.js +6 -0
- data/lib/docks_theme_api/components/base_component.rb +99 -0
- data/lib/docks_theme_api/components/code_block_component.rb +10 -0
- data/lib/docks_theme_api/components/popover_component.rb +15 -0
- data/lib/docks_theme_api/components/table_component.rb +34 -0
- data/lib/docks_theme_api/components/tablist_component.rb +11 -0
- data/lib/docks_theme_api/components.rb +21 -0
- data/lib/docks_theme_api/helpers/ui_helper.rb +69 -0
- data/lib/docks_theme_api/theme.rb +21 -0
- data/lib/docks_theme_api.rb +1 -0
- data/package.json +60 -0
- data/source/behaviors/filterable/filterable.coffee +353 -0
- data/source/behaviors/filterable/filterable.js +0 -0
- data/source/behaviors/filterable/filterable.scss +34 -0
- data/source/behaviors/filterable/package.json +3 -0
- data/source/behaviors/index.js +0 -0
- data/source/components/avatar/avatar.erb +20 -0
- data/source/components/avatar/avatar.js +142 -0
- data/source/components/avatar/avatar.scss +200 -0
- data/source/components/avatar/avatar_container.erb +13 -0
- data/source/components/avatar/package.json +3 -0
- data/source/components/avatar/spec/avatar_spec.js +81 -0
- data/source/components/badge/badge.scss +158 -0
- data/source/components/button/button.scss +213 -0
- data/source/components/card/card.scss +32 -0
- data/source/components/code_block/code-block.scss +353 -0
- data/source/components/code_block/code_block.erb +95 -0
- data/source/components/code_block/code_block.js +444 -0
- data/source/components/code_block/package.json +3 -0
- data/source/components/code_block/spec/code_block_spec.js +10 -0
- data/source/components/demo/demo.js +244 -0
- data/source/components/demo/demo.scss +90 -0
- data/source/components/demo/package.json +3 -0
- data/source/components/exploded/exploded.erb +25 -0
- data/source/components/exploded/exploded.js +694 -0
- data/source/components/exploded/exploded.scss +166 -0
- data/source/components/exploded/package.json +3 -0
- data/source/components/field/field.js +24 -0
- data/source/components/field/field.scss +101 -0
- data/source/components/field/package.json +3 -0
- data/source/components/header/header.scss +33 -0
- data/source/components/iframe/iframe.erb +12 -0
- data/source/components/iframe/iframe.js +381 -0
- data/source/components/iframe/package.json +3 -0
- data/source/components/index.js +37 -0
- data/source/components/inline_group/inline-group.scss +14 -0
- data/source/components/internal_link/internal_link.js +49 -0
- data/source/components/internal_link/package.json +3 -0
- data/source/components/list/list.scss +230 -0
- data/source/components/modal/modal.coffee +84 -0
- data/source/components/modal/modal.erb +19 -0
- data/source/components/modal/modal.js +0 -0
- data/source/components/modal/modal.scss +57 -0
- data/source/components/modal/package.json +3 -0
- data/source/components/notice/notice.scss +48 -0
- data/source/components/popover/package.json +3 -0
- data/source/components/popover/popover.coffee +562 -0
- data/source/components/popover/popover.erb +21 -0
- data/source/components/popover/popover.js +0 -0
- data/source/components/popover/popover.scss +139 -0
- data/source/components/range/range.scss +78 -0
- data/source/components/resizable/package.json +3 -0
- data/source/components/resizable/resizable.erb +30 -0
- data/source/components/resizable/resizable.js +250 -0
- data/source/components/resizable/resizable.scss +245 -0
- data/source/components/resizable/size_buttons.js +249 -0
- data/source/components/scroll_container/package.json +3 -0
- data/source/components/scroll_container/scroll-container.scss +4 -0
- data/source/components/scroll_container/scroll_container.js +24 -0
- data/source/components/section/section.scss +99 -0
- data/source/components/select/package.json +3 -0
- data/source/components/select/select.erb +21 -0
- data/source/components/select/select.js +35 -0
- data/source/components/select/select.scss +163 -0
- data/source/components/table/package.json +3 -0
- data/source/components/table/table.erb +16 -0
- data/source/components/table/table.js +351 -0
- data/source/components/table/table.scss +236 -0
- data/source/components/tablist/package.json +3 -0
- data/source/components/tablist/tablist.erb +13 -0
- data/source/components/tablist/tablist.js +246 -0
- data/source/components/tablist/tablist.scss +191 -0
- data/source/components/tablist/tablist_panel.erb +14 -0
- data/source/components/tablist/tablist_tab.erb +20 -0
- data/source/components/toggle/package.json +3 -0
- data/source/components/toggle/toggle.erb +11 -0
- data/source/components/toggle/toggle.js +211 -0
- data/source/components/toggle/toggle_container.erb +30 -0
- data/source/components/vertical_spacer/vertical-spacer.scss +3 -0
- data/source/components/vertical_stack/vertical-stack.scss +19 -0
- data/source/components/xray/package.json +3 -0
- data/source/components/xray/xray.erb +50 -0
- data/source/components/xray/xray.js +123 -0
- data/source/components/xray/xray.scss +79 -0
- data/source/foundation/app/app.js +15 -0
- data/source/foundation/app/package.json +3 -0
- data/source/pattern-library-demo.scss +13 -0
- data/source/pattern-library.scss +13 -0
- data/source/pattern_library.js +8 -0
- data/source/pattern_library_demo.js +8 -0
- data/source/structures/index.js +11 -0
- data/source/structures/sidebar/package.json +3 -0
- data/source/structures/sidebar/sidebar.js +69 -0
- data/source/structures/sidebar/sidebar.scss +79 -0
- data/source/utilities/builder/builder.js +138 -0
- data/source/utilities/builder/package.json +3 -0
- data/source/utilities/client/client.js +7 -0
- data/source/utilities/client/package.json +3 -0
- data/source/utilities/colors/colors.scss +112 -0
- data/source/utilities/defaults/defaults.scss +38 -0
- data/source/utilities/dom_cache/dom_cache.js +24 -0
- data/source/utilities/dom_cache/package.json +3 -0
- data/source/utilities/events/events.js +25 -0
- data/source/utilities/events/package.json +3 -0
- data/source/utilities/font_sizes/font-sizes.scss +85 -0
- data/source/utilities/foundation/a11y.scss +10 -0
- data/source/utilities/foundation/base.scss +29 -0
- data/source/utilities/foundation/icon.scss +114 -0
- data/source/utilities/foundation/layout.scss +67 -0
- data/source/utilities/foundation/page.scss +39 -0
- data/source/utilities/foundation/type.scss +208 -0
- data/source/utilities/functions/functions.scss +127 -0
- data/source/utilities/keycodes/keycodes.js +23 -0
- data/source/utilities/keycodes/package.json +3 -0
- data/source/utilities/markup/markup.js +90 -0
- data/source/utilities/markup/package.json +3 -0
- data/source/utilities/media/media.scss +172 -0
- data/source/utilities/mixins/mixins.scss +89 -0
- data/source/utilities/naming_convention/naming_convention.js +3 -0
- data/source/utilities/naming_convention/package.json +3 -0
- data/source/utilities/numbers/numbers.js +14 -0
- data/source/utilities/numbers/package.json +3 -0
- data/source/utilities/painting/package.json +3 -0
- data/source/utilities/painting/painting.js +7 -0
- data/source/utilities/pattern/package.json +3 -0
- data/source/utilities/pattern/pattern.js +50 -0
- data/source/utilities/query_string/package.json +3 -0
- data/source/utilities/query_string/query_string.js +24 -0
- data/source/utilities/template/package.json +3 -0
- data/source/utilities/template/template.js +10 -0
- data/source/utilities/text_range/package.json +3 -0
- data/source/utilities/text_range/text_range.js +30 -0
- data/source/utilities/ui_events/package.json +3 -0
- data/source/utilities/ui_events/ui_events.js +85 -0
- data/source/utilities/variables/variables.scss +18 -0
- data/source/utilities/z_indexes/z-indexes.scss +88 -0
- data/source/vendor/array_includes.js +28 -0
- data/source/vendor/highlight.js +1142 -0
- data/source/vendor/index.js +1 -0
- data/source/vendor/matrix.js +399 -0
- data/source/vendor/query_string.js +66 -0
- data/spec/assets/.eslintrc +9 -0
- data/spec/assets/spec_fixture.js +33 -0
- data/spec/assets/spec_helper.js +19 -0
- data/spec/lib/components/base_component_spec.rb +156 -0
- data/spec/lib/components_spec.rb +30 -0
- data/spec/lib/helpers/ui_helper_spec.rb +62 -0
- data/spec/lib/theme_spec.rb +25 -0
- data/spec/spec_helper.rb +15 -0
- data/tasks/gulp/.eslintrc +6 -0
- data/tasks/gulp/browser_sync.js +8 -0
- data/tasks/gulp/code_quality/scripts.js +10 -0
- data/tasks/gulp/config/index.js +116 -0
- data/tasks/gulp/minify/scripts.js +13 -0
- data/tasks/gulp/minify/styles.js +13 -0
- data/tasks/gulp/pattern_library/index.js +5 -0
- data/tasks/gulp/pattern_library/scripts.js +10 -0
- data/tasks/gulp/pattern_library/styles.js +10 -0
- data/tasks/gulp/scripts.js +8 -0
- data/tasks/gulp/spec/scripts.js +11 -0
- data/tasks/gulp/styles.js +17 -0
- data/tasks/gulp/utilities/babel/relative_require.js +22 -0
- data/tasks/gulp/utilities/babel/spec_helper.js +20 -0
- data/tasks/gulp/utilities/browserify_bundler.js +22 -0
- data/tasks/gulp/utilities/handle_errors.js +13 -0
- data/tasks/gulp/watch.js +9 -0
- data/tasks/rake/rspec.rake +7 -0
- data/tasks/rake/rubocop.rake +8 -0
- data/tasks/rake/templates.rake +50 -0
- metadata +470 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
// ___ ___ ___ ___ _____ ___ _____
|
|
2
|
+
// / /\ /__/| / /\ / /\ / /::\ / /\ / /::\
|
|
3
|
+
// / /:/_ | |:| / /::\ / /::\ / /:/\:\ / /:/_ / /:/\:\
|
|
4
|
+
// / /:/ /\ | |:| / /:/\:\___ ___ / /:/\:\ / /:/ \:\ / /:/ /\ / /:/ \:\
|
|
5
|
+
// / /:/ /:/_ __|__|:| / /:/~/:/__/\ / /\/ /:/ \:\/__/:/ \__\:|/ /:/ /:/_/__/:/ \__\:|
|
|
6
|
+
// /__/:/ /:/ /\/__/::::\___/__/:/ /:/\ \:\ / /:/__/:/ \__\:\ \:\ / /:/__/:/ /:/ /\ \:\ / /:/
|
|
7
|
+
// \ \:\/:/ /:/ ~\~~\::::| \:\/:/ \ \:\ /:/\ \:\ / /:/\ \:\ /:/\ \:\/:/ /:/\ \:\ /:/
|
|
8
|
+
// \ \::/ /:/ |~~|:|~~ \ \::/ \ \:\/:/ \ \:\ /:/ \ \:\/:/ \ \::/ /:/ \ \:\/:/
|
|
9
|
+
// \ \:\/:/ | |:| \ \:\ \ \::/ \ \:\/:/ \ \::/ \ \:\/:/ \ \::/
|
|
10
|
+
// \ \::/ | |:| \ \:\ \__\/ \ \::/ \__\/ \ \::/ \__\/
|
|
11
|
+
// \__\/ |__|/ \__\/ \__\/ \__\/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import UIEvents from "../../utilities/ui_events";
|
|
15
|
+
import Client from "../../utilities/client";
|
|
16
|
+
import Builder from "../../utilities/builder";
|
|
17
|
+
import { between, Matrix } from "../../utilities/numbers";
|
|
18
|
+
import { force_repaint } from "../../utilities/painting";
|
|
19
|
+
|
|
20
|
+
const classes = {
|
|
21
|
+
root: "exploded",
|
|
22
|
+
structure: "exploded__structure",
|
|
23
|
+
content: "exploded__structure__content",
|
|
24
|
+
source: "exploded__source",
|
|
25
|
+
pane: "exploded__pane"
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const states = {
|
|
29
|
+
root: {
|
|
30
|
+
initialized: `${classes.root}--is-being-initialized`
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
pane: {
|
|
34
|
+
hovered: `${classes.pane}--is-hovered`,
|
|
35
|
+
selected: `${classes.pane}--is-selected`
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const attrs = {
|
|
40
|
+
id: "data-explosion-id",
|
|
41
|
+
node: "data-explosion-node",
|
|
42
|
+
range_attr: "data-explosion-attribute"
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const events = {
|
|
46
|
+
pane_selected: `${classes.root}:pane-selected`
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const LAYER_GAP = 40;
|
|
50
|
+
|
|
51
|
+
var clone, initialize_panes, initialize_ranges, reset, start_dragging,
|
|
52
|
+
rotate_by, update_panes, node_for_pane, main_class_for_node, Exploded;
|
|
53
|
+
|
|
54
|
+
//*
|
|
55
|
+
// Initializes the panes for an explosion. This does all of the required
|
|
56
|
+
// cloning, stores the resulting panes on the secrets object, and performs an
|
|
57
|
+
// initial rotation.
|
|
58
|
+
//
|
|
59
|
+
// @param {Object} self - The internal details of an `Exploded`.
|
|
60
|
+
// @private
|
|
61
|
+
|
|
62
|
+
initialize_panes = async function(self) {
|
|
63
|
+
var { source, structure } = self;
|
|
64
|
+
if(source.children[0].children.length < 1) { return; }
|
|
65
|
+
|
|
66
|
+
reset(self);
|
|
67
|
+
|
|
68
|
+
self.panes = clone(source.children[0], structure.children[0]);
|
|
69
|
+
self.spread = 1;
|
|
70
|
+
|
|
71
|
+
setTimeout(async function() {
|
|
72
|
+
await UIEvents.transition(self.node, () => {
|
|
73
|
+
self.node.classList.add(states.root.initialized);
|
|
74
|
+
rotate_by(20, 5, self);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
self.node.classList.remove(states.root.initialized);
|
|
78
|
+
}, 400);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
//*
|
|
82
|
+
// Initializes the ranges within an `Exploded` to perform their action. This
|
|
83
|
+
// function also contains the definitions of the possible actions for a range,
|
|
84
|
+
// the `attrs.range_attr` value that will give that behavior to a range, and
|
|
85
|
+
// the actual event handlers for when the range changes values.
|
|
86
|
+
//
|
|
87
|
+
// @param {Object} self - The internal details of an `Exploded`.
|
|
88
|
+
// @private
|
|
89
|
+
|
|
90
|
+
initialize_ranges = (() => {
|
|
91
|
+
var actions, ranges, percentage_from_center, handlers, create_range_listener;
|
|
92
|
+
|
|
93
|
+
actions = {
|
|
94
|
+
gap: "pane-gap",
|
|
95
|
+
perspective: "perspective"
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
ranges = {
|
|
99
|
+
[actions.gap]: { min: 0.25, max: 2, default: 1 },
|
|
100
|
+
[actions.perspective]: { min: 500, max: 4000, default: 2000 }
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
//*
|
|
104
|
+
// Calculates the difference a value from 0-100 is from 50, then normalizes that
|
|
105
|
+
// value for how close it is to the center. So, values close to 50 will be close,
|
|
106
|
+
// to 0, while 0 and 100 will be -1 and 1, respectively.
|
|
107
|
+
//
|
|
108
|
+
// @param {Number} value - The number on a scale of 0-100.
|
|
109
|
+
// @private
|
|
110
|
+
// @returns Number
|
|
111
|
+
|
|
112
|
+
percentage_from_center = (value) => {
|
|
113
|
+
return ((parseInt(value, 10) / 100) - 0.5) / 0.5;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
handlers = {
|
|
117
|
+
|
|
118
|
+
//*
|
|
119
|
+
// @name gap
|
|
120
|
+
//
|
|
121
|
+
// Updates the spread between panes. 50% on the range will generate a z-axis
|
|
122
|
+
// distance between child/ parent panes of `LAYER_GAP`. Anything less than
|
|
123
|
+
// 50% will reduce this gap, and anything greater than 50% will increase it.
|
|
124
|
+
//
|
|
125
|
+
// @param {Object} self - The internal details of an `Exploded`.
|
|
126
|
+
// @param {Object} event - The `input` event on a range input.
|
|
127
|
+
//
|
|
128
|
+
// @private
|
|
129
|
+
|
|
130
|
+
[actions.gap]: function(self, event) {
|
|
131
|
+
var range = ranges[actions.gap],
|
|
132
|
+
spread_from_center = percentage_from_center(event.target.value);
|
|
133
|
+
|
|
134
|
+
if(spread_from_center < 0) {
|
|
135
|
+
self.spread = range.default + (spread_from_center * (range.default - range.min));
|
|
136
|
+
} else {
|
|
137
|
+
self.spread = range.default + (spread_from_center * (range.max - range.default));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
update_panes(self);
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
//*
|
|
144
|
+
// @name perspective
|
|
145
|
+
//
|
|
146
|
+
// Updates the perspective of an `Exploded`. 50% on the range will generate
|
|
147
|
+
// the default perspective, and values lower and higher will decrease and
|
|
148
|
+
// increase that perspective, respectively.
|
|
149
|
+
//
|
|
150
|
+
// @param {Object} self - The internal details of an `Exploded`.
|
|
151
|
+
// @param {Object} event - The `input` event on a range input.
|
|
152
|
+
//
|
|
153
|
+
// @private
|
|
154
|
+
|
|
155
|
+
[actions.perspective]: function(self, event) {
|
|
156
|
+
var range = ranges[actions.perspective],
|
|
157
|
+
spread_from_center = percentage_from_center(event.target.value),
|
|
158
|
+
perspective;
|
|
159
|
+
|
|
160
|
+
if(spread_from_center > 0) {
|
|
161
|
+
perspective = range.default - (spread_from_center * (range.default - range.min));
|
|
162
|
+
} else {
|
|
163
|
+
perspective = range.default - (spread_from_center * (range.max - range.default));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
self.structure.style.perspective = `${perspective}px`;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
create_range_listener = (action) => {
|
|
171
|
+
return (event) => { handlers[action](self, event); };
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
return function(self) {
|
|
175
|
+
var range_node;
|
|
176
|
+
|
|
177
|
+
for(let name of Object.keys(actions)) {
|
|
178
|
+
let action = actions[name];
|
|
179
|
+
range_node = self.node.querySelector(`[${attrs.range_attr}="${action}"]`);
|
|
180
|
+
|
|
181
|
+
if(!range_node) { continue; }
|
|
182
|
+
range_node.addEventListener("input", create_range_listener(action));
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
})();
|
|
186
|
+
|
|
187
|
+
//*
|
|
188
|
+
// Resets the internal state of an `Exploded`.
|
|
189
|
+
//
|
|
190
|
+
// @param {Object} self - The internal details of an `Exploded`.
|
|
191
|
+
// @private
|
|
192
|
+
|
|
193
|
+
reset = (self) => {
|
|
194
|
+
self.rotation = { x: 0, y: 0, z: 0 };
|
|
195
|
+
self.source.style.display = null;
|
|
196
|
+
self.structure.children[0].innerHTML = "";
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
//*
|
|
200
|
+
// Creates the clone representations of the content of `from` into the container
|
|
201
|
+
// `to`. This is done by determining the position of each descendant of `from`
|
|
202
|
+
// relative to the `from` container itself, and then absolutely positioning an
|
|
203
|
+
// `exploded__pane` at the same relative position in `to`. In order to present
|
|
204
|
+
// a useful diagram, the DOM level of each node is captured and is used to stack
|
|
205
|
+
// the panes in the z-axis. Additionally, any overlap between siblings should
|
|
206
|
+
// be recorded and resolved by adding a small adjustment to the z-index
|
|
207
|
+
// stacking of those panes.
|
|
208
|
+
//
|
|
209
|
+
// @param {HTMLElement} from - The node containing the source DOM tree.
|
|
210
|
+
// @param {HTMLElement} to - The node in which to create the cloned presentation.
|
|
211
|
+
//
|
|
212
|
+
// @private
|
|
213
|
+
//
|
|
214
|
+
// @returns Array
|
|
215
|
+
// An array of objects representing the cloned panes. Each object has a `node`,
|
|
216
|
+
// `level`, and `adjustment` property so that future translations can be done
|
|
217
|
+
// performantly.
|
|
218
|
+
|
|
219
|
+
clone = (() => {
|
|
220
|
+
var explosion_id = 0,
|
|
221
|
+
destination, pane_count, widths,
|
|
222
|
+
clone_level, original_offset, panes,
|
|
223
|
+
prepare_for_cloning, append_clone, append_all_clones,
|
|
224
|
+
clone_node, find_overlaps, stack_siblings;
|
|
225
|
+
|
|
226
|
+
//*
|
|
227
|
+
// Resets the internal information used to perform explosions.
|
|
228
|
+
//
|
|
229
|
+
// @private
|
|
230
|
+
|
|
231
|
+
prepare_for_cloning = () => {
|
|
232
|
+
explosion_id += 1;
|
|
233
|
+
pane_count = 0;
|
|
234
|
+
clone_level = 0;
|
|
235
|
+
panes = [];
|
|
236
|
+
destination = null;
|
|
237
|
+
original_offset = null;
|
|
238
|
+
|
|
239
|
+
widths = {
|
|
240
|
+
min: Infinity,
|
|
241
|
+
max: 0
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
//*
|
|
246
|
+
// Appends a new pane with the provided dimensions to the `to` node.
|
|
247
|
+
//
|
|
248
|
+
// @param {Object} dims - The dimensions of the cloned node. Should have
|
|
249
|
+
// `width`, `height`, `left`, `top`, `level`, and
|
|
250
|
+
// `adjustment` properties.
|
|
251
|
+
// @param {HTMLElement} to - The node in which to append the new pane.
|
|
252
|
+
//
|
|
253
|
+
// @private
|
|
254
|
+
// @returns HTMLElement - The cloned node.
|
|
255
|
+
|
|
256
|
+
append_clone = (dims, to) => {
|
|
257
|
+
var parent_width = destination.offsetWidth,
|
|
258
|
+
parent_height = destination.offsetHeight,
|
|
259
|
+
node = $(`<div class='${classes.pane}' style='height: ${dims.height}px; width: ${dims.width}px; top: ${dims.top}px; left: ${dims.left}px; transform-origin: ${(parent_width / 2) - dims.left}px ${(parent_height / 2) - dims.top}px ${LAYER_GAP}px;' />`)[0];
|
|
260
|
+
|
|
261
|
+
to.appendChild(node);
|
|
262
|
+
return node;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
//*
|
|
266
|
+
// Appends all of the required panes to the `to` node passed to
|
|
267
|
+
// [`clone`](@link).
|
|
268
|
+
//
|
|
269
|
+
// @private
|
|
270
|
+
|
|
271
|
+
append_all_clones = () => {
|
|
272
|
+
var fragment = document.createDocumentFragment(), pane;
|
|
273
|
+
|
|
274
|
+
for(pane of panes) {
|
|
275
|
+
pane.clone = append_clone(pane, fragment);
|
|
276
|
+
pane.clone.setAttribute(attrs.node, pane.id);
|
|
277
|
+
pane.clone.style.zIndex = (LAYER_GAP * pane.level) + (pane.adjustment || 0);
|
|
278
|
+
pane.node.setAttribute(attrs.id, pane.id);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
destination.appendChild(fragment);
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
//*
|
|
285
|
+
// Generates the details required to clone a node as a pane. These include
|
|
286
|
+
// its dimensions, its ID, the node it is cloning, its level, and whether or
|
|
287
|
+
// not it is actually visible. These are added to the closured `panes` array
|
|
288
|
+
// so that they can be easily accessed by other methods.
|
|
289
|
+
//
|
|
290
|
+
// @param {HTMLElement} node - The source node to clone.
|
|
291
|
+
// @private
|
|
292
|
+
|
|
293
|
+
clone_node = (node) => {
|
|
294
|
+
var node_offsets = node.getBoundingClientRect(),
|
|
295
|
+
pane, child;
|
|
296
|
+
|
|
297
|
+
original_offset = original_offset || node.parentNode.getBoundingClientRect();
|
|
298
|
+
pane_count += 1;
|
|
299
|
+
|
|
300
|
+
// If we have a visible node
|
|
301
|
+
if((node_offsets.height + node_offsets.width) > 2) {
|
|
302
|
+
pane = {
|
|
303
|
+
height: node_offsets.height,
|
|
304
|
+
width: node_offsets.width,
|
|
305
|
+
top: node_offsets.top - original_offset.top,
|
|
306
|
+
left: node_offsets.left - original_offset.left,
|
|
307
|
+
level: clone_level,
|
|
308
|
+
node: node,
|
|
309
|
+
id: `${explosion_id}-${pane_count}`
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
panes.push(pane);
|
|
313
|
+
|
|
314
|
+
widths.min = Math.min(pane.left, widths.min);
|
|
315
|
+
widths.max = Math.max(pane.left + pane.width, widths.max);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
clone_level += 1;
|
|
319
|
+
for(child of Array.from(node.children)) { clone_node(child); }
|
|
320
|
+
clone_level -= 1;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
//*
|
|
324
|
+
// Finds pairs of nodes whose dimensions overlap one another.
|
|
325
|
+
//
|
|
326
|
+
// @param {Array} siblings - The set of nodes to check for overlap.
|
|
327
|
+
// @private
|
|
328
|
+
// @returns Array - An array of arrays that each contain a set of two
|
|
329
|
+
// overlapping nodes.
|
|
330
|
+
|
|
331
|
+
find_overlaps = (siblings) => {
|
|
332
|
+
var overlaps = [],
|
|
333
|
+
sibling_count = siblings.length,
|
|
334
|
+
index, sibling,
|
|
335
|
+
other_index, other,
|
|
336
|
+
within_other,
|
|
337
|
+
other_within, custom_between;
|
|
338
|
+
|
|
339
|
+
custom_between = (...args) => { return between(...args, { include_min: true }); };
|
|
340
|
+
|
|
341
|
+
for(index = 0; index < sibling_count; index++) {
|
|
342
|
+
sibling = siblings[index];
|
|
343
|
+
|
|
344
|
+
for(other_index = index + 1; other_index < sibling_count; other_index++) {
|
|
345
|
+
other = siblings[other_index];
|
|
346
|
+
|
|
347
|
+
other_within = custom_between(other.left, sibling.left, sibling.left + sibling.width) &&
|
|
348
|
+
custom_between(other.top, sibling.top, sibling.top + sibling.height);
|
|
349
|
+
|
|
350
|
+
within_other = custom_between(sibling.left, other.left, other.left + other.width) &&
|
|
351
|
+
custom_between(sibling.top, other.top, other.top + other.height);
|
|
352
|
+
|
|
353
|
+
if(other_within || within_other) { overlaps.push([sibling, other]); }
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return overlaps;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
//*
|
|
361
|
+
// Creates the necessary adjustments to provide z-space between siblings that
|
|
362
|
+
// would otherwise overlap one another (that is, on the same level with some
|
|
363
|
+
// overlapping coordinates). These adjustments are added directly to the
|
|
364
|
+
// objects in the closured `panes` array.
|
|
365
|
+
//
|
|
366
|
+
// @private
|
|
367
|
+
|
|
368
|
+
stack_siblings = () => {
|
|
369
|
+
var levels = [],
|
|
370
|
+
overlaps, pane, level, overlap;
|
|
371
|
+
|
|
372
|
+
for(pane of panes) {
|
|
373
|
+
levels[pane.level] = levels[pane.level] || [];
|
|
374
|
+
levels[pane.level].push(pane);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
for(level of levels) {
|
|
378
|
+
overlaps = find_overlaps(level);
|
|
379
|
+
|
|
380
|
+
for(overlap of overlaps) {
|
|
381
|
+
overlap[0].adjustment = -LAYER_GAP / 8;
|
|
382
|
+
overlap[1].adjustment = LAYER_GAP / 8;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
return (from, to) => {
|
|
388
|
+
var clone_results = [],
|
|
389
|
+
child, pane;
|
|
390
|
+
|
|
391
|
+
prepare_for_cloning();
|
|
392
|
+
destination = to;
|
|
393
|
+
|
|
394
|
+
for(child of Array.from(from.children)) { clone_node(child); }
|
|
395
|
+
stack_siblings();
|
|
396
|
+
append_all_clones();
|
|
397
|
+
|
|
398
|
+
to.style.maxWidth = `${widths.max - widths.min}px`;
|
|
399
|
+
to.style.height = `${from.offsetHeight}px`;
|
|
400
|
+
from.parentNode.style.display = "none";
|
|
401
|
+
|
|
402
|
+
for(pane in panes) {
|
|
403
|
+
clone_results.push({
|
|
404
|
+
node: pane.clone,
|
|
405
|
+
level: pane.level,
|
|
406
|
+
adjustment: pane.adjustment || 0
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return clone_results;
|
|
411
|
+
};
|
|
412
|
+
})();
|
|
413
|
+
|
|
414
|
+
//*
|
|
415
|
+
// Attaches the events required to handle touches and clicks on the exploded
|
|
416
|
+
// structure. If the click ends before the user drags the `DRAG_THRESHOLD`
|
|
417
|
+
// distance, the action will be treated as a click and the pane on which the
|
|
418
|
+
// user clicked will be selected. If the user drags more than the threshold,
|
|
419
|
+
// the entire structure will be rotated according to the distance dragged.
|
|
420
|
+
//
|
|
421
|
+
// @param {Object} self - The internal details of an `Exploded`.
|
|
422
|
+
// @param {Object} event - The initial `mousedown`/ `touchdown` event.
|
|
423
|
+
//
|
|
424
|
+
// @private
|
|
425
|
+
// @returns Object - The listener object with a `remove` method that allows the
|
|
426
|
+
// drag to be cleanly cancelled.
|
|
427
|
+
|
|
428
|
+
start_dragging = (self, start_event) => {
|
|
429
|
+
var old_coordinates = UIEvents.coordinates(start_event),
|
|
430
|
+
drag_threshold_met = false,
|
|
431
|
+
drag, drag_end;
|
|
432
|
+
|
|
433
|
+
start_event.preventDefault();
|
|
434
|
+
|
|
435
|
+
drag = (event) => {
|
|
436
|
+
var new_coordinates = UIEvents.coordinates(event);
|
|
437
|
+
event.preventDefault();
|
|
438
|
+
|
|
439
|
+
if(drag_threshold_met) {
|
|
440
|
+
document.body.style.pointerEvents = "none";
|
|
441
|
+
rotate_by((new_coordinates.x - old_coordinates.x) / 2, (new_coordinates.y - old_coordinates.y) / 2, self);
|
|
442
|
+
} else {
|
|
443
|
+
drag_threshold_met = UIEvents.coordinates.distance_between(old_coordinates, new_coordinates) > UIEvents.DRAG_THRESHOLD;
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
drag_end = (event) => {
|
|
448
|
+
if(!drag_threshold_met && event.target.classList.contains(classes.pane)) {
|
|
449
|
+
self.select_pane(event.target);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// TODO: Maybe move to helper?
|
|
453
|
+
document.body.style.pointerEvents = null;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
return UIEvents.add_drag_listeners(drag, drag_end);
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
//*
|
|
460
|
+
// Rotates the panes of an `Exploded` by the passed x and y degrees.
|
|
461
|
+
//
|
|
462
|
+
// @param {Number} x - The degrees in the x-axis to rotate the panes.
|
|
463
|
+
// @param {Number} y - The degrees in the x-axis to rotate the panes. Note that
|
|
464
|
+
// this will be reversed so that the rotation feels natural.
|
|
465
|
+
// @param {Object} self - The internal details of an `Exploded`.
|
|
466
|
+
//
|
|
467
|
+
// @private
|
|
468
|
+
|
|
469
|
+
rotate_by = (x, y, self) => {
|
|
470
|
+
self.rotation.x = Math.max(Math.min(90, (self.rotation.x + x) % 360), -90);
|
|
471
|
+
self.rotation.y = Math.max(Math.min(90, (self.rotation.y + y) % 360), -90);
|
|
472
|
+
update_panes(self);
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
//*
|
|
476
|
+
// Applies the current rotation to all panes within an `Exploded`. It will also
|
|
477
|
+
// make sure that the z-translation of each pane is correct given its level in
|
|
478
|
+
// the original source tree and its stacking order against its siblings.
|
|
479
|
+
//
|
|
480
|
+
// @param {Object} self - The internal details of an `Exploded`.
|
|
481
|
+
// @private
|
|
482
|
+
|
|
483
|
+
update_panes = (self) => {
|
|
484
|
+
var { x, y } = self.rotation,
|
|
485
|
+
identity_matrix = Matrix(),
|
|
486
|
+
rotation_matrix = identity_matrix.rotate(-y, x, 0),
|
|
487
|
+
updates = [],
|
|
488
|
+
transform = Client.name_for("transform"),
|
|
489
|
+
z_translate, pane;
|
|
490
|
+
|
|
491
|
+
for(pane of self.panes) {
|
|
492
|
+
if(!pane.node) { continue; }
|
|
493
|
+
|
|
494
|
+
z_translate = ((pane.level * LAYER_GAP) + pane.adjustment) * self.spread;
|
|
495
|
+
updates.push({
|
|
496
|
+
node: pane.node,
|
|
497
|
+
transform: rotation_matrix.translate(0, 0, z_translate).toString()
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
requestAnimationFrame(() => {
|
|
502
|
+
var update;
|
|
503
|
+
|
|
504
|
+
for(update of updates) {
|
|
505
|
+
update.node.style[transform] = update.transform;
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
//*
|
|
511
|
+
// Returns the original node (from the source content) that corresponds to the
|
|
512
|
+
// passed pane.
|
|
513
|
+
//
|
|
514
|
+
// @param {HTMLElement} pane - The exploded pane for which a corresponding source
|
|
515
|
+
// node should be found.
|
|
516
|
+
// @private
|
|
517
|
+
// @returns {HTMLElement | undefined} - The corresponding node or, if none exists,
|
|
518
|
+
// undefined.
|
|
519
|
+
|
|
520
|
+
node_for_pane = (pane) => {
|
|
521
|
+
var node_id = pane.getAttribute(attrs.node);
|
|
522
|
+
if(!node_id) { throw new Error(`The passed node must have an "${attrs.node}" attribute.`); }
|
|
523
|
+
return document.querySelector(`[${attrs.id}='${node_id}']`);
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
// TODO: get this out of here.
|
|
527
|
+
|
|
528
|
+
//*
|
|
529
|
+
// Gets the main class name for a given node.
|
|
530
|
+
//
|
|
531
|
+
// @param {HTMLElement} node - The node to retrieve the main class name for.
|
|
532
|
+
//
|
|
533
|
+
// @private
|
|
534
|
+
// @returns String
|
|
535
|
+
|
|
536
|
+
main_class_for_node = (node) => {
|
|
537
|
+
return node.getAttribute("class").split(" ")[0];
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
//*
|
|
541
|
+
// The constructor around an explosion.
|
|
542
|
+
//
|
|
543
|
+
// @factory
|
|
544
|
+
//
|
|
545
|
+
// @param {HTMLElement} node - The base explosion node.
|
|
546
|
+
//
|
|
547
|
+
// @returns Object - The API for manipulating this explosion, including methods
|
|
548
|
+
// to update the markup to demonstrate, selecting particular
|
|
549
|
+
// panes or all panes for particular components, and adding
|
|
550
|
+
// callbacks to pane selection.
|
|
551
|
+
|
|
552
|
+
Exploded = (node) => {
|
|
553
|
+
var self, api,
|
|
554
|
+
set_markup, select_pane, select_component, on;
|
|
555
|
+
|
|
556
|
+
self = {
|
|
557
|
+
node: node,
|
|
558
|
+
// TODO: write a simpler method for finding all occurances of a class
|
|
559
|
+
structure: node.querySelector(`.${classes.structure}`),
|
|
560
|
+
source: node.querySelector(`.${classes.source}`)
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
//*
|
|
564
|
+
// Clears the existing explosion and re-initalizes the component with the new
|
|
565
|
+
// markup.
|
|
566
|
+
//
|
|
567
|
+
// @method
|
|
568
|
+
//
|
|
569
|
+
// @param {String} markup - The new markup to demonstrate.
|
|
570
|
+
|
|
571
|
+
set_markup = (markup) => {
|
|
572
|
+
self.source.children[0].innerHTML = markup;
|
|
573
|
+
force_repaint(node);
|
|
574
|
+
initialize_panes(self);
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
//*
|
|
578
|
+
// Selects a given pane and emits the selected event. This event can be
|
|
579
|
+
// picked up by other components so that they can display useful information
|
|
580
|
+
// related to this pane. See [`on_pane_select`](@link Exploded#on_pane_select) for details
|
|
581
|
+
// on attaching listeners to pane selection.
|
|
582
|
+
//
|
|
583
|
+
// @method
|
|
584
|
+
//
|
|
585
|
+
// @param {HTMLElement} pane - The selected pane.
|
|
586
|
+
|
|
587
|
+
select_pane = (pane) => {
|
|
588
|
+
var panes = Array.isArray(pane) ? pane : [pane],
|
|
589
|
+
event, related_node;
|
|
590
|
+
|
|
591
|
+
requestAnimationFrame(() => {
|
|
592
|
+
for(pane of self.panes) { pane.node.classList.remove(states.pane.selected); }
|
|
593
|
+
for(pane of panes) { pane.classList.add(states.pane.selected); }
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
if(!panes.length) { return; }
|
|
597
|
+
pane = panes[0];
|
|
598
|
+
related_node = node_for_pane(pane);
|
|
599
|
+
|
|
600
|
+
// The event provides the selected pane, the related (source) node, and
|
|
601
|
+
// the class of the node for easy component identification.
|
|
602
|
+
// TODO: clean this up, kill $
|
|
603
|
+
event = $.Event(events.pane_selected, {
|
|
604
|
+
node: related_node,
|
|
605
|
+
pane: pane,
|
|
606
|
+
component: main_class_for_node(related_node)
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
$(node).trigger(event);
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
//*
|
|
613
|
+
// Selects the pane that corresponds to the provided component.
|
|
614
|
+
//
|
|
615
|
+
// @method
|
|
616
|
+
//
|
|
617
|
+
// @param {String} component - The class name of the component to select.
|
|
618
|
+
|
|
619
|
+
select_component = (component) => {
|
|
620
|
+
var panes = [],
|
|
621
|
+
components = self.source.querySelectorAll(`.${component}`),
|
|
622
|
+
explosion_id, pane, event;
|
|
623
|
+
|
|
624
|
+
for(component of components) {
|
|
625
|
+
explosion_id = component.getAttribute(attrs.id);
|
|
626
|
+
pane = node.querySelector(`[${attrs.node}="${explosion_id}"]`);
|
|
627
|
+
if(pane) { panes.push(pane); }
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
select_pane(panes);
|
|
631
|
+
|
|
632
|
+
// Event won't get triggered by select_pane. Do it here instead.
|
|
633
|
+
if(components.length && !panes.length) {
|
|
634
|
+
event = $.Event(events.pane_selected, {
|
|
635
|
+
node: components[0],
|
|
636
|
+
component: components[0].getAttribute("class").split(" ")[0]
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
$(node).trigger(event);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
//*
|
|
644
|
+
// A helper method to attach an event listener to the `Exploded`.
|
|
645
|
+
//
|
|
646
|
+
// @method
|
|
647
|
+
//
|
|
648
|
+
// @param {String} event - The event to listen for.
|
|
649
|
+
// @param {Function} callback - The callback function.
|
|
650
|
+
// @param {Object} callback.event
|
|
651
|
+
// The event object. Most importantly, the `detail` property of this object
|
|
652
|
+
// contains the source `node`, the selected `pane`, and the name of the
|
|
653
|
+
// `component` that was selected.
|
|
654
|
+
//
|
|
655
|
+
// @returns Object - An object that allows you to easily remove the listener
|
|
656
|
+
// with the `#remove` method.
|
|
657
|
+
|
|
658
|
+
on = (event, callback) => {
|
|
659
|
+
var $node = $(node);
|
|
660
|
+
$node.on(event, callback);
|
|
661
|
+
|
|
662
|
+
return {
|
|
663
|
+
remove() { $node.off(event, callback); }
|
|
664
|
+
};
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
api = { select_pane, select_component, set_markup, on };
|
|
668
|
+
Object.assign(self, api);
|
|
669
|
+
|
|
670
|
+
initialize_panes(self);
|
|
671
|
+
initialize_ranges(self);
|
|
672
|
+
|
|
673
|
+
self.structure.querySelector(`.${classes.content}`).addEventListener(UIEvents.drag.start, (event) => {
|
|
674
|
+
start_dragging(self, event);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
return api;
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
//*
|
|
681
|
+
// Initializes the `Exploded` component.
|
|
682
|
+
//
|
|
683
|
+
// @method
|
|
684
|
+
// @static
|
|
685
|
+
//
|
|
686
|
+
// @param {HTMLElement} [context = document] - The context in which to search
|
|
687
|
+
// for DOM nodes that should be used as the root of an `Exploded` component.
|
|
688
|
+
|
|
689
|
+
Exploded.init = () => {
|
|
690
|
+
Builder.build_and_cache(Exploded, { name: classes.root });
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
export { classes, states, attrs, events };
|
|
694
|
+
export default Exploded;
|