├── .babelrc.js
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── dist
├── index.js
├── index.js.map
├── index.m.js
├── index.m.js.map
├── index.umd.js
└── index.umd.js.map
├── package.json
├── rollup.config.js
├── scripts
└── post_install.js
├── src
└── index.js
└── yarn.lock
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | test: {
4 | plugins: [
5 | [
6 | 'istanbul',
7 | {
8 | exclude: ['spec/**/*.js']
9 | }
10 | ]
11 | ]
12 | }
13 | },
14 | presets: [
15 | [
16 | '@babel/preset-env',
17 | {
18 | targets: {
19 | node: 'current'
20 | }
21 | }
22 | ]
23 | ],
24 | plugins: [
25 | '@babel/plugin-proposal-class-properties',
26 | '@babel/plugin-transform-classes',
27 | '@babel/plugin-proposal-object-rest-spread'
28 | ]
29 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | /node_modules
3 | yarn-error.log
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .babelrc
2 | .DS_Store
3 | .gitignore
4 | .yarn.lock
5 | /.git
6 | /node_modules
7 | rollup.config.js
8 | .babelrc.js
9 | .prettierrc.json
10 | /.vscode
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 leastbad
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Stimulus Image Grid
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | A Stimulus controller for beautiful image grids
10 | Tiny at <100 LOC
11 |
12 |
13 |
14 |
15 | - **Simple**: with only three optional parameters, this is a drop-in, code-free solution
16 | - **Unstyled**: Completely free of CSS opinions
17 | - **Responsive**: Scales to whatever bounding container you give it
18 | - **Backend Agnostic**: 100% client-side
19 | - **Performant AF**: uses the ResizeObserver so there's zero screen flicker
20 | - **Turbolinks**: compatible with Turbolinks by design
21 | - **MIT Licensed**: free for personal and commercial use
22 |
23 | [](http://www.youtube.com/watch?v=e08sFfBYoiE "Stimulus Image Grid")
24 |
25 | ## Built for StimulusJS
26 |
27 | This [Stimulus](https://stimulusjs.org/) controller allows you to make any configurations for the image grid directly with data attributes in your HTML. Once registered in your Stimulus application, you can use it anywhere you like.
28 |
29 | Here is a simple example:
30 |
31 | ```html
32 |
33 |

34 |

35 |

36 |

37 |

38 |
39 | ```
40 | Yes, that's really it.
41 |
42 | ### Credit where credit is due
43 |
44 | I don't know who wrote the original image-grid.js library. It shipped with a bunch of premium Bootstrap themes, but it relied on jQuery. While stimulus-image-grid is an improvement on the original in several significant ways, the actual meat and potatoes of the algorithm is 100% adapted from the code of a stranger. If you know who wrote [image-grid.js](https://github.com/Pactum/pactum.io/blob/9f2d162bc21f26d62c5d4ba801309bdeb8b9fa9e/v4/js/custom/image-grid.js), please let me know!
45 |
46 | ## Setup
47 |
48 | Note: **stimulus-image-grid requires StimulusJS v2.0+**
49 |
50 | *If you are reading this in the past* (Stimulus 2 isn't out yet) you can change your `stimulus` package in `package.json` to point to [this commit](https://github.com/stimulusjs/dev-builds/archive/b8cc8c4/stimulus.tar.gz).
51 |
52 | Add image-grid to your main JS entry point or Stimulus controllers root folder:
53 |
54 | ```js
55 | import { Application } from 'stimulus'
56 | import ImageGrid from 'stimulus-image-grid'
57 |
58 | import { definitionsFromContext } from 'stimulus/webpack-helpers'
59 | const application = Application.start()
60 | const context = require.context('../controllers', true, /\.js$/)
61 | application.load(definitionsFromContext(context))
62 |
63 | // Manually register ImageGrid as a Stimulus controller
64 | application.register('image-grid', ImageGrid)
65 | ```
66 |
67 | ## HTML Markup
68 |
69 | For the image grid to work properly, it needs the raw image dimensions. If you know the dimensions at render time, set the `data-width` and `data-height` attributes. Otherwise, the library will calculate the size when it loads. This is slower and could cause a flicker, but it will only happen once - even across Turbolinks visits.
70 |
71 | ```html
72 |
73 |

74 |

75 |

76 |

77 |

78 |
79 | ```
80 |
81 | The library tries really hard to not be opinionated about HTML structure. The basic idea is that every child of the container element that has the image-grid controller declared upon it will have [zero or one] image(s), somewhere in its DOM hierarchy. So for example, you could have a scenario where images are wrapped in DIV tags and it will find [zero or one] image(s) in the hierarchy:
82 |
83 | ```html
84 |
85 |
86 |

87 |
88 |
89 |

90 |
91 |
92 |

93 |
94 |
95 |

96 |
97 |
98 |

99 |
100 |
101 | ```
102 |
103 | This library is fully responsive in that it will automatically re-flow the images to the ideal layout in real-time as the container it lives in changes size. If you're using a CSS library such as Bootstrap, this is usually managed with the [responsive breakpoint classes](https://getbootstrap.com/docs/4.4/layout/grid/#grid-options).
104 |
105 | ## Optional Parameters
106 |
107 | There are only three configurable properties, all of which are set on the DOM element using data attributes:
108 |
109 | Property | Default Value
110 | -------- | -------------
111 | padding | 10
112 | targetHeight | 150
113 | display | inline-block
114 |
115 | ```html
116 |
121 | ...
122 |
123 | ```
124 |
125 | Padding is applied to the bottom of each image as well as the right edge of each image *that isn't the right-most image of its row*. Target height is applied to the row and you can tweak this value to suit the look of your application.
126 |
127 | ### Obtaining a reference to the Stimulus controller instance
128 |
129 | When you place an image-grid controller on a DOM element, it hangs a variable on the element called `imageGrid` which allows you to access the internal state of the controller.
130 |
131 | The only method you should ever call directly is `processImages()`, which shouldn't be necessary but I'm not a fucking oracle so I've got your back. Here's an example of forcing a grid re-flow on an element with the id `grid`:
132 |
133 | ```javascript
134 | document.getElementById('grid').imageGrid.processImages()
135 | ```
136 |
137 | ## Contributing
138 |
139 | Bug reports and pull requests are welcome.
140 |
141 | ## License
142 |
143 | This package is available as open source under the terms of the MIT License.
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var stimulus = require('stimulus');
4 |
5 | function _classCallCheck(instance, Constructor) {
6 | if (!(instance instanceof Constructor)) {
7 | throw new TypeError("Cannot call a class as a function");
8 | }
9 | }
10 |
11 | function _defineProperties(target, props) {
12 | for (var i = 0; i < props.length; i++) {
13 | var descriptor = props[i];
14 | descriptor.enumerable = descriptor.enumerable || false;
15 | descriptor.configurable = true;
16 | if ("value" in descriptor) descriptor.writable = true;
17 | Object.defineProperty(target, descriptor.key, descriptor);
18 | }
19 | }
20 |
21 | function _createClass(Constructor, protoProps, staticProps) {
22 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
23 | if (staticProps) _defineProperties(Constructor, staticProps);
24 | return Constructor;
25 | }
26 |
27 | function _defineProperty(obj, key, value) {
28 | if (key in obj) {
29 | Object.defineProperty(obj, key, {
30 | value: value,
31 | enumerable: true,
32 | configurable: true,
33 | writable: true
34 | });
35 | } else {
36 | obj[key] = value;
37 | }
38 |
39 | return obj;
40 | }
41 |
42 | function _inherits(subClass, superClass) {
43 | if (typeof superClass !== "function" && superClass !== null) {
44 | throw new TypeError("Super expression must either be null or a function");
45 | }
46 |
47 | subClass.prototype = Object.create(superClass && superClass.prototype, {
48 | constructor: {
49 | value: subClass,
50 | writable: true,
51 | configurable: true
52 | }
53 | });
54 | if (superClass) _setPrototypeOf(subClass, superClass);
55 | }
56 |
57 | function _getPrototypeOf(o) {
58 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
59 | return o.__proto__ || Object.getPrototypeOf(o);
60 | };
61 | return _getPrototypeOf(o);
62 | }
63 |
64 | function _setPrototypeOf(o, p) {
65 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
66 | o.__proto__ = p;
67 | return o;
68 | };
69 |
70 | return _setPrototypeOf(o, p);
71 | }
72 |
73 | function _isNativeReflectConstruct() {
74 | if (typeof Reflect === "undefined" || !Reflect.construct) return false;
75 | if (Reflect.construct.sham) return false;
76 | if (typeof Proxy === "function") return true;
77 |
78 | try {
79 | Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
80 | return true;
81 | } catch (e) {
82 | return false;
83 | }
84 | }
85 |
86 | function _assertThisInitialized(self) {
87 | if (self === void 0) {
88 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
89 | }
90 |
91 | return self;
92 | }
93 |
94 | function _possibleConstructorReturn(self, call) {
95 | if (call && (typeof call === "object" || typeof call === "function")) {
96 | return call;
97 | }
98 |
99 | return _assertThisInitialized(self);
100 | }
101 |
102 | function _createSuper(Derived) {
103 | var hasNativeReflectConstruct = _isNativeReflectConstruct();
104 |
105 | return function () {
106 | var Super = _getPrototypeOf(Derived),
107 | result;
108 |
109 | if (hasNativeReflectConstruct) {
110 | var NewTarget = _getPrototypeOf(this).constructor;
111 |
112 | result = Reflect.construct(Super, arguments, NewTarget);
113 | } else {
114 | result = Super.apply(this, arguments);
115 | }
116 |
117 | return _possibleConstructorReturn(this, result);
118 | };
119 | }
120 |
121 | let _default = /*#__PURE__*/function (_Controller) {
122 | _inherits(_default, _Controller);
123 |
124 | var _super = _createSuper(_default);
125 |
126 | function _default() {
127 | _classCallCheck(this, _default);
128 |
129 | return _super.apply(this, arguments);
130 | }
131 |
132 | _createClass(_default, [{
133 | key: "initialize",
134 | value: function initialize() {
135 | this.element['imageGrid'] = this;
136 | Array.prototype.filter.call(this.element.childNodes, node => node.nodeType == 3 && !/\S/.test(node.nodeValue)).forEach(node => node.remove());
137 | if (!this.hasPaddingValue) this.paddingValue = 10;
138 | if (!this.hasTargetHeightValue) this.targetHeightValue = 150;
139 | if (!this.hasDisplayValue) this.displayValue = 'inline-block';
140 | this.resizeObserver = new ResizeObserver(this.observed.bind(this));
141 | }
142 | }, {
143 | key: "observed",
144 | value: function observed(elements) {
145 | this.albumWidth = elements[0].contentRect.width;
146 | this.processImages();
147 | }
148 | }, {
149 | key: "connect",
150 | value: function connect() {
151 | this.resizeObserver.observe(this.element);
152 | }
153 | }, {
154 | key: "disconnect",
155 | value: function disconnect() {
156 | this.resizeObserver.unobserve(this.element);
157 | }
158 | }, {
159 | key: "processImages",
160 | value: function processImages() {
161 | let row = 0;
162 | this.elements = [];
163 | this.images = Array.from(this.element.children);
164 | this.images.forEach((ele, index) => {
165 | const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img');
166 | let width, height;
167 |
168 | if ('width' in image.dataset && 'height' in image.dataset) {
169 | width = image.dataset.width;
170 | height = image.dataset.height;
171 | } else {
172 | const comp = window.getComputedStyle(image);
173 | width = parseFloat(comp.getPropertyValue('width').slice(0, -2));
174 | height = parseFloat(comp.getPropertyValue('height').slice(0, -2));
175 | image.dataset.width = width;
176 | image.dataset.height = height;
177 | }
178 |
179 | const idealW = Math.ceil(width / height * this.targetHeightValue);
180 | const idealH = Math.ceil(this.targetHeightValue);
181 | this.elements.push([ele, idealW, idealH]);
182 | row += idealW + this.paddingValue;
183 |
184 | if (row > this.albumWidth && this.elements.length) {
185 | this.resizeRow(row - this.paddingValue);
186 | row = 0;
187 | this.elements = [];
188 | }
189 |
190 | if (this.images.length - 1 == index && this.elements.length) {
191 | this.resizeRow(row);
192 | row = 0;
193 | this.elements = [];
194 | }
195 | }, this);
196 | }
197 | }, {
198 | key: "resizeRow",
199 | value: function resizeRow(row) {
200 | const imageExtras = this.paddingValue * (this.elements.length - 1);
201 | const albumWidthAdjusted = this.albumWidth - imageExtras;
202 | const overPercent = albumWidthAdjusted / (row - imageExtras);
203 | let trackWidth = imageExtras;
204 | this.elements.forEach((element, index) => {
205 | const [ele, idealW, idealH] = element;
206 | let fw = Math.floor(idealW * overPercent);
207 | let fh = Math.floor(idealH * overPercent);
208 | const isNotLast = index < this.elements.length - 1;
209 | trackWidth += fw;
210 | if (!isNotLast && trackWidth < this.albumWidth) fw += this.albumWidth - trackWidth;
211 | fw--;
212 | const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img');
213 | image.style.width = fw + 'px';
214 | image.style.height = fh + 'px';
215 | ele.style.marginBottom = this.paddingValue + 'px';
216 | ele.style.marginRight = isNotLast ? this.paddingValue + 'px' : 0;
217 | ele.style.display = this.displayValue;
218 | ele.style.verticalAlign = 'bottom';
219 | }, this);
220 | }
221 | }]);
222 |
223 | return _default;
224 | }(stimulus.Controller);
225 |
226 | _defineProperty(_default, "values", {
227 | padding: Number,
228 | targetHeight: Number,
229 | display: String
230 | });
231 |
232 | module.exports = _default;
233 | //# sourceMappingURL=index.js.map
234 |
--------------------------------------------------------------------------------
/dist/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sources":["../src/index.js"],"sourcesContent":["import { Controller } from 'stimulus'\r\n\r\nexport default class extends Controller {\r\n static values = {\r\n padding: Number,\r\n targetHeight: Number,\r\n display: String\r\n }\r\n\r\n initialize () {\r\n this.element['imageGrid'] = this\r\n Array.prototype.filter\r\n .call(\r\n this.element.childNodes,\r\n node => node.nodeType == 3 && !/\\S/.test(node.nodeValue)\r\n )\r\n .forEach(node => node.remove())\r\n if (!this.hasPaddingValue) this.paddingValue = 10\r\n if (!this.hasTargetHeightValue) this.targetHeightValue = 150\r\n if (!this.hasDisplayValue) this.displayValue = 'inline-block'\r\n this.resizeObserver = new ResizeObserver(this.observed.bind(this))\r\n }\r\n\r\n observed (elements) {\r\n this.albumWidth = elements[0].contentRect.width\r\n this.processImages()\r\n }\r\n\r\n connect () {\r\n this.resizeObserver.observe(this.element)\r\n }\r\n\r\n disconnect () {\r\n this.resizeObserver.unobserve(this.element)\r\n }\r\n\r\n processImages () {\r\n let row = 0\r\n this.elements = []\r\n this.images = Array.from(this.element.children)\r\n this.images.forEach((ele, index) => {\r\n const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img')\r\n let width, height\r\n if ('width' in image.dataset && 'height' in image.dataset) {\r\n width = image.dataset.width\r\n height = image.dataset.height\r\n } else {\r\n const comp = window.getComputedStyle(image)\r\n width = parseFloat(comp.getPropertyValue('width').slice(0, -2))\r\n height = parseFloat(comp.getPropertyValue('height').slice(0, -2))\r\n image.dataset.width = width\r\n image.dataset.height = height\r\n }\r\n const idealW = Math.ceil((width / height) * this.targetHeightValue)\r\n const idealH = Math.ceil(this.targetHeightValue)\r\n this.elements.push([ele, idealW, idealH])\r\n row += idealW + this.paddingValue\r\n if (row > this.albumWidth && this.elements.length) {\r\n this.resizeRow(row - this.paddingValue)\r\n row = 0\r\n this.elements = []\r\n }\r\n if (this.images.length - 1 == index && this.elements.length) {\r\n this.resizeRow(row)\r\n row = 0\r\n this.elements = []\r\n }\r\n }, this)\r\n }\r\n\r\n resizeRow (row) {\r\n const imageExtras = this.paddingValue * (this.elements.length - 1)\r\n const albumWidthAdjusted = this.albumWidth - imageExtras\r\n const overPercent = albumWidthAdjusted / (row - imageExtras)\r\n let trackWidth = imageExtras\r\n this.elements.forEach((element, index) => {\r\n const [ele, idealW, idealH] = element\r\n let fw = Math.floor(idealW * overPercent)\r\n let fh = Math.floor(idealH * overPercent)\r\n const isNotLast = index < this.elements.length - 1\r\n trackWidth += fw\r\n if (!isNotLast && trackWidth < this.albumWidth)\r\n fw += this.albumWidth - trackWidth\r\n fw--\r\n const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img')\r\n image.style.width = fw + 'px'\r\n image.style.height = fh + 'px'\r\n ele.style.marginBottom = this.paddingValue + 'px'\r\n ele.style.marginRight = isNotLast ? this.paddingValue + 'px' : 0\r\n ele.style.display = this.displayValue\r\n ele.style.verticalAlign = 'bottom'\r\n }, this)\r\n }\r\n}\r\n"],"names":["element","Array","prototype","filter","call","childNodes","node","nodeType","test","nodeValue","forEach","remove","hasPaddingValue","paddingValue","hasTargetHeightValue","targetHeightValue","hasDisplayValue","displayValue","resizeObserver","ResizeObserver","observed","bind","elements","albumWidth","contentRect","width","processImages","observe","unobserve","row","images","from","children","ele","index","image","nodeName","querySelector","height","dataset","comp","window","getComputedStyle","parseFloat","getPropertyValue","slice","idealW","Math","ceil","idealH","push","length","resizeRow","imageExtras","albumWidthAdjusted","overPercent","trackWidth","fw","floor","fh","isNotLast","style","marginBottom","marginRight","display","verticalAlign","Controller","padding","Number","targetHeight","String"],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/index.m.js:
--------------------------------------------------------------------------------
1 | import { Controller } from 'stimulus';
2 |
3 | function _classCallCheck(instance, Constructor) {
4 | if (!(instance instanceof Constructor)) {
5 | throw new TypeError("Cannot call a class as a function");
6 | }
7 | }
8 |
9 | function _defineProperties(target, props) {
10 | for (var i = 0; i < props.length; i++) {
11 | var descriptor = props[i];
12 | descriptor.enumerable = descriptor.enumerable || false;
13 | descriptor.configurable = true;
14 | if ("value" in descriptor) descriptor.writable = true;
15 | Object.defineProperty(target, descriptor.key, descriptor);
16 | }
17 | }
18 |
19 | function _createClass(Constructor, protoProps, staticProps) {
20 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
21 | if (staticProps) _defineProperties(Constructor, staticProps);
22 | return Constructor;
23 | }
24 |
25 | function _defineProperty(obj, key, value) {
26 | if (key in obj) {
27 | Object.defineProperty(obj, key, {
28 | value: value,
29 | enumerable: true,
30 | configurable: true,
31 | writable: true
32 | });
33 | } else {
34 | obj[key] = value;
35 | }
36 |
37 | return obj;
38 | }
39 |
40 | function _inherits(subClass, superClass) {
41 | if (typeof superClass !== "function" && superClass !== null) {
42 | throw new TypeError("Super expression must either be null or a function");
43 | }
44 |
45 | subClass.prototype = Object.create(superClass && superClass.prototype, {
46 | constructor: {
47 | value: subClass,
48 | writable: true,
49 | configurable: true
50 | }
51 | });
52 | if (superClass) _setPrototypeOf(subClass, superClass);
53 | }
54 |
55 | function _getPrototypeOf(o) {
56 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
57 | return o.__proto__ || Object.getPrototypeOf(o);
58 | };
59 | return _getPrototypeOf(o);
60 | }
61 |
62 | function _setPrototypeOf(o, p) {
63 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
64 | o.__proto__ = p;
65 | return o;
66 | };
67 |
68 | return _setPrototypeOf(o, p);
69 | }
70 |
71 | function _isNativeReflectConstruct() {
72 | if (typeof Reflect === "undefined" || !Reflect.construct) return false;
73 | if (Reflect.construct.sham) return false;
74 | if (typeof Proxy === "function") return true;
75 |
76 | try {
77 | Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
78 | return true;
79 | } catch (e) {
80 | return false;
81 | }
82 | }
83 |
84 | function _assertThisInitialized(self) {
85 | if (self === void 0) {
86 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
87 | }
88 |
89 | return self;
90 | }
91 |
92 | function _possibleConstructorReturn(self, call) {
93 | if (call && (typeof call === "object" || typeof call === "function")) {
94 | return call;
95 | }
96 |
97 | return _assertThisInitialized(self);
98 | }
99 |
100 | function _createSuper(Derived) {
101 | var hasNativeReflectConstruct = _isNativeReflectConstruct();
102 |
103 | return function () {
104 | var Super = _getPrototypeOf(Derived),
105 | result;
106 |
107 | if (hasNativeReflectConstruct) {
108 | var NewTarget = _getPrototypeOf(this).constructor;
109 |
110 | result = Reflect.construct(Super, arguments, NewTarget);
111 | } else {
112 | result = Super.apply(this, arguments);
113 | }
114 |
115 | return _possibleConstructorReturn(this, result);
116 | };
117 | }
118 |
119 | let _default = /*#__PURE__*/function (_Controller) {
120 | _inherits(_default, _Controller);
121 |
122 | var _super = _createSuper(_default);
123 |
124 | function _default() {
125 | _classCallCheck(this, _default);
126 |
127 | return _super.apply(this, arguments);
128 | }
129 |
130 | _createClass(_default, [{
131 | key: "initialize",
132 | value: function initialize() {
133 | this.element['imageGrid'] = this;
134 | Array.prototype.filter.call(this.element.childNodes, node => node.nodeType == 3 && !/\S/.test(node.nodeValue)).forEach(node => node.remove());
135 | if (!this.hasPaddingValue) this.paddingValue = 10;
136 | if (!this.hasTargetHeightValue) this.targetHeightValue = 150;
137 | if (!this.hasDisplayValue) this.displayValue = 'inline-block';
138 | this.resizeObserver = new ResizeObserver(this.observed.bind(this));
139 | }
140 | }, {
141 | key: "observed",
142 | value: function observed(elements) {
143 | this.albumWidth = elements[0].contentRect.width;
144 | this.processImages();
145 | }
146 | }, {
147 | key: "connect",
148 | value: function connect() {
149 | this.resizeObserver.observe(this.element);
150 | }
151 | }, {
152 | key: "disconnect",
153 | value: function disconnect() {
154 | this.resizeObserver.unobserve(this.element);
155 | }
156 | }, {
157 | key: "processImages",
158 | value: function processImages() {
159 | let row = 0;
160 | this.elements = [];
161 | this.images = Array.from(this.element.children);
162 | this.images.forEach((ele, index) => {
163 | const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img');
164 | let width, height;
165 |
166 | if ('width' in image.dataset && 'height' in image.dataset) {
167 | width = image.dataset.width;
168 | height = image.dataset.height;
169 | } else {
170 | const comp = window.getComputedStyle(image);
171 | width = parseFloat(comp.getPropertyValue('width').slice(0, -2));
172 | height = parseFloat(comp.getPropertyValue('height').slice(0, -2));
173 | image.dataset.width = width;
174 | image.dataset.height = height;
175 | }
176 |
177 | const idealW = Math.ceil(width / height * this.targetHeightValue);
178 | const idealH = Math.ceil(this.targetHeightValue);
179 | this.elements.push([ele, idealW, idealH]);
180 | row += idealW + this.paddingValue;
181 |
182 | if (row > this.albumWidth && this.elements.length) {
183 | this.resizeRow(row - this.paddingValue);
184 | row = 0;
185 | this.elements = [];
186 | }
187 |
188 | if (this.images.length - 1 == index && this.elements.length) {
189 | this.resizeRow(row);
190 | row = 0;
191 | this.elements = [];
192 | }
193 | }, this);
194 | }
195 | }, {
196 | key: "resizeRow",
197 | value: function resizeRow(row) {
198 | const imageExtras = this.paddingValue * (this.elements.length - 1);
199 | const albumWidthAdjusted = this.albumWidth - imageExtras;
200 | const overPercent = albumWidthAdjusted / (row - imageExtras);
201 | let trackWidth = imageExtras;
202 | this.elements.forEach((element, index) => {
203 | const [ele, idealW, idealH] = element;
204 | let fw = Math.floor(idealW * overPercent);
205 | let fh = Math.floor(idealH * overPercent);
206 | const isNotLast = index < this.elements.length - 1;
207 | trackWidth += fw;
208 | if (!isNotLast && trackWidth < this.albumWidth) fw += this.albumWidth - trackWidth;
209 | fw--;
210 | const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img');
211 | image.style.width = fw + 'px';
212 | image.style.height = fh + 'px';
213 | ele.style.marginBottom = this.paddingValue + 'px';
214 | ele.style.marginRight = isNotLast ? this.paddingValue + 'px' : 0;
215 | ele.style.display = this.displayValue;
216 | ele.style.verticalAlign = 'bottom';
217 | }, this);
218 | }
219 | }]);
220 |
221 | return _default;
222 | }(Controller);
223 |
224 | _defineProperty(_default, "values", {
225 | padding: Number,
226 | targetHeight: Number,
227 | display: String
228 | });
229 |
230 | export default _default;
231 | //# sourceMappingURL=index.m.js.map
232 |
--------------------------------------------------------------------------------
/dist/index.m.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.m.js","sources":["../src/index.js"],"sourcesContent":["import { Controller } from 'stimulus'\r\n\r\nexport default class extends Controller {\r\n static values = {\r\n padding: Number,\r\n targetHeight: Number,\r\n display: String\r\n }\r\n\r\n initialize () {\r\n this.element['imageGrid'] = this\r\n Array.prototype.filter\r\n .call(\r\n this.element.childNodes,\r\n node => node.nodeType == 3 && !/\\S/.test(node.nodeValue)\r\n )\r\n .forEach(node => node.remove())\r\n if (!this.hasPaddingValue) this.paddingValue = 10\r\n if (!this.hasTargetHeightValue) this.targetHeightValue = 150\r\n if (!this.hasDisplayValue) this.displayValue = 'inline-block'\r\n this.resizeObserver = new ResizeObserver(this.observed.bind(this))\r\n }\r\n\r\n observed (elements) {\r\n this.albumWidth = elements[0].contentRect.width\r\n this.processImages()\r\n }\r\n\r\n connect () {\r\n this.resizeObserver.observe(this.element)\r\n }\r\n\r\n disconnect () {\r\n this.resizeObserver.unobserve(this.element)\r\n }\r\n\r\n processImages () {\r\n let row = 0\r\n this.elements = []\r\n this.images = Array.from(this.element.children)\r\n this.images.forEach((ele, index) => {\r\n const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img')\r\n let width, height\r\n if ('width' in image.dataset && 'height' in image.dataset) {\r\n width = image.dataset.width\r\n height = image.dataset.height\r\n } else {\r\n const comp = window.getComputedStyle(image)\r\n width = parseFloat(comp.getPropertyValue('width').slice(0, -2))\r\n height = parseFloat(comp.getPropertyValue('height').slice(0, -2))\r\n image.dataset.width = width\r\n image.dataset.height = height\r\n }\r\n const idealW = Math.ceil((width / height) * this.targetHeightValue)\r\n const idealH = Math.ceil(this.targetHeightValue)\r\n this.elements.push([ele, idealW, idealH])\r\n row += idealW + this.paddingValue\r\n if (row > this.albumWidth && this.elements.length) {\r\n this.resizeRow(row - this.paddingValue)\r\n row = 0\r\n this.elements = []\r\n }\r\n if (this.images.length - 1 == index && this.elements.length) {\r\n this.resizeRow(row)\r\n row = 0\r\n this.elements = []\r\n }\r\n }, this)\r\n }\r\n\r\n resizeRow (row) {\r\n const imageExtras = this.paddingValue * (this.elements.length - 1)\r\n const albumWidthAdjusted = this.albumWidth - imageExtras\r\n const overPercent = albumWidthAdjusted / (row - imageExtras)\r\n let trackWidth = imageExtras\r\n this.elements.forEach((element, index) => {\r\n const [ele, idealW, idealH] = element\r\n let fw = Math.floor(idealW * overPercent)\r\n let fh = Math.floor(idealH * overPercent)\r\n const isNotLast = index < this.elements.length - 1\r\n trackWidth += fw\r\n if (!isNotLast && trackWidth < this.albumWidth)\r\n fw += this.albumWidth - trackWidth\r\n fw--\r\n const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img')\r\n image.style.width = fw + 'px'\r\n image.style.height = fh + 'px'\r\n ele.style.marginBottom = this.paddingValue + 'px'\r\n ele.style.marginRight = isNotLast ? this.paddingValue + 'px' : 0\r\n ele.style.display = this.displayValue\r\n ele.style.verticalAlign = 'bottom'\r\n }, this)\r\n }\r\n}\r\n"],"names":["element","Array","prototype","filter","call","childNodes","node","nodeType","test","nodeValue","forEach","remove","hasPaddingValue","paddingValue","hasTargetHeightValue","targetHeightValue","hasDisplayValue","displayValue","resizeObserver","ResizeObserver","observed","bind","elements","albumWidth","contentRect","width","processImages","observe","unobserve","row","images","from","children","ele","index","image","nodeName","querySelector","height","dataset","comp","window","getComputedStyle","parseFloat","getPropertyValue","slice","idealW","Math","ceil","idealH","push","length","resizeRow","imageExtras","albumWidthAdjusted","overPercent","trackWidth","fw","floor","fh","isNotLast","style","marginBottom","marginRight","display","verticalAlign","Controller","padding","Number","targetHeight","String"],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/index.umd.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('stimulus')) :
3 | typeof define === 'function' && define.amd ? define(['stimulus'], factory) :
4 | (global = global || self, global['stimulus-image-grid'] = factory(global.Stimulus));
5 | }(this, (function (stimulus) { 'use strict';
6 |
7 | function _classCallCheck(instance, Constructor) {
8 | if (!(instance instanceof Constructor)) {
9 | throw new TypeError("Cannot call a class as a function");
10 | }
11 | }
12 |
13 | function _defineProperties(target, props) {
14 | for (var i = 0; i < props.length; i++) {
15 | var descriptor = props[i];
16 | descriptor.enumerable = descriptor.enumerable || false;
17 | descriptor.configurable = true;
18 | if ("value" in descriptor) descriptor.writable = true;
19 | Object.defineProperty(target, descriptor.key, descriptor);
20 | }
21 | }
22 |
23 | function _createClass(Constructor, protoProps, staticProps) {
24 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
25 | if (staticProps) _defineProperties(Constructor, staticProps);
26 | return Constructor;
27 | }
28 |
29 | function _defineProperty(obj, key, value) {
30 | if (key in obj) {
31 | Object.defineProperty(obj, key, {
32 | value: value,
33 | enumerable: true,
34 | configurable: true,
35 | writable: true
36 | });
37 | } else {
38 | obj[key] = value;
39 | }
40 |
41 | return obj;
42 | }
43 |
44 | function _inherits(subClass, superClass) {
45 | if (typeof superClass !== "function" && superClass !== null) {
46 | throw new TypeError("Super expression must either be null or a function");
47 | }
48 |
49 | subClass.prototype = Object.create(superClass && superClass.prototype, {
50 | constructor: {
51 | value: subClass,
52 | writable: true,
53 | configurable: true
54 | }
55 | });
56 | if (superClass) _setPrototypeOf(subClass, superClass);
57 | }
58 |
59 | function _getPrototypeOf(o) {
60 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
61 | return o.__proto__ || Object.getPrototypeOf(o);
62 | };
63 | return _getPrototypeOf(o);
64 | }
65 |
66 | function _setPrototypeOf(o, p) {
67 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
68 | o.__proto__ = p;
69 | return o;
70 | };
71 |
72 | return _setPrototypeOf(o, p);
73 | }
74 |
75 | function _isNativeReflectConstruct() {
76 | if (typeof Reflect === "undefined" || !Reflect.construct) return false;
77 | if (Reflect.construct.sham) return false;
78 | if (typeof Proxy === "function") return true;
79 |
80 | try {
81 | Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
82 | return true;
83 | } catch (e) {
84 | return false;
85 | }
86 | }
87 |
88 | function _assertThisInitialized(self) {
89 | if (self === void 0) {
90 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
91 | }
92 |
93 | return self;
94 | }
95 |
96 | function _possibleConstructorReturn(self, call) {
97 | if (call && (typeof call === "object" || typeof call === "function")) {
98 | return call;
99 | }
100 |
101 | return _assertThisInitialized(self);
102 | }
103 |
104 | function _createSuper(Derived) {
105 | var hasNativeReflectConstruct = _isNativeReflectConstruct();
106 |
107 | return function () {
108 | var Super = _getPrototypeOf(Derived),
109 | result;
110 |
111 | if (hasNativeReflectConstruct) {
112 | var NewTarget = _getPrototypeOf(this).constructor;
113 |
114 | result = Reflect.construct(Super, arguments, NewTarget);
115 | } else {
116 | result = Super.apply(this, arguments);
117 | }
118 |
119 | return _possibleConstructorReturn(this, result);
120 | };
121 | }
122 |
123 | let _default = /*#__PURE__*/function (_Controller) {
124 | _inherits(_default, _Controller);
125 |
126 | var _super = _createSuper(_default);
127 |
128 | function _default() {
129 | _classCallCheck(this, _default);
130 |
131 | return _super.apply(this, arguments);
132 | }
133 |
134 | _createClass(_default, [{
135 | key: "initialize",
136 | value: function initialize() {
137 | this.element['imageGrid'] = this;
138 | Array.prototype.filter.call(this.element.childNodes, node => node.nodeType == 3 && !/\S/.test(node.nodeValue)).forEach(node => node.remove());
139 | if (!this.hasPaddingValue) this.paddingValue = 10;
140 | if (!this.hasTargetHeightValue) this.targetHeightValue = 150;
141 | if (!this.hasDisplayValue) this.displayValue = 'inline-block';
142 | this.resizeObserver = new ResizeObserver(this.observed.bind(this));
143 | }
144 | }, {
145 | key: "observed",
146 | value: function observed(elements) {
147 | this.albumWidth = elements[0].contentRect.width;
148 | this.processImages();
149 | }
150 | }, {
151 | key: "connect",
152 | value: function connect() {
153 | this.resizeObserver.observe(this.element);
154 | }
155 | }, {
156 | key: "disconnect",
157 | value: function disconnect() {
158 | this.resizeObserver.unobserve(this.element);
159 | }
160 | }, {
161 | key: "processImages",
162 | value: function processImages() {
163 | let row = 0;
164 | this.elements = [];
165 | this.images = Array.from(this.element.children);
166 | this.images.forEach((ele, index) => {
167 | const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img');
168 | let width, height;
169 |
170 | if ('width' in image.dataset && 'height' in image.dataset) {
171 | width = image.dataset.width;
172 | height = image.dataset.height;
173 | } else {
174 | const comp = window.getComputedStyle(image);
175 | width = parseFloat(comp.getPropertyValue('width').slice(0, -2));
176 | height = parseFloat(comp.getPropertyValue('height').slice(0, -2));
177 | image.dataset.width = width;
178 | image.dataset.height = height;
179 | }
180 |
181 | const idealW = Math.ceil(width / height * this.targetHeightValue);
182 | const idealH = Math.ceil(this.targetHeightValue);
183 | this.elements.push([ele, idealW, idealH]);
184 | row += idealW + this.paddingValue;
185 |
186 | if (row > this.albumWidth && this.elements.length) {
187 | this.resizeRow(row - this.paddingValue);
188 | row = 0;
189 | this.elements = [];
190 | }
191 |
192 | if (this.images.length - 1 == index && this.elements.length) {
193 | this.resizeRow(row);
194 | row = 0;
195 | this.elements = [];
196 | }
197 | }, this);
198 | }
199 | }, {
200 | key: "resizeRow",
201 | value: function resizeRow(row) {
202 | const imageExtras = this.paddingValue * (this.elements.length - 1);
203 | const albumWidthAdjusted = this.albumWidth - imageExtras;
204 | const overPercent = albumWidthAdjusted / (row - imageExtras);
205 | let trackWidth = imageExtras;
206 | this.elements.forEach((element, index) => {
207 | const [ele, idealW, idealH] = element;
208 | let fw = Math.floor(idealW * overPercent);
209 | let fh = Math.floor(idealH * overPercent);
210 | const isNotLast = index < this.elements.length - 1;
211 | trackWidth += fw;
212 | if (!isNotLast && trackWidth < this.albumWidth) fw += this.albumWidth - trackWidth;
213 | fw--;
214 | const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img');
215 | image.style.width = fw + 'px';
216 | image.style.height = fh + 'px';
217 | ele.style.marginBottom = this.paddingValue + 'px';
218 | ele.style.marginRight = isNotLast ? this.paddingValue + 'px' : 0;
219 | ele.style.display = this.displayValue;
220 | ele.style.verticalAlign = 'bottom';
221 | }, this);
222 | }
223 | }]);
224 |
225 | return _default;
226 | }(stimulus.Controller);
227 |
228 | _defineProperty(_default, "values", {
229 | padding: Number,
230 | targetHeight: Number,
231 | display: String
232 | });
233 |
234 | return _default;
235 |
236 | })));
237 | //# sourceMappingURL=index.umd.js.map
238 |
--------------------------------------------------------------------------------
/dist/index.umd.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.umd.js","sources":["../src/index.js"],"sourcesContent":["import { Controller } from 'stimulus'\r\n\r\nexport default class extends Controller {\r\n static values = {\r\n padding: Number,\r\n targetHeight: Number,\r\n display: String\r\n }\r\n\r\n initialize () {\r\n this.element['imageGrid'] = this\r\n Array.prototype.filter\r\n .call(\r\n this.element.childNodes,\r\n node => node.nodeType == 3 && !/\\S/.test(node.nodeValue)\r\n )\r\n .forEach(node => node.remove())\r\n if (!this.hasPaddingValue) this.paddingValue = 10\r\n if (!this.hasTargetHeightValue) this.targetHeightValue = 150\r\n if (!this.hasDisplayValue) this.displayValue = 'inline-block'\r\n this.resizeObserver = new ResizeObserver(this.observed.bind(this))\r\n }\r\n\r\n observed (elements) {\r\n this.albumWidth = elements[0].contentRect.width\r\n this.processImages()\r\n }\r\n\r\n connect () {\r\n this.resizeObserver.observe(this.element)\r\n }\r\n\r\n disconnect () {\r\n this.resizeObserver.unobserve(this.element)\r\n }\r\n\r\n processImages () {\r\n let row = 0\r\n this.elements = []\r\n this.images = Array.from(this.element.children)\r\n this.images.forEach((ele, index) => {\r\n const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img')\r\n let width, height\r\n if ('width' in image.dataset && 'height' in image.dataset) {\r\n width = image.dataset.width\r\n height = image.dataset.height\r\n } else {\r\n const comp = window.getComputedStyle(image)\r\n width = parseFloat(comp.getPropertyValue('width').slice(0, -2))\r\n height = parseFloat(comp.getPropertyValue('height').slice(0, -2))\r\n image.dataset.width = width\r\n image.dataset.height = height\r\n }\r\n const idealW = Math.ceil((width / height) * this.targetHeightValue)\r\n const idealH = Math.ceil(this.targetHeightValue)\r\n this.elements.push([ele, idealW, idealH])\r\n row += idealW + this.paddingValue\r\n if (row > this.albumWidth && this.elements.length) {\r\n this.resizeRow(row - this.paddingValue)\r\n row = 0\r\n this.elements = []\r\n }\r\n if (this.images.length - 1 == index && this.elements.length) {\r\n this.resizeRow(row)\r\n row = 0\r\n this.elements = []\r\n }\r\n }, this)\r\n }\r\n\r\n resizeRow (row) {\r\n const imageExtras = this.paddingValue * (this.elements.length - 1)\r\n const albumWidthAdjusted = this.albumWidth - imageExtras\r\n const overPercent = albumWidthAdjusted / (row - imageExtras)\r\n let trackWidth = imageExtras\r\n this.elements.forEach((element, index) => {\r\n const [ele, idealW, idealH] = element\r\n let fw = Math.floor(idealW * overPercent)\r\n let fh = Math.floor(idealH * overPercent)\r\n const isNotLast = index < this.elements.length - 1\r\n trackWidth += fw\r\n if (!isNotLast && trackWidth < this.albumWidth)\r\n fw += this.albumWidth - trackWidth\r\n fw--\r\n const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img')\r\n image.style.width = fw + 'px'\r\n image.style.height = fh + 'px'\r\n ele.style.marginBottom = this.paddingValue + 'px'\r\n ele.style.marginRight = isNotLast ? this.paddingValue + 'px' : 0\r\n ele.style.display = this.displayValue\r\n ele.style.verticalAlign = 'bottom'\r\n }, this)\r\n }\r\n}\r\n"],"names":["element","Array","prototype","filter","call","childNodes","node","nodeType","test","nodeValue","forEach","remove","hasPaddingValue","paddingValue","hasTargetHeightValue","targetHeightValue","hasDisplayValue","displayValue","resizeObserver","ResizeObserver","observed","bind","elements","albumWidth","contentRect","width","processImages","observe","unobserve","row","images","from","children","ele","index","image","nodeName","querySelector","height","dataset","comp","window","getComputedStyle","parseFloat","getPropertyValue","slice","idealW","Math","ceil","idealH","push","length","resizeRow","imageExtras","albumWidthAdjusted","overPercent","trackWidth","fw","floor","fh","isNotLast","style","marginBottom","marginRight","display","verticalAlign","Controller","padding","Number","targetHeight","String"],"mappings":"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"}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stimulus-image-grid",
3 | "version": "1.0.3",
4 | "description": "A Stimulus controller for beautiful responsive image grids",
5 | "keywords": [
6 | "stimulus",
7 | "stimulusjs",
8 | "bootstrap",
9 | "image",
10 | "grid",
11 | "responsive"
12 | ],
13 | "main": "dist/index.js",
14 | "umd:main": "dist/index.umd.js",
15 | "module": "dist/index.m.js",
16 | "source": "src/index.js",
17 | "author": "@leastbad",
18 | "license": "MIT",
19 | "external": "stimulus",
20 | "scripts": {
21 | "postinstall": "node scripts/post_install.js",
22 | "prettier-standard:check": "yarn run prettier-standard --check *.js **/*.js",
23 | "prettier-standard:format": "yarn run prettier-standard *.js **/*.js",
24 | "build": "rollup -c",
25 | "dev": "rollup -wc",
26 | "release": "np"
27 | },
28 | "homepage": "https://leastbad.com/",
29 | "bugs": {
30 | "url": "https://github.com/leastbad/stimulus-image-grid/issues"
31 | },
32 | "dependencies": {
33 | "stimulus": ">=2.0.0"
34 | },
35 | "devDependencies": {
36 | "@babel/core": "^7.6.2",
37 | "@babel/plugin-proposal-class-properties": "^7.3.4",
38 | "@babel/plugin-proposal-object-rest-spread": "^7.5.5",
39 | "@babel/plugin-transform-classes": "^7.3.4",
40 | "@babel/plugin-transform-spread": "^7.2.2",
41 | "@babel/preset-env": "^7.6.2",
42 | "np": "^5.1.3",
43 | "prettier-standard": "^16.1.0",
44 | "rollup": "^1.20.3",
45 | "rollup-plugin-babel": "^4.3.2",
46 | "rollup-plugin-filesize": "^6.0.1",
47 | "rollup-plugin-node-resolve": "^5.2.0"
48 | },
49 | "repository": {
50 | "type": "git",
51 | "url": "https://github.com/leastbad/stimulus-image-grid.git"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import filesize from 'rollup-plugin-filesize'
2 | import resolve from 'rollup-plugin-node-resolve'
3 | import babel from 'rollup-plugin-babel'
4 |
5 | const pkg = require('./package.json')
6 |
7 | const name = pkg.name
8 |
9 | export default {
10 | input: 'src/index.js',
11 | external: ['stimulus'],
12 | output: [
13 | {
14 | file: 'dist/index.js',
15 | format: 'cjs',
16 | sourcemap: true
17 | },
18 | {
19 | file: 'dist/index.m.js',
20 | format: 'es',
21 | sourcemap: true
22 | },
23 | {
24 | file: 'dist/index.umd.js',
25 | format: 'umd',
26 | name,
27 | sourcemap: true,
28 | globals: {
29 | stimulus: 'Stimulus'
30 | }
31 | }
32 | ],
33 | plugins: [resolve(), babel(), filesize()]
34 | }
35 |
--------------------------------------------------------------------------------
/scripts/post_install.js:
--------------------------------------------------------------------------------
1 | console.log(
2 | 'Registering a Stimulus controller for use is easy!\n\n' +
3 | '1. Open index.js in your controllers folder.\n' +
4 | '2. Add the following import:\n' +
5 | " import ImageGrid from 'stimulus-image-grid'\n" +
6 | '3. Register the image-grid controller at the bottom:\n' +
7 | " application.register('image-grid', ImageGrid)\n"
8 | )
9 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { Controller } from 'stimulus'
2 |
3 | export default class extends Controller {
4 | static values = {
5 | padding: Number,
6 | targetHeight: Number,
7 | display: String
8 | }
9 |
10 | initialize () {
11 | this.element['imageGrid'] = this
12 | Array.prototype.filter
13 | .call(
14 | this.element.childNodes,
15 | node => node.nodeType == 3 && !/\S/.test(node.nodeValue)
16 | )
17 | .forEach(node => node.remove())
18 | if (!this.hasPaddingValue) this.paddingValue = 10
19 | if (!this.hasTargetHeightValue) this.targetHeightValue = 150
20 | if (!this.hasDisplayValue) this.displayValue = 'inline-block'
21 | this.resizeObserver = new ResizeObserver(this.observed.bind(this))
22 | }
23 |
24 | observed (elements) {
25 | this.albumWidth = elements[0].contentRect.width
26 | this.processImages()
27 | }
28 |
29 | connect () {
30 | this.resizeObserver.observe(this.element)
31 | }
32 |
33 | disconnect () {
34 | this.resizeObserver.unobserve(this.element)
35 | }
36 |
37 | processImages () {
38 | let row = 0
39 | this.elements = []
40 | this.images = Array.from(this.element.children)
41 | this.images.forEach((ele, index) => {
42 | const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img')
43 | let width, height
44 | if ('width' in image.dataset && 'height' in image.dataset) {
45 | width = image.dataset.width
46 | height = image.dataset.height
47 | } else {
48 | const comp = window.getComputedStyle(image)
49 | width = parseFloat(comp.getPropertyValue('width').slice(0, -2))
50 | height = parseFloat(comp.getPropertyValue('height').slice(0, -2))
51 | image.dataset.width = width
52 | image.dataset.height = height
53 | }
54 | const idealW = Math.ceil((width / height) * this.targetHeightValue)
55 | const idealH = Math.ceil(this.targetHeightValue)
56 | this.elements.push([ele, idealW, idealH])
57 | row += idealW + this.paddingValue
58 | if (row > this.albumWidth && this.elements.length) {
59 | this.resizeRow(row - this.paddingValue)
60 | row = 0
61 | this.elements = []
62 | }
63 | if (this.images.length - 1 == index && this.elements.length) {
64 | this.resizeRow(row)
65 | row = 0
66 | this.elements = []
67 | }
68 | }, this)
69 | }
70 |
71 | resizeRow (row) {
72 | const imageExtras = this.paddingValue * (this.elements.length - 1)
73 | const albumWidthAdjusted = this.albumWidth - imageExtras
74 | const overPercent = albumWidthAdjusted / (row - imageExtras)
75 | let trackWidth = imageExtras
76 | this.elements.forEach((element, index) => {
77 | const [ele, idealW, idealH] = element
78 | let fw = Math.floor(idealW * overPercent)
79 | let fh = Math.floor(idealH * overPercent)
80 | const isNotLast = index < this.elements.length - 1
81 | trackWidth += fw
82 | if (!isNotLast && trackWidth < this.albumWidth)
83 | fw += this.albumWidth - trackWidth
84 | fw--
85 | const image = ele.nodeName === 'IMG' ? ele : ele.querySelector('img')
86 | image.style.width = fw + 'px'
87 | image.style.height = fh + 'px'
88 | ele.style.marginBottom = this.paddingValue + 'px'
89 | ele.style.marginRight = isNotLast ? this.paddingValue + 'px' : 0
90 | ele.style.display = this.displayValue
91 | ele.style.verticalAlign = 'bottom'
92 | }, this)
93 | }
94 | }
95 |
--------------------------------------------------------------------------------