docks_theme_api 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,444 @@
|
|
1
|
+
// See: http://updates.html5rocks.com/2015/04/cut-and-copy-commands
|
2
|
+
|
3
|
+
// ___ ___ _____ ___
|
4
|
+
// / /\ / /\ / /::\ / /\
|
5
|
+
// / /:/ / /::\ / /:/\:\ / /:/_
|
6
|
+
// / /:/ / /:/\:\ / /:/ \:\ / /:/ /\
|
7
|
+
// / /:/ ___ / /:/ \:\/__/:/ \__\:|/ /:/ /:/_
|
8
|
+
// /__/:/ / /\/__/:/ \__\:\ \:\ / /:/__/:/ /:/ /\
|
9
|
+
// \ \:\ / /:/\ \:\ / /:/\ \:\ /:/\ \:\/:/ /:/
|
10
|
+
// \ \:\ /:/ \ \:\ /:/ \ \:\/:/ \ \::/ /:/
|
11
|
+
// \ \:\/:/ \ \:\/:/ \ \::/ \ \:\/:/
|
12
|
+
// \ \::/ \ \::/ \__\/ \ \::/
|
13
|
+
// \__\/ \__\/ \__\/
|
14
|
+
|
15
|
+
import ScrollContainer from "../scroll_container";
|
16
|
+
import Events from "../../utilities/events";
|
17
|
+
import UIEvents from "../../utilities/ui_events";
|
18
|
+
import Range from "../../utilities/text_range";
|
19
|
+
import Builder from "../../utilities/builder";
|
20
|
+
import { Communicator } from "../iframe";
|
21
|
+
import { classes as select_classes } from "../select";
|
22
|
+
import { indent, clean, highlight } from "../../utilities/markup";
|
23
|
+
import { force_repaint as repaint } from "../../utilities/painting";
|
24
|
+
|
25
|
+
const classes = {
|
26
|
+
root: "code-block",
|
27
|
+
header: "code-block__header",
|
28
|
+
code: "code-block__code",
|
29
|
+
select: "code-block__demo-selector",
|
30
|
+
code_container: "code-block__code-container",
|
31
|
+
toggler: "code-block__toggler",
|
32
|
+
content: "code-block__content",
|
33
|
+
iframe: "code-block__iframe",
|
34
|
+
demo_content: "code-block__demo__content"
|
35
|
+
};
|
36
|
+
|
37
|
+
const variants = {
|
38
|
+
root: { with_demo: `${classes.root}--with-demo` }
|
39
|
+
};
|
40
|
+
|
41
|
+
const states = {
|
42
|
+
root: { hidden: `${classes.root}--is-hidden` }
|
43
|
+
};
|
44
|
+
|
45
|
+
const attrs = {
|
46
|
+
language: "data-code-block-language",
|
47
|
+
cached_max_height: "data-cached-max-height"
|
48
|
+
};
|
49
|
+
|
50
|
+
var CodeBlock, CodeCaches,
|
51
|
+
clean_and_highlight_code, update_helper, toggle_code_block_visibility,
|
52
|
+
select_code, hide, show, cache_content_height, hook_up_iframe_communication,
|
53
|
+
attach_event_listeners;
|
54
|
+
|
55
|
+
//*
|
56
|
+
// Cleans a string of code and updates the string with syntax highlighting
|
57
|
+
// markup.
|
58
|
+
//
|
59
|
+
// @param {String} code - The raw code.
|
60
|
+
//
|
61
|
+
// @param {Object} [options = {}] - The options for the highlighting operation.
|
62
|
+
//
|
63
|
+
// @param {String} [options.language_code = "html"]
|
64
|
+
// The language of the passed code. This is used by the syntax highlighter to
|
65
|
+
// pick the appropriate highlighter.
|
66
|
+
//
|
67
|
+
// @param {Boolean} [options.collapse_newlines = false]
|
68
|
+
// Whether or not to combine multiple consecutive newlines into a single newline.
|
69
|
+
//
|
70
|
+
// @private
|
71
|
+
// @returns String - The cleaned and syntax-highlighted string.
|
72
|
+
|
73
|
+
clean_and_highlight_code = (code, options = {}) => {
|
74
|
+
var { language_code } = options;
|
75
|
+
|
76
|
+
code = clean(code, options);
|
77
|
+
if(!language_code || language_code === "html") { code = indent(code); }
|
78
|
+
return highlight(code, options);
|
79
|
+
};
|
80
|
+
|
81
|
+
//*
|
82
|
+
// Updates helper code (that is, a template language that generates markup) for
|
83
|
+
// changes in classes that have corresponding attributes in the helper markup.
|
84
|
+
// It does this by searching through the helper markup for the symbol that sets
|
85
|
+
// a given class (the `setter`), and then assigns that a value depending on the
|
86
|
+
// nature of the change.
|
87
|
+
//
|
88
|
+
// - If there is no `constant` for the change, the value of the `setter` is
|
89
|
+
// assumed to be `true` if the class is active and `false` otherwise.
|
90
|
+
//
|
91
|
+
// - If there is a `constant`, this value is used when a class is added. The
|
92
|
+
// cache is required to store values for when a `setter` with a constant is
|
93
|
+
// removed — the value of the `setter` is then returned to the previous
|
94
|
+
// `constant`, which is stored in the cache.
|
95
|
+
//
|
96
|
+
// @param {String} code - The raw code.
|
97
|
+
// @param {Object} change - The details about the class change. This should
|
98
|
+
// include whether the class was added or removed and
|
99
|
+
// all of the `setters` for the corresponding variation.
|
100
|
+
// @param {Object} cache - The cache of previous constant values.
|
101
|
+
//
|
102
|
+
// @private
|
103
|
+
// @returns String - The helper code with the relevant attributes updated.
|
104
|
+
|
105
|
+
update_helper = (code, change, cache) => {
|
106
|
+
var add, helper_param, constant, helper_matcher, regex,
|
107
|
+
constants_for_param, index, replace_value, set_by,
|
108
|
+
constant_replacer, boolean_replacer;
|
109
|
+
|
110
|
+
add = !!change.add;
|
111
|
+
|
112
|
+
constant_replacer = (match, param_portion, constant_portion) => {
|
113
|
+
cache[helper_param] = cache[helper_param] || [constant_portion];
|
114
|
+
|
115
|
+
if(change.method === "add") {
|
116
|
+
cache[helper_param].push(constant);
|
117
|
+
return `${param_portion}${constant}`;
|
118
|
+
|
119
|
+
} else {
|
120
|
+
constants_for_param = cache[helper_param];
|
121
|
+
if(!constants_for_param) { return match; }
|
122
|
+
|
123
|
+
index = constants_for_param.indexOf(constant);
|
124
|
+
if(index >= 0) { constants_for_param.splice(index, 1); }
|
125
|
+
|
126
|
+
replace_value = constants_for_param[constants_for_param.length - 1];
|
127
|
+
return `${param_portion}${replace_value}`;
|
128
|
+
}
|
129
|
+
};
|
130
|
+
|
131
|
+
boolean_replacer = (match, param_portion) => {
|
132
|
+
return `${param_portion}${add ? "true" : "false"}`;
|
133
|
+
};
|
134
|
+
|
135
|
+
if(!change.set_by) { return code; }
|
136
|
+
|
137
|
+
for(set_by of change.set_by) {
|
138
|
+
constant = set_by.constant || "";
|
139
|
+
helper_param = set_by.setter;
|
140
|
+
helper_matcher = `:?\"?${helper_param.replace(":", "").replace("?", "\\?")}\"?:?\\s*(?:=>\\s*)?`;
|
141
|
+
|
142
|
+
if(constant) {
|
143
|
+
// If a value was actually declared for the set_by, find the current constant
|
144
|
+
// and replace it as needed
|
145
|
+
// key: VALUE, :key => VALUE, "key" => VALUE, :"key" => VALUE
|
146
|
+
|
147
|
+
regex = new RegExp(`(${helper_matcher})([a-zA-Z\\-_:]*)`);
|
148
|
+
code = code.replace(regex, constant_replacer);
|
149
|
+
|
150
|
+
} else {
|
151
|
+
// No constant declared, assume it is true/ false
|
152
|
+
regex = new RegExp(`(${helper_matcher})(true|false)`);
|
153
|
+
code = code.replace(regex, boolean_replacer);
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
return code;
|
158
|
+
};
|
159
|
+
|
160
|
+
//*
|
161
|
+
// Handles a click on the contained `button` that toggles the visibility of the
|
162
|
+
// code block.
|
163
|
+
//
|
164
|
+
// @private
|
165
|
+
// @param {Object} event - The `click` event on the select.
|
166
|
+
|
167
|
+
toggle_code_block_visibility = (event) => {
|
168
|
+
var code_block = CodeBlock.for(event.target);
|
169
|
+
if(!code_block) { return; }
|
170
|
+
code_block.toggle();
|
171
|
+
};
|
172
|
+
|
173
|
+
//*
|
174
|
+
// Handles a focus on the code area of a code block by selecting all of the
|
175
|
+
// text within the code block.
|
176
|
+
//
|
177
|
+
// @private
|
178
|
+
// @param {Object} event - The `focusin` event on the code.
|
179
|
+
|
180
|
+
select_code = () => {
|
181
|
+
Range(this).select_all();
|
182
|
+
};
|
183
|
+
|
184
|
+
$(document).on("click", `.${classes.toggler}`, toggle_code_block_visibility);
|
185
|
+
$(document).on("click", `.${classes.code}`, select_code);
|
186
|
+
|
187
|
+
//*
|
188
|
+
// Hides a code block.
|
189
|
+
//
|
190
|
+
// @param {Object} self - The internal details of a [`CodeBlock`](@link).
|
191
|
+
// @param {Object} options ({}) - The options for how the code block should be
|
192
|
+
// hidden. Currently, only the `without_transition` (which hides automatically
|
193
|
+
// rather than scaling the height of the code block) option is supported.
|
194
|
+
//
|
195
|
+
// @private
|
196
|
+
|
197
|
+
hide = (self, options = {}) => {
|
198
|
+
var { node, toggler, content } = self,
|
199
|
+
{ without_transition } = options,
|
200
|
+
scroll_container;
|
201
|
+
|
202
|
+
ScrollContainer.init();
|
203
|
+
scroll_container = ScrollContainer.for(node);
|
204
|
+
if(scroll_container) { scroll_container.maintain_current_height(); }
|
205
|
+
|
206
|
+
node.classList.add(states.root.hidden);
|
207
|
+
if(toggler) { toggler.querySelector("span").textContent = "Show"; }
|
208
|
+
|
209
|
+
content.style.transition = "none";
|
210
|
+
|
211
|
+
if(!without_transition) {
|
212
|
+
content.style.height = `${Math.min(content.scrollHeight, parseInt(content.getAttribute(attrs.cached_max_height), 10))}px`;
|
213
|
+
repaint(content);
|
214
|
+
content.style.transition = null;
|
215
|
+
}
|
216
|
+
|
217
|
+
repaint(content);
|
218
|
+
content.style.height = "0px";
|
219
|
+
|
220
|
+
if(without_transition) {
|
221
|
+
repaint(content);
|
222
|
+
content.style.transition = null;
|
223
|
+
}
|
224
|
+
|
225
|
+
self.is_hidden = true;
|
226
|
+
};
|
227
|
+
|
228
|
+
//*
|
229
|
+
// Shows a code block.
|
230
|
+
//
|
231
|
+
// @param {Object} self - The internal details of a [`CodeBlock`](@link).
|
232
|
+
//
|
233
|
+
// @private
|
234
|
+
|
235
|
+
show = async function(self) {
|
236
|
+
var { node, toggler, content } = self;
|
237
|
+
|
238
|
+
node.classList.remove(states.root.hidden);
|
239
|
+
self.is_hidden = false;
|
240
|
+
if(toggler) { toggler.querySelector("span").textContent = "Hide"; }
|
241
|
+
|
242
|
+
await UIEvents.transition(content, function() {
|
243
|
+
content.style.height = `${Math.min(content.scrollHeight, parseInt(content.getAttribute(attrs.cached_max_height), 10))}px`;
|
244
|
+
});
|
245
|
+
|
246
|
+
content.style.height = null;
|
247
|
+
};
|
248
|
+
|
249
|
+
//*
|
250
|
+
// Caches the max height of the main content area of a code block. This is done
|
251
|
+
// so that the transition from hidden to shown caps out at the `max-height`
|
252
|
+
// specified in CSS.
|
253
|
+
//
|
254
|
+
// In order to allow the code areas to scroll, an appropriate max-height is also
|
255
|
+
// set on them.
|
256
|
+
//
|
257
|
+
// @param {Object} self - The internal details of a [`CodeBlock`](@link).
|
258
|
+
//
|
259
|
+
// @private
|
260
|
+
|
261
|
+
cache_content_height = (self) => {
|
262
|
+
var { node, content } = self,
|
263
|
+
max_height, header, header_height, max_code_height, code_container;
|
264
|
+
|
265
|
+
max_height = parseInt(window.getComputedStyle(content).maxHeight, 10);
|
266
|
+
|
267
|
+
content.setAttribute(attrs.cached_max_height, max_height);
|
268
|
+
|
269
|
+
header = node.querySelector(`.${classes.header}`);
|
270
|
+
header_height = (header ? header.offsetHeight : 0);
|
271
|
+
max_code_height = `${max_height - header_height}px`;
|
272
|
+
|
273
|
+
for(code_container of Array.from(node.querySelectorAll(`.${classes.code_container}`))) {
|
274
|
+
code_container.style.maxHeight = max_code_height;
|
275
|
+
}
|
276
|
+
};
|
277
|
+
|
278
|
+
//*
|
279
|
+
// Does all of the necessary work to manage the two-way communication between
|
280
|
+
// a code block connected to an `iframe` and that `iframe`. This includes
|
281
|
+
// listening for changes to markup of the associated demo and triggering an
|
282
|
+
// intial markup request to get the most up-to-date representation possible.
|
283
|
+
//
|
284
|
+
// @param {Object} self - The internal details of a [`CodeBlock`](@link).
|
285
|
+
//
|
286
|
+
// @private
|
287
|
+
|
288
|
+
hook_up_iframe_communication = (self) => {
|
289
|
+
var communicator = Communicator(),
|
290
|
+
registered = communicator.register.from_node(self.node),
|
291
|
+
handle_markup_change, handle_class_change;
|
292
|
+
|
293
|
+
if(!registered) { return false; }
|
294
|
+
|
295
|
+
handle_markup_change = (event) => {
|
296
|
+
if(!event.html || !self.code_caches.markup) { return; }
|
297
|
+
self.code_caches.markup.code = event.html;
|
298
|
+
};
|
299
|
+
|
300
|
+
handle_class_change = (event) => {
|
301
|
+
if(!self.code_caches.helper) { return; }
|
302
|
+
if(event.details.add === undefined) { event.details.add = event.add; }
|
303
|
+
self.code_caches.helper.update(event.details);
|
304
|
+
};
|
305
|
+
|
306
|
+
communicator.on(Events.types.markup_request, handle_markup_change);
|
307
|
+
communicator.on(Events.types.markup_change, handle_markup_change);
|
308
|
+
communicator.on(Events.types.class_change, handle_class_change);
|
309
|
+
|
310
|
+
communicator.trigger(Events.types.markup_request);
|
311
|
+
return communicator;
|
312
|
+
};
|
313
|
+
|
314
|
+
attach_event_listeners = (self) => {
|
315
|
+
var select = self.node.querySelector(`.${select_classes.root}`);
|
316
|
+
|
317
|
+
if(select && self.communicator) {
|
318
|
+
select.addEventListener("change", function(event) {
|
319
|
+
self.communicator.trigger(Events.types.markup_request, {
|
320
|
+
demo: event.target.value
|
321
|
+
});
|
322
|
+
});
|
323
|
+
}
|
324
|
+
};
|
325
|
+
|
326
|
+
//*
|
327
|
+
// An API for cacheing, updating, and highlighting code within a code block.
|
328
|
+
//
|
329
|
+
// @param {HTMLElement} node - The main code block.
|
330
|
+
//
|
331
|
+
// @private
|
332
|
+
// @factory
|
333
|
+
|
334
|
+
CodeCaches = (() => {
|
335
|
+
const languages = {
|
336
|
+
markup: ["html"],
|
337
|
+
helper: ["erb", "haml", "slim"]
|
338
|
+
};
|
339
|
+
|
340
|
+
var CodeCache;
|
341
|
+
|
342
|
+
CodeCache = (node, options = {}) => {
|
343
|
+
var language = node.getAttribute(attrs.language) || "html",
|
344
|
+
dom_code = node.querySelector("code"),
|
345
|
+
code = dom_code.innerHTML,
|
346
|
+
helper_cache = null, code_cache;
|
347
|
+
|
348
|
+
code_cache = {
|
349
|
+
language: language,
|
350
|
+
highlight() { this.code = code; },
|
351
|
+
get code() { return code; },
|
352
|
+
set code(new_code) {
|
353
|
+
code = new_code;
|
354
|
+
dom_code.innerHTML = clean_and_highlight_code(new_code, {
|
355
|
+
language_code: language,
|
356
|
+
collapse_newlines: options.generated_from_helper
|
357
|
+
});
|
358
|
+
}
|
359
|
+
};
|
360
|
+
|
361
|
+
code_cache.highlight();
|
362
|
+
|
363
|
+
if(languages.helper.includes(language)) {
|
364
|
+
helper_cache = {};
|
365
|
+
|
366
|
+
Object.defineProperty(code_cache, "update", {
|
367
|
+
value: function(change) {
|
368
|
+
this.code = update_helper(this.code, change, helper_cache);
|
369
|
+
}
|
370
|
+
});
|
371
|
+
}
|
372
|
+
|
373
|
+
return code_cache;
|
374
|
+
};
|
375
|
+
|
376
|
+
return (node) => {
|
377
|
+
var code_nodes, code_caches, api, index;
|
378
|
+
|
379
|
+
code_nodes = Array.from(node.querySelectorAll(`.${classes.code}`));
|
380
|
+
code_caches = code_nodes.map((code_node) => {
|
381
|
+
return CodeCache(code_node, { generated_from_helper: code_nodes.length > 1 });
|
382
|
+
});
|
383
|
+
|
384
|
+
api = {
|
385
|
+
get markup() {
|
386
|
+
return code_caches.filter((code_cache) => languages.markup.includes(code_cache.language))[0];
|
387
|
+
},
|
388
|
+
|
389
|
+
get helper() {
|
390
|
+
return code_caches.filter((code_cache) => languages.helper.includes(code_cache.language))[0];
|
391
|
+
},
|
392
|
+
|
393
|
+
length: code_caches.length
|
394
|
+
};
|
395
|
+
|
396
|
+
for(index = 0; index < code_caches.length; index++) {
|
397
|
+
Object.defineProperty(api, index, { value: code_caches[index] });
|
398
|
+
}
|
399
|
+
|
400
|
+
return api;
|
401
|
+
};
|
402
|
+
})();
|
403
|
+
|
404
|
+
//*
|
405
|
+
// The constructor around a code block.
|
406
|
+
//
|
407
|
+
// @factory
|
408
|
+
// @public
|
409
|
+
//
|
410
|
+
// @param {HTMLElement} node - The node with the `code-block` root class.
|
411
|
+
|
412
|
+
CodeBlock = (node) => {
|
413
|
+
var self, api, toggle;
|
414
|
+
|
415
|
+
self = {
|
416
|
+
node: node,
|
417
|
+
is_hidden: node.classList.contains(states.root.hidden),
|
418
|
+
toggler: node.querySelector(`.${classes.toggler}`),
|
419
|
+
content: node.querySelector(`.${classes.content}`),
|
420
|
+
code_caches: CodeCaches(node)
|
421
|
+
};
|
422
|
+
|
423
|
+
self.communicator = hook_up_iframe_communication(self);
|
424
|
+
|
425
|
+
attach_event_listeners(self);
|
426
|
+
|
427
|
+
if(self.is_hidden) { hide(self, { without_transition: true }); }
|
428
|
+
if(self.toggler) { cache_content_height(self); }
|
429
|
+
|
430
|
+
//*
|
431
|
+
// Toggles the code block.
|
432
|
+
//
|
433
|
+
// @method
|
434
|
+
|
435
|
+
toggle = () => { return (self.is_hidden ? show(self) : hide(self)); };
|
436
|
+
api = { toggle };
|
437
|
+
|
438
|
+
return api;
|
439
|
+
};
|
440
|
+
|
441
|
+
CodeBlock.init = Builder.initialize_once(CodeBlock, { name: classes.root, cache: true });
|
442
|
+
|
443
|
+
export { classes, states, variants, attrs };
|
444
|
+
export default CodeBlock;
|
@@ -0,0 +1,244 @@
|
|
1
|
+
import Events from "../../utilities/events";
|
2
|
+
import UIEvents from "../../utilities/ui_events";
|
3
|
+
import { Communicator } from "../iframe";
|
4
|
+
|
5
|
+
//*
|
6
|
+
// The name of classes relevant to `Demo`.
|
7
|
+
// @object
|
8
|
+
|
9
|
+
const classes = {
|
10
|
+
root: "demo",
|
11
|
+
section: "demo__section",
|
12
|
+
content: "content"
|
13
|
+
};
|
14
|
+
|
15
|
+
var Demo, create_self, set_correct_background_color, allocate_minimum_height;
|
16
|
+
|
17
|
+
//*
|
18
|
+
// The delay after a change in markup to keep track of height changes and
|
19
|
+
// communicate them to the attached [`Iframe`](@link).
|
20
|
+
//
|
21
|
+
// @type Number
|
22
|
+
// @value 1000
|
23
|
+
|
24
|
+
const HEIGHT_CHANGE_WATCH_DURATION = 1000;
|
25
|
+
|
26
|
+
//*
|
27
|
+
// Updates the background color of the parent for the demo to match the
|
28
|
+
// background color of the last section. This is necessary because, during the
|
29
|
+
// transition from a larger size to a smaller size, not doing this would show
|
30
|
+
// white below all of the demo sections regardless of their color.
|
31
|
+
//
|
32
|
+
// @private
|
33
|
+
// @param {HTMLElement} node - The base `Demo` node.
|
34
|
+
|
35
|
+
set_correct_background_color = (node) => {
|
36
|
+
var parent = node.parentNode,
|
37
|
+
sections = node.querySelectorAll(`.${classes.section}`),
|
38
|
+
last_section = sections[sections.length - 1];
|
39
|
+
|
40
|
+
parent.style.backgroundColor = window.getComputedStyle(last_section).backgroundColor;
|
41
|
+
};
|
42
|
+
|
43
|
+
//*
|
44
|
+
// Spreads the minimum height of the total demo between the sections that are
|
45
|
+
// present. This is important because the resizable demo will show the full
|
46
|
+
// minimum width, so if there are colored sections that don't completely fill
|
47
|
+
// the minimum width, there will be an awkward white patch below the sections.
|
48
|
+
//
|
49
|
+
// @private
|
50
|
+
// @param {HTMLElement} node - The base `Demo` node.
|
51
|
+
|
52
|
+
allocate_minimum_height = (node) => {
|
53
|
+
var min_height = parseInt(window.getComputedStyle(node).minHeight, 10),
|
54
|
+
demo_sections = node.querySelectorAll(`.${classes.section}`), demo_section;
|
55
|
+
|
56
|
+
for(demo_section of demo_sections) {
|
57
|
+
demo_section.style.minHeight = `${min_height / demo_sections.length}px`;
|
58
|
+
}
|
59
|
+
};
|
60
|
+
|
61
|
+
//*
|
62
|
+
// Caches all of the internal details for an [`Demo`](@link).
|
63
|
+
//
|
64
|
+
// @private
|
65
|
+
// @param {HTMLElement} node - The node backing the `Demo`.
|
66
|
+
// @returns Object - The private, internal details of the `Demo`.
|
67
|
+
|
68
|
+
create_self = (node) => {
|
69
|
+
return {
|
70
|
+
markup_source: document.querySelector(`.${classes.content}`),
|
71
|
+
demo_handlers: window.parent.demo_handlers || {},
|
72
|
+
parent: node.parentNode,
|
73
|
+
height: 0,
|
74
|
+
actions: {},
|
75
|
+
context: {
|
76
|
+
body: document.body,
|
77
|
+
document: document
|
78
|
+
}
|
79
|
+
};
|
80
|
+
};
|
81
|
+
|
82
|
+
//*
|
83
|
+
// The constructor for a new `Demo`. This will sign the demo up for all the
|
84
|
+
// required events and will do the required initialization work.
|
85
|
+
//
|
86
|
+
// @param {HTMLElement} node - The base `Demo` node.
|
87
|
+
//
|
88
|
+
// @factory
|
89
|
+
|
90
|
+
Demo = (node) => {
|
91
|
+
var self = create_self(node),
|
92
|
+
communicator = Communicator(),
|
93
|
+
send_markup, height_update, apply_class_change;
|
94
|
+
|
95
|
+
//*
|
96
|
+
// Sends the markup for the current "main" section.
|
97
|
+
//
|
98
|
+
// @param {Object} [event = {}] - The (optional) event that specifies the demo
|
99
|
+
// to send markup for.
|
100
|
+
//
|
101
|
+
// @method
|
102
|
+
// @private
|
103
|
+
|
104
|
+
send_markup = (event = {}) => {
|
105
|
+
if(event.demo) {
|
106
|
+
self.markup_source = document.querySelector(`#${classes.section}--${event.demo} .${classes.content}`);
|
107
|
+
}
|
108
|
+
|
109
|
+
communicator.trigger(Events.types.markup_request, {
|
110
|
+
html: self.markup_source.innerHTML
|
111
|
+
});
|
112
|
+
};
|
113
|
+
|
114
|
+
//*
|
115
|
+
// Sends the height for the demo as a whole, and sets that height on the
|
116
|
+
// demo's container. The height is set on the container after a delay to
|
117
|
+
// ensure that there is no patch of unstyled background color underneath a
|
118
|
+
// demo that is shrinking.
|
119
|
+
//
|
120
|
+
// @method
|
121
|
+
// @private
|
122
|
+
|
123
|
+
height_update = () => {
|
124
|
+
var new_height = node.offsetHeight;
|
125
|
+
if(new_height === self.height) { return; }
|
126
|
+
|
127
|
+
self.height = new_height;
|
128
|
+
setTimeout(() => {
|
129
|
+
self.parent.style.minHeight = `${new_height}px`;
|
130
|
+
}, HEIGHT_CHANGE_WATCH_DURATION);
|
131
|
+
|
132
|
+
communicator.trigger(Events.types.height_change, { height: new_height });
|
133
|
+
};
|
134
|
+
|
135
|
+
//*
|
136
|
+
// Applies a class change to the demo. This class change will avoid adding
|
137
|
+
// classes to components that have a class procluded from the new class, will
|
138
|
+
// filter to the passed filter, and will perform the optional JavaScript
|
139
|
+
// action instead of a simple class addition/ removal. If appropriate, the
|
140
|
+
// component will then return the class change event, send a markup change
|
141
|
+
// event, and send a height update event.
|
142
|
+
//
|
143
|
+
// @param {Object} event - The class change event.
|
144
|
+
// @private
|
145
|
+
//
|
146
|
+
|
147
|
+
apply_class_change = (event) => {
|
148
|
+
var details = event.details,
|
149
|
+
markup_change_in_source = false,
|
150
|
+
minimum_one_class_change = false,
|
151
|
+
matches = node.querySelectorAll(`.${classes.content} .${details.for}`),
|
152
|
+
bail_early, class_list, action, match, preclude;
|
153
|
+
|
154
|
+
if(details.filter_to) {
|
155
|
+
// Check on matches
|
156
|
+
matches = matches.filter((a_match) => a_match.matches(details.filter_to));
|
157
|
+
}
|
158
|
+
|
159
|
+
// Some height changes may occur over time. Watch for transitions
|
160
|
+
// and send height again on each transitionend event
|
161
|
+
//
|
162
|
+
// TODO: integrate better iframe resizing
|
163
|
+
// see: https://github.com/davidjbradshaw/iframe-resizer/tree/master/test
|
164
|
+
|
165
|
+
document.addEventListener(UIEvents.transition_end, height_update);
|
166
|
+
|
167
|
+
for(match of matches) {
|
168
|
+
bail_early = false;
|
169
|
+
class_list = match.classList;
|
170
|
+
action = null;
|
171
|
+
|
172
|
+
for(preclude of details.preclude) {
|
173
|
+
if(class_list.contains(preclude)) {
|
174
|
+
bail_early = true;
|
175
|
+
break;
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
if(bail_early) { continue; }
|
180
|
+
|
181
|
+
minimum_one_class_change = true;
|
182
|
+
|
183
|
+
action = details.javascript_action;
|
184
|
+
if(action) {
|
185
|
+
if(!event.add) {
|
186
|
+
action = action.replace(/addClass/g, "removeClass")
|
187
|
+
.replace(/classList\.add/g, "classList.remove")
|
188
|
+
.replace(/(true|false)/, { true: "false", false: "true" });
|
189
|
+
}
|
190
|
+
|
191
|
+
eval(action);
|
192
|
+
} else {
|
193
|
+
class_list[event.add ? "add" : "remove"](details.name);
|
194
|
+
}
|
195
|
+
|
196
|
+
// Only update markup in source when the markup source is above in the
|
197
|
+
// DOM tree.
|
198
|
+
markup_change_in_source = markup_change_in_source ||
|
199
|
+
$(match).closest(self.markup_source).length > 0;
|
200
|
+
}
|
201
|
+
|
202
|
+
if(markup_change_in_source) { send_markup(); }
|
203
|
+
|
204
|
+
if(minimum_one_class_change) {
|
205
|
+
// Pass along the class change event
|
206
|
+
communicator.trigger(event.type, event);
|
207
|
+
height_update();
|
208
|
+
}
|
209
|
+
|
210
|
+
setTimeout(() => {
|
211
|
+
document.removeEventListener(UIEvents.transition_end, height_update);
|
212
|
+
}, HEIGHT_CHANGE_WATCH_DURATION);
|
213
|
+
};
|
214
|
+
|
215
|
+
communicator.register.from_node(node);
|
216
|
+
communicator.on(Events.types.height_request, height_update);
|
217
|
+
communicator.on(Events.types.markup_request, send_markup);
|
218
|
+
communicator.on(Events.types.class_change, apply_class_change);
|
219
|
+
|
220
|
+
window.addEventListener("resize", height_update);
|
221
|
+
setInterval(height_update, HEIGHT_CHANGE_WATCH_DURATION);
|
222
|
+
|
223
|
+
height_update();
|
224
|
+
allocate_minimum_height(node);
|
225
|
+
set_correct_background_color(node);
|
226
|
+
|
227
|
+
return {};
|
228
|
+
};
|
229
|
+
|
230
|
+
//*
|
231
|
+
// Initializes the `Demo` component.
|
232
|
+
//
|
233
|
+
// @method
|
234
|
+
// @static
|
235
|
+
//
|
236
|
+
// @param {HTMLElement} [context = document] - The context in which to search
|
237
|
+
// for DOM nodes that should be used as the root of an `Demo` component.
|
238
|
+
|
239
|
+
Demo.init = (context = document) => {
|
240
|
+
var demo, demos = Array.from(context.querySelectorAll(`.${classes.root}`));
|
241
|
+
for(demo of demos) { Demo(demo); }
|
242
|
+
};
|
243
|
+
|
244
|
+
export default Demo;
|