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.
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
+ };