docks_theme_api 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (237) hide show
  1. checksums.yaml +15 -0
  2. data/.babelrc +4 -0
  3. data/.editorconfig +8 -0
  4. data/.eslintrc +115 -0
  5. data/.gitignore +24 -0
  6. data/.rubocop.yml +20 -0
  7. data/.travis.yml +16 -0
  8. data/Gemfile +4 -0
  9. data/README.md +5 -0
  10. data/Rakefile +3 -0
  11. data/assets/images/icons.svg +63 -0
  12. data/assets/scripts/coffeescript/pattern_library_helpers.coffee +8 -0
  13. data/assets/scripts/javascript/pattern_library_helpers.js +11 -0
  14. data/assets/scripts/pattern_library.js +10380 -0
  15. data/assets/scripts/pattern_library_demo.js +0 -0
  16. data/assets/styles/less/pattern-library-helpers.less +103 -0
  17. data/assets/styles/pattern-library-demo.css +1882 -0
  18. data/assets/styles/pattern-library.css +1882 -0
  19. data/assets/styles/sass/pattern-library-helpers.sass +90 -0
  20. data/assets/styles/scss/pattern-library-helpers.scss +99 -0
  21. data/assets/styles/stylus/pattern-library-helpers.styl +90 -0
  22. data/assets/templates/erb/demo.erb +26 -0
  23. data/assets/templates/erb/layouts/demo.erb +17 -0
  24. data/assets/templates/erb/layouts/pattern.erb +76 -0
  25. data/assets/templates/erb/partials/sidebar.erb +124 -0
  26. data/assets/templates/erb/partials/symbols/class.erb +1 -0
  27. data/assets/templates/erb/partials/symbols/demo.erb +40 -0
  28. data/assets/templates/erb/partials/symbols/factory.erb +70 -0
  29. data/assets/templates/erb/partials/symbols/function.erb +103 -0
  30. data/assets/templates/erb/partials/symbols/mixin.erb +62 -0
  31. data/assets/templates/erb/partials/symbols/variable.erb +59 -0
  32. data/assets/templates/erb/pattern.erb +102 -0
  33. data/assets/templates/haml/demo.haml +14 -0
  34. data/assets/templates/haml/layouts/demo.haml +6 -0
  35. data/assets/templates/haml/layouts/pattern.haml +38 -0
  36. data/assets/templates/haml/partials/sidebar.haml +68 -0
  37. data/assets/templates/haml/partials/symbols/class.haml +1 -0
  38. data/assets/templates/haml/partials/symbols/demo.haml +23 -0
  39. data/assets/templates/haml/partials/symbols/factory.haml +38 -0
  40. data/assets/templates/haml/partials/symbols/function.haml +54 -0
  41. data/assets/templates/haml/partials/symbols/mixin.haml +31 -0
  42. data/assets/templates/haml/partials/symbols/variable.haml +22 -0
  43. data/assets/templates/haml/pattern.haml +54 -0
  44. data/assets/templates/slim/demo.slim +24 -0
  45. data/assets/templates/slim/layouts/demo.slim +5 -0
  46. data/assets/templates/slim/layouts/pattern.slim +48 -0
  47. data/assets/templates/slim/partials/sidebar.slim +112 -0
  48. data/assets/templates/slim/partials/symbols/class.slim +1 -0
  49. data/assets/templates/slim/partials/symbols/demo.slim +30 -0
  50. data/assets/templates/slim/partials/symbols/factory.slim +57 -0
  51. data/assets/templates/slim/partials/symbols/function.slim +81 -0
  52. data/assets/templates/slim/partials/symbols/mixin.slim +45 -0
  53. data/assets/templates/slim/partials/symbols/variable.slim +35 -0
  54. data/assets/templates/slim/pattern.slim +63 -0
  55. data/docks_config.rb +32 -0
  56. data/docks_theme_api.gemspec +37 -0
  57. data/gulpfile.js +88 -0
  58. data/karma.conf.js +6 -0
  59. data/lib/docks_theme_api/components/base_component.rb +99 -0
  60. data/lib/docks_theme_api/components/code_block_component.rb +10 -0
  61. data/lib/docks_theme_api/components/popover_component.rb +15 -0
  62. data/lib/docks_theme_api/components/table_component.rb +34 -0
  63. data/lib/docks_theme_api/components/tablist_component.rb +11 -0
  64. data/lib/docks_theme_api/components.rb +21 -0
  65. data/lib/docks_theme_api/helpers/ui_helper.rb +69 -0
  66. data/lib/docks_theme_api/theme.rb +21 -0
  67. data/lib/docks_theme_api.rb +1 -0
  68. data/package.json +60 -0
  69. data/source/behaviors/filterable/filterable.coffee +353 -0
  70. data/source/behaviors/filterable/filterable.js +0 -0
  71. data/source/behaviors/filterable/filterable.scss +34 -0
  72. data/source/behaviors/filterable/package.json +3 -0
  73. data/source/behaviors/index.js +0 -0
  74. data/source/components/avatar/avatar.erb +20 -0
  75. data/source/components/avatar/avatar.js +142 -0
  76. data/source/components/avatar/avatar.scss +200 -0
  77. data/source/components/avatar/avatar_container.erb +13 -0
  78. data/source/components/avatar/package.json +3 -0
  79. data/source/components/avatar/spec/avatar_spec.js +81 -0
  80. data/source/components/badge/badge.scss +158 -0
  81. data/source/components/button/button.scss +213 -0
  82. data/source/components/card/card.scss +32 -0
  83. data/source/components/code_block/code-block.scss +353 -0
  84. data/source/components/code_block/code_block.erb +95 -0
  85. data/source/components/code_block/code_block.js +444 -0
  86. data/source/components/code_block/package.json +3 -0
  87. data/source/components/code_block/spec/code_block_spec.js +10 -0
  88. data/source/components/demo/demo.js +244 -0
  89. data/source/components/demo/demo.scss +90 -0
  90. data/source/components/demo/package.json +3 -0
  91. data/source/components/exploded/exploded.erb +25 -0
  92. data/source/components/exploded/exploded.js +694 -0
  93. data/source/components/exploded/exploded.scss +166 -0
  94. data/source/components/exploded/package.json +3 -0
  95. data/source/components/field/field.js +24 -0
  96. data/source/components/field/field.scss +101 -0
  97. data/source/components/field/package.json +3 -0
  98. data/source/components/header/header.scss +33 -0
  99. data/source/components/iframe/iframe.erb +12 -0
  100. data/source/components/iframe/iframe.js +381 -0
  101. data/source/components/iframe/package.json +3 -0
  102. data/source/components/index.js +37 -0
  103. data/source/components/inline_group/inline-group.scss +14 -0
  104. data/source/components/internal_link/internal_link.js +49 -0
  105. data/source/components/internal_link/package.json +3 -0
  106. data/source/components/list/list.scss +230 -0
  107. data/source/components/modal/modal.coffee +84 -0
  108. data/source/components/modal/modal.erb +19 -0
  109. data/source/components/modal/modal.js +0 -0
  110. data/source/components/modal/modal.scss +57 -0
  111. data/source/components/modal/package.json +3 -0
  112. data/source/components/notice/notice.scss +48 -0
  113. data/source/components/popover/package.json +3 -0
  114. data/source/components/popover/popover.coffee +562 -0
  115. data/source/components/popover/popover.erb +21 -0
  116. data/source/components/popover/popover.js +0 -0
  117. data/source/components/popover/popover.scss +139 -0
  118. data/source/components/range/range.scss +78 -0
  119. data/source/components/resizable/package.json +3 -0
  120. data/source/components/resizable/resizable.erb +30 -0
  121. data/source/components/resizable/resizable.js +250 -0
  122. data/source/components/resizable/resizable.scss +245 -0
  123. data/source/components/resizable/size_buttons.js +249 -0
  124. data/source/components/scroll_container/package.json +3 -0
  125. data/source/components/scroll_container/scroll-container.scss +4 -0
  126. data/source/components/scroll_container/scroll_container.js +24 -0
  127. data/source/components/section/section.scss +99 -0
  128. data/source/components/select/package.json +3 -0
  129. data/source/components/select/select.erb +21 -0
  130. data/source/components/select/select.js +35 -0
  131. data/source/components/select/select.scss +163 -0
  132. data/source/components/table/package.json +3 -0
  133. data/source/components/table/table.erb +16 -0
  134. data/source/components/table/table.js +351 -0
  135. data/source/components/table/table.scss +236 -0
  136. data/source/components/tablist/package.json +3 -0
  137. data/source/components/tablist/tablist.erb +13 -0
  138. data/source/components/tablist/tablist.js +246 -0
  139. data/source/components/tablist/tablist.scss +191 -0
  140. data/source/components/tablist/tablist_panel.erb +14 -0
  141. data/source/components/tablist/tablist_tab.erb +20 -0
  142. data/source/components/toggle/package.json +3 -0
  143. data/source/components/toggle/toggle.erb +11 -0
  144. data/source/components/toggle/toggle.js +211 -0
  145. data/source/components/toggle/toggle_container.erb +30 -0
  146. data/source/components/vertical_spacer/vertical-spacer.scss +3 -0
  147. data/source/components/vertical_stack/vertical-stack.scss +19 -0
  148. data/source/components/xray/package.json +3 -0
  149. data/source/components/xray/xray.erb +50 -0
  150. data/source/components/xray/xray.js +123 -0
  151. data/source/components/xray/xray.scss +79 -0
  152. data/source/foundation/app/app.js +15 -0
  153. data/source/foundation/app/package.json +3 -0
  154. data/source/pattern-library-demo.scss +13 -0
  155. data/source/pattern-library.scss +13 -0
  156. data/source/pattern_library.js +8 -0
  157. data/source/pattern_library_demo.js +8 -0
  158. data/source/structures/index.js +11 -0
  159. data/source/structures/sidebar/package.json +3 -0
  160. data/source/structures/sidebar/sidebar.js +69 -0
  161. data/source/structures/sidebar/sidebar.scss +79 -0
  162. data/source/utilities/builder/builder.js +138 -0
  163. data/source/utilities/builder/package.json +3 -0
  164. data/source/utilities/client/client.js +7 -0
  165. data/source/utilities/client/package.json +3 -0
  166. data/source/utilities/colors/colors.scss +112 -0
  167. data/source/utilities/defaults/defaults.scss +38 -0
  168. data/source/utilities/dom_cache/dom_cache.js +24 -0
  169. data/source/utilities/dom_cache/package.json +3 -0
  170. data/source/utilities/events/events.js +25 -0
  171. data/source/utilities/events/package.json +3 -0
  172. data/source/utilities/font_sizes/font-sizes.scss +85 -0
  173. data/source/utilities/foundation/a11y.scss +10 -0
  174. data/source/utilities/foundation/base.scss +29 -0
  175. data/source/utilities/foundation/icon.scss +114 -0
  176. data/source/utilities/foundation/layout.scss +67 -0
  177. data/source/utilities/foundation/page.scss +39 -0
  178. data/source/utilities/foundation/type.scss +208 -0
  179. data/source/utilities/functions/functions.scss +127 -0
  180. data/source/utilities/keycodes/keycodes.js +23 -0
  181. data/source/utilities/keycodes/package.json +3 -0
  182. data/source/utilities/markup/markup.js +90 -0
  183. data/source/utilities/markup/package.json +3 -0
  184. data/source/utilities/media/media.scss +172 -0
  185. data/source/utilities/mixins/mixins.scss +89 -0
  186. data/source/utilities/naming_convention/naming_convention.js +3 -0
  187. data/source/utilities/naming_convention/package.json +3 -0
  188. data/source/utilities/numbers/numbers.js +14 -0
  189. data/source/utilities/numbers/package.json +3 -0
  190. data/source/utilities/painting/package.json +3 -0
  191. data/source/utilities/painting/painting.js +7 -0
  192. data/source/utilities/pattern/package.json +3 -0
  193. data/source/utilities/pattern/pattern.js +50 -0
  194. data/source/utilities/query_string/package.json +3 -0
  195. data/source/utilities/query_string/query_string.js +24 -0
  196. data/source/utilities/template/package.json +3 -0
  197. data/source/utilities/template/template.js +10 -0
  198. data/source/utilities/text_range/package.json +3 -0
  199. data/source/utilities/text_range/text_range.js +30 -0
  200. data/source/utilities/ui_events/package.json +3 -0
  201. data/source/utilities/ui_events/ui_events.js +85 -0
  202. data/source/utilities/variables/variables.scss +18 -0
  203. data/source/utilities/z_indexes/z-indexes.scss +88 -0
  204. data/source/vendor/array_includes.js +28 -0
  205. data/source/vendor/highlight.js +1142 -0
  206. data/source/vendor/index.js +1 -0
  207. data/source/vendor/matrix.js +399 -0
  208. data/source/vendor/query_string.js +66 -0
  209. data/spec/assets/.eslintrc +9 -0
  210. data/spec/assets/spec_fixture.js +33 -0
  211. data/spec/assets/spec_helper.js +19 -0
  212. data/spec/lib/components/base_component_spec.rb +156 -0
  213. data/spec/lib/components_spec.rb +30 -0
  214. data/spec/lib/helpers/ui_helper_spec.rb +62 -0
  215. data/spec/lib/theme_spec.rb +25 -0
  216. data/spec/spec_helper.rb +15 -0
  217. data/tasks/gulp/.eslintrc +6 -0
  218. data/tasks/gulp/browser_sync.js +8 -0
  219. data/tasks/gulp/code_quality/scripts.js +10 -0
  220. data/tasks/gulp/config/index.js +116 -0
  221. data/tasks/gulp/minify/scripts.js +13 -0
  222. data/tasks/gulp/minify/styles.js +13 -0
  223. data/tasks/gulp/pattern_library/index.js +5 -0
  224. data/tasks/gulp/pattern_library/scripts.js +10 -0
  225. data/tasks/gulp/pattern_library/styles.js +10 -0
  226. data/tasks/gulp/scripts.js +8 -0
  227. data/tasks/gulp/spec/scripts.js +11 -0
  228. data/tasks/gulp/styles.js +17 -0
  229. data/tasks/gulp/utilities/babel/relative_require.js +22 -0
  230. data/tasks/gulp/utilities/babel/spec_helper.js +20 -0
  231. data/tasks/gulp/utilities/browserify_bundler.js +22 -0
  232. data/tasks/gulp/utilities/handle_errors.js +13 -0
  233. data/tasks/gulp/watch.js +9 -0
  234. data/tasks/rake/rspec.rake +7 -0
  235. data/tasks/rake/rubocop.rake +8 -0
  236. data/tasks/rake/templates.rake +50 -0
  237. metadata +470 -0
@@ -0,0 +1,163 @@
1
+ // ___ ___ ___ ___
2
+ // / /\ / /\ / /\ / /\ ___
3
+ // / /:/_ / /:/_ / /:/_ / /:/ / /\
4
+ // / /:/ /\ / /:/ /\ ___ ___ / /:/ /\ / /:/ / /:/
5
+ // / /:/ /::\ / /:/ /:/_/__/\ / /\/ /:/ /:/_ / /:/ ___ / /:/
6
+ // /__/:/ /:/\:\/__/:/ /:/ /\ \:\ / /:/__/:/ /:/ /\/__/:/ / /\/ /::\
7
+ // \ \:\/:/~/:/\ \:\/:/ /:/\ \:\ /:/\ \:\/:/ /:/\ \:\ / /:/__/:/\:\
8
+ // \ \::/ /:/ \ \::/ /:/ \ \:\/:/ \ \::/ /:/ \ \:\ /:/\__\/ \:\
9
+ // \__\/ /:/ \ \:\/:/ \ \::/ \ \:\/:/ \ \:\/:/ \ \:\
10
+ // /__/:/ \ \::/ \__\/ \ \::/ \ \::/ \__\/
11
+ // \__\/ \__\/ \__\/ \__\/
12
+
13
+ //*
14
+ // @pattern Select
15
+ //
16
+ // A custom `select` element. In order to present these in the same way across
17
+ // browsers, most of the styling is done on a node wrapped around an actual
18
+ // `select` element, and a bit of JavaScript that translates the focus/ blur
19
+ // on the contained `select` into a state class.
20
+ //
21
+ // @since 1.0.0
22
+
23
+
24
+
25
+ $select--font-size: (font-size(control) * 0.9);
26
+ $select--horizontal-padding: default(control-padding);
27
+ $select--line-height: default(control-size);
28
+ $select--icon-size: $select--font-size / 1.6;
29
+
30
+
31
+
32
+ //*
33
+ // The wrapper around a `select`. This wrapper provides most of the visual
34
+ // styling, including the focus styling. This part of the component also
35
+ // creates a triangle icon in place of the browser-supplied one.
36
+ //
37
+ // @helper
38
+ // <%= docks_select options: %w(Super_long_option_that_is_quite_long normal_size SMALL) %>
39
+
40
+ .select {
41
+ // position
42
+ position: relative;
43
+
44
+ // box model
45
+ display: inline-block;
46
+
47
+ // backdrop
48
+ border-radius: default(border-radius);
49
+ background-color: ui-color(gray);
50
+
51
+ &:after {
52
+ content: "";
53
+
54
+ // position
55
+ position: absolute;
56
+ right: $select--horizontal-padding;
57
+ top: 50%;
58
+ transform: rotate(45deg);
59
+
60
+ // box model
61
+ display: block;
62
+ height: $select--icon-size;
63
+ width: $select--icon-size;
64
+ margin-top: (-0.75 * $select--icon-size);
65
+
66
+ // backdrop
67
+ border: solid rgba(ui-color(gray, darker), 0.5);
68
+ border-width: 0 2px 2px 0;
69
+ pointer-events: none;
70
+ }
71
+
72
+ + .docks-button {
73
+ margin-left: half(default(spacing));
74
+ }
75
+ }
76
+
77
+ //*
78
+ // This state is applied to a `select` whose contained input is focused.
79
+
80
+ .select--is-focused {
81
+ border-color: color(blue);
82
+ }
83
+
84
+ //*
85
+ // A select that should be displayed in a monospace font (for example, because
86
+ // it is choosing a class name).
87
+
88
+ .select--code {
89
+ > .select__input {
90
+ @include type--monospace-font-family;
91
+ }
92
+ }
93
+
94
+
95
+
96
+ //*
97
+ // The actual `select`. Most of the styles here go to undoing the default
98
+ // browser styles for `select`s. All of the font sizing for the component must
99
+ // be placed here for those styles to display correctly.
100
+
101
+ .select__input {
102
+ // box model
103
+ @include remove-browser-styling;
104
+ box-sizing: border-box;
105
+ display: block;
106
+ width: 100%;
107
+ max-width: none;
108
+ height: auto;
109
+ padding: 0 (double($select--horizontal-padding) + $select--icon-size) 0 $select--horizontal-padding;
110
+
111
+ // backdrop
112
+ background: transparent;
113
+ border: none;
114
+
115
+ // type
116
+ font-size: $select--font-size;
117
+ line-height: default(control-size);
118
+ font-family: inherit;
119
+ color: ui-color(gray, darker);
120
+ // font-family: Consolas, monospace;
121
+
122
+ &:focus {
123
+ outline: none;
124
+ border: none;
125
+ }
126
+
127
+ > option {
128
+ color: color(slate);
129
+ }
130
+ }
131
+
132
+ // All of the below hacks courtesy of Todd Parker's fine worK here:
133
+ // http://jsbin.com / yaruh / 49 / edit
134
+
135
+ // Hides native select arrow in IE 10+
136
+ _:-ms-input-placeholder, :root .select__input::-ms-expand {
137
+ display: none;
138
+ }
139
+
140
+ // Hides blue background on focus in IE 10+
141
+ _:-ms-input-placeholder, :root .select__input:focus::-ms-value {
142
+ background: transparent;
143
+ }
144
+
145
+ // Crazy hacks to target FF and clip the native select arrow
146
+ @supports (-moz-appearance:meterbar) and (background-blend-mode:difference,normal) {
147
+
148
+ _::-moz-progress-bar,
149
+ body:last-child .select__input__wrapper {
150
+ overflow: hidden;
151
+ }
152
+
153
+ _::-moz-progress-bar,
154
+ body:last-child .select__input {
155
+ width: calc(100% + 30px);
156
+ }
157
+ }
158
+
159
+ // Kills the stupid focus ring in FF
160
+ .select__input:-moz-focusring {
161
+ color: transparent;
162
+ text-shadow: 0 0 0 black;
163
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "main": "table.js"
3
+ }
@@ -0,0 +1,16 @@
1
+ <div class="table__container">
2
+ <div class="table__actions">
3
+ <div class="docks-button__segmented-container">
4
+ <button class="docks-button" table-action="shift-left" style="font-family: Consolas;">&lt;</button>
5
+ <button class="docks-button" table-action="shift-right" style="font-family: Consolas;">&gt;</button>
6
+ </div>
7
+ </div>
8
+
9
+ <div class="table__backdrop">
10
+ <div class="table__scroller">
11
+ <table class="table">
12
+ <%= capture(table, &table.block) %>
13
+ </table>
14
+ </div>
15
+ </div>
16
+ </div>
@@ -0,0 +1,351 @@
1
+ import Builder from "~utilities/builder";
2
+ import Keycodes from "~utilities/keycodes";
3
+
4
+ const classes = {
5
+ root: "table",
6
+ header: "table__header",
7
+ body: "table__body",
8
+ row: "table__row",
9
+ cell: "table__cell",
10
+ scroller: "table__scroller",
11
+ container: "table__container",
12
+ actions: "table__actions"
13
+ };
14
+
15
+ const states = {
16
+ scroller: { scrolled: "table__scroller--is-scrolled" },
17
+ container: { overflowing: "table__container--is-overflowing" }
18
+ };
19
+
20
+ const attrs = {
21
+ action: "table-action"
22
+ };
23
+
24
+ const actions = {
25
+ shift_right: "shift-right",
26
+ shift_left: "shift-left"
27
+ };
28
+
29
+ var Table, cache_preferred_widths, check_for_overflow, shift_table_right,
30
+ shift_table_left, last_visible_cell, handle_keypress, handle_scroll,
31
+ initialize_table_actions, update_actions;
32
+
33
+ //*
34
+ // Calculates and applies the intrinsic widths of the columns of a `table`,
35
+ // keeping in mind the effective maximum column size implied by the `min-width`
36
+ // set on the table.
37
+ //
38
+ // The intrinsic widths of each column are applied only once, to the header
39
+ // cells of the column. These are applied by using them as the `min-width`s for
40
+ // each header cell, so that the table will appropriately overflow once the
41
+ // space available to the table is less than the sum of its intrinsic widths.
42
+ //
43
+ // A side effect of this function is that `self` is augmented with the minimum
44
+ // total intrinsic width of its columns (`min_width`).
45
+ //
46
+ // @param {Object} self - The internal details of a `Table`.
47
+ // @private
48
+
49
+ cache_preferred_widths = (self) => {
50
+ var table = self.root,
51
+ clone = table.parentNode.parentNode.cloneNode(true),
52
+ width_calculation_container, clone_table, cloned_header_cells;
53
+
54
+ // For the purposes of the width calculations, let the table be at the smaller
55
+ // of its intrinsic size and the `min-width` set in CSS.
56
+ clone.style.maxWidth = window.getComputedStyle(table).minWidth;
57
+ table.style.minWidth = "0px";
58
+ clone.style.display = "inline-block";
59
+
60
+ // Creates a container that won't restrict the size of the table.
61
+ width_calculation_container = $("<div style='width: 10000px; visibility: hidden; height: 0;' />")[0];
62
+ width_calculation_container.appendChild(clone);
63
+ document.body.appendChild(width_calculation_container);
64
+
65
+ clone_table = clone.querySelector(`.${classes.root}`);
66
+ self.min_width = clone_table.offsetWidth; // sum of constrained intrinsic widths
67
+
68
+ // Apply the constrained intrinsic widths to each of the header cells in the
69
+ // actual table.
70
+ cloned_header_cells = Array.from(clone.querySelectorAll(`.${classes.header} .${classes.cell}`));
71
+ self.header_cells.forEach((cell, index) => {
72
+ cell.style.minWidth = `${cloned_header_cells[index].offsetWidth}px`;
73
+ });
74
+
75
+ // Cleanup.
76
+ document.body.removeChild(width_calculation_container);
77
+ };
78
+
79
+ //*
80
+ // Determines whether or not there is overflow and performs all necessary size
81
+ // and other DOM updates. This includes fixing the size of the first cell in a
82
+ // row and adding a compensating amount of left padding to the second cell in
83
+ // each row when the table should overflow, and reversing this when it no longer
84
+ // needs to do so.
85
+ //
86
+ // @param {Object} self - The internal details of a `Table`.
87
+ // @private
88
+
89
+ check_for_overflow = (self) => {
90
+ var { scroller, root, container, overflowing, min_width } = self,
91
+ scroller_width = scroller.offsetWidth,
92
+ first_cell_width, cell, available_space, index;
93
+
94
+ if(!scroller_width) { return; }
95
+
96
+ // Newly overflowing, get the header's width and apply that same width
97
+ // to each first cell (since they'll be absolutely positioned), and add an
98
+ // equivalent amount of left padding to the second cell.
99
+ if(!overflowing && scroller_width < min_width) {
100
+ for(cell of Array.from(root.querySelectorAll(`.${classes.cell}:first-child`))) {
101
+ first_cell_width = first_cell_width || cell.offsetWidth;
102
+
103
+ cell.style.width = `${first_cell_width}px`;
104
+ self.scroller.style.paddingLeft = `${first_cell_width - 1}px`;
105
+ }
106
+
107
+ container.classList.add(states.container.overflowing);
108
+ self.overflowing = true;
109
+ }
110
+
111
+ // No longer overflowing — reverse what we did before!
112
+ if(overflowing && scroller_width >= min_width) {
113
+ for(cell of Array.from(root.querySelectorAll(`.${classes.cell}:first-child`))) {
114
+ first_cell_width = first_cell_width || cell.offsetWidth;
115
+
116
+ cell.style.width = null;
117
+ self.scroller.style.paddingLeft = null;
118
+ }
119
+
120
+ for(cell of self.header_cells) { cell.style.maxWidth = null; }
121
+
122
+ container.classList.remove(states.container.overflowing);
123
+ self.overflowing = false;
124
+ }
125
+
126
+ // Even if already overflowing, update the max-widths of columns such that the
127
+ // persistant cell + any other cell <= the total width.
128
+ if(scroller_width < min_width) {
129
+ available_space = scroller_width - self.header_cells[0].offsetWidth;
130
+
131
+ for(index = 1; index++; index < self.header_cells.length) {
132
+ self.header_cells[index].style.maxWidth = `${available_space}px`;
133
+ }
134
+ }
135
+ };
136
+
137
+ last_visible_cell = (self) => {
138
+ var last_cell = self.header_cells[1],
139
+ parent_width = self.scroller.scrollLeft + self.scroller.offsetWidth - parseInt(self.scroller.style.paddingLeft, 10),
140
+ width_so_far = last_cell.offsetWidth,
141
+ cell, index;
142
+
143
+ for(index = 2; index++; index < self.header_cells[index]) {
144
+ cell = self.header_cells[index];
145
+ if((width_so_far + cell.offsetWidth) > parent_width) { break; }
146
+ last_cell = cell;
147
+ width_so_far += cell.offsetWidth;
148
+ }
149
+
150
+ return [last_cell, parent_width - width_so_far];
151
+ };
152
+
153
+ //*
154
+ // Shifts the `Table` represented by `self` to the right by one column. If the
155
+ // table currently has a column that is partially visible on the right, the
156
+ // table will be scrolled such that that entire column is visible. If a column
157
+ // is entirely visible and pressed right against the right edge of the scroll
158
+ // area, the next (fully hidden) column will be shown.
159
+ //
160
+ // This has no effect if the table is already fully scrolled.
161
+ //
162
+ // @param {Object} self - The internal details of a `Table`.
163
+
164
+ shift_table_right = (self) => {
165
+ var last_cell, next_cell_overlap;
166
+
167
+ if(!self.overflowing) { return; }
168
+ [last_cell, next_cell_overlap] = last_visible_cell(self);
169
+
170
+ if(last_cell === self.header_cells[self.header_cells.length - 1]) { return; }
171
+ self.scroller.scrollLeft += (last_cell.nextElementSibling.offsetWidth - next_cell_overlap);
172
+ self.scroller.classList.add(states.scroller.scrolled);
173
+ update_actions(self);
174
+ };
175
+
176
+ //*
177
+ // Shifts the `Table` represented by `self` to the left by one column. If the
178
+ // table currently has a column that is partially visible on the right, the
179
+ // table will be scrolled such that that entire column is hidden. If a column
180
+ // is entirely visible and pressed right against the right edge of the scroll
181
+ // area, that column will be scrolled out of view.
182
+ //
183
+ // This has no effect if the table is at a scroll position of 0.
184
+ //
185
+ // @param {Object} self - The internal details of a `Table`.
186
+
187
+ shift_table_left = (self) => {
188
+ var last_cell, next_cell_overlap, scroll_delta;
189
+
190
+ if(!self.overflowing) { return; }
191
+ [last_cell, next_cell_overlap] = last_visible_cell(self);
192
+ scroll_delta = (next_cell_overlap ? -next_cell_overlap : -last_cell.offsetWidth);
193
+
194
+ self.scroller.scrollLeft += scroll_delta;
195
+ if(!self.scroller.scrollLeft) { self.scroller.classList.remove(states.scroller.scrolled); }
196
+ update_actions(self);
197
+ };
198
+
199
+ //*
200
+ // Handles a keypress while focused on the table. Only left/ right/ up/ down
201
+ // keypresses are handled here: left and down will shift the table left, while
202
+ // right and up will shift the table right.
203
+ //
204
+ // @param {Object} event - The original `keypress` event.
205
+ // @param {Object} self - The internal details of a `Table`.
206
+ // @private
207
+
208
+ handle_keypress = (event, self) => {
209
+ switch(event.which) {
210
+ case Keycodes.RIGHT:
211
+ event.preventDefault();
212
+ shift_table_right(self);
213
+ break;
214
+ case Keycodes.LEFT:
215
+ event.preventDefault();
216
+ shift_table_left(self);
217
+ break;
218
+ }
219
+ };
220
+
221
+ //*
222
+ // Handles scrolling on the table by updating the classes on the scroller/
223
+ // action buttons to reflect the current scroll position.
224
+ //
225
+ // @param {Object} event - The original `scroll` event.
226
+ // @param {Object} self - The internal details of a `Table`.
227
+ // @private
228
+
229
+ handle_scroll = (event, self) => {
230
+ var scroller;
231
+
232
+ if(!self.overflowing) { return; }
233
+
234
+ scroller = self.scroller;
235
+ if(scroller.scrollLeft > 0) {
236
+ scroller.classList.add(states.scroller.scrolled);
237
+ } else {
238
+ scroller.classList.remove(states.scroller.scrolled);
239
+ }
240
+
241
+ update_actions(self);
242
+ event.stopPropagation();
243
+ };
244
+
245
+ //*
246
+ // Hooks up the event handlers for table actions, stores the actions on
247
+ // `self.shifters` for easier access later, and performes the initial updates
248
+ // to make the state of the actions match the table itself.
249
+ //
250
+ // @param {Object} self - The internal details of a `Table`.
251
+ // @private
252
+
253
+ initialize_table_actions = (self) => {
254
+ var action;
255
+
256
+ self.shifters = {};
257
+ for(action of Array.from(self.container.querySelectorAll(`.${classes.actions} [${attrs.action}]`))) {
258
+ self.shifters[action.getAttribute(attrs.action).replace("shift-", "")] = action;
259
+ }
260
+
261
+ update_actions(self);
262
+
263
+ $(self.container).on("click", `.${classes.actions}`, (event) => {
264
+ switch(event.target.getAttribute(attrs.action)) {
265
+ case actions.shift_right:
266
+ shift_table_right(self);
267
+ break;
268
+ case actions.shift_left:
269
+ shift_table_left(self);
270
+ break;
271
+ }
272
+ });
273
+ };
274
+
275
+ //*
276
+ // Updates the table actions by disabling actions that can't be performed given
277
+ // the state of the table (for example, a left shifter when the table is fully
278
+ // scrolled to the left).
279
+ //
280
+ // @param {Object} self - The internal details of a `Table`.
281
+ // @private
282
+
283
+ update_actions = (() => {
284
+ var disable, enable;
285
+
286
+ disable = (shifter) => {
287
+ shifter.disabled = true;
288
+ shifter.classList.add(`${shifter.className.split(" ")[0]}--is-disabled`);
289
+ };
290
+
291
+ enable = (shifter) => {
292
+ shifter.disabled = false;
293
+ shifter.classList.remove(`${shifter.className.split(" ")[0]}--is-disabled`);
294
+ };
295
+
296
+ return (self) => {
297
+ var shifters = self.shifters,
298
+ scroll = self.scroller.scrollLeft;
299
+
300
+ if(!scroll) {
301
+ disable(shifters.left);
302
+ } else {
303
+ enable(shifters.left);
304
+ }
305
+
306
+ if((scroll + self.scroller.offsetWidth + 1) >= self.scroller.scrollWidth) {
307
+ disable(shifters.right);
308
+ } else {
309
+ enable(shifters.right);
310
+ }
311
+ };
312
+ })();
313
+
314
+ //*
315
+ // A factory for producing `Table` objects.
316
+ //
317
+ // @param {HTMLElement} root - The root (`.table`) node of the table. Note that
318
+ // this is not the container or scroller, but the
319
+ // actual `table` element itself.
320
+ //
321
+ // @factory
322
+
323
+ Table = (root) => {
324
+ var $root, self;
325
+
326
+ $root = $(root);
327
+ self = {
328
+ root,
329
+ scroller: $root.closest(`.${classes.scroller}`)[0],
330
+ container: $root.closest(`.${classes.container}`)[0],
331
+ overflowing: false,
332
+ header_cells: Array.from(root.querySelectorAll(`.${classes.header} .${classes.cell}`))
333
+ };
334
+
335
+ root.setAttribute("tabindex", "-1");
336
+ cache_preferred_widths(self);
337
+ check_for_overflow(self);
338
+ initialize_table_actions(self);
339
+
340
+ $(window).on("resize", () => {
341
+ check_for_overflow(self);
342
+ update_actions(self);
343
+ });
344
+
345
+ root.addEventListener("keydown", (event) => { handle_keypress(event, self); });
346
+ self.scroller.addEventListener("scroll", (event) => { handle_scroll(event, self); });
347
+ };
348
+
349
+ Table.init = () => {
350
+ Builder.build(Table, { name: classes.root });
351
+ };