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,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;
|