fin-hypergrid
Advanced tools
Comparing version 3.1.0 to 3.2.0
@@ -15,2 +15,3 @@ /* eslint-env browser */ | ||
var _ = require('object-iterators'); | ||
var svgThemer = require('svg-themer'); | ||
@@ -76,16 +77,119 @@ var images = require('./images'); // this is the file generated by gulpfile.js (and ignored by git) | ||
/** | ||
* @name add | ||
* @method | ||
* @param {string} key | ||
* @param {string} name | ||
* @param {HTMLImageElement} img | ||
* @param {boolean} [themeable] - If truthy, the image will be themed by {@link module:images.setTheme images.setTheme}, called by {@link Hypergrid.applyTheme}. | ||
* If falsy, the image won't be themed until `images[name].themeable` is set to `true`. | ||
* In any case the remaining parameters are processed. | ||
* @param {function} [setSvgProps=svgThemer.setSvgProps] - Optional custom theming code for this image and the rules implied by `styles`. _If omitted, `styles` is promoted 2nd parameter position._ | ||
* @param {boolean|string[]} [styles] - Optional list style names with which to create CSS rules. | ||
* * If falsy (or omitted), no rules are created. | ||
* * Else if truthy but not an array, create a single rule: | ||
* ```css | ||
* `.hypergrid-background-image-name { background-image: url(...) }` | ||
* where _name_ is the value of the `name` parameter. | ||
* * Else if an array, create a CSS rule for each style named therein. | ||
* | ||
* For each rule thus created: | ||
* * Inserted into `style#injected-stylesheet-grid`. | ||
* * Selector is `.hypergrid-style-name` (where `style` is element value and `name` is image name). | ||
* (If a rule with that selector already exists, it is replaced.) | ||
* * Contains the named style with a value of `url(...)` where `...` is the image data. | ||
* Possible styles must be one of those listed in {*link https://github.com/joneit/svg-themer/blob/master/README.md#cssimagepropertynames svgThemer.cssImagePropertyNames} (which you can extend if needed). | ||
* * Will be automatically themed when the grid is themed (which is the whole point). | ||
* | ||
* @see {@link https://github.com/joneit/svg-themer} | ||
* @memberOf module:images | ||
*/ | ||
images.add = function(key, img) { | ||
return images[key] = img; | ||
}; | ||
function add(name, img, themeable, setSvgProps, styles) { | ||
if (/^data:image\/svg\+xml|\.svg/.test(img.src)) { | ||
img.themeable = !!themeable; | ||
if (typeof setSvgProps === 'object') { | ||
styles = setSvgProps; | ||
setSvgProps = undefined; | ||
} | ||
if (setSvgProps) { | ||
img.setSvgProps = setSvgProps; | ||
} | ||
if (styles) { | ||
img.themeableRules = createThemeableRules(name, img, setSvgProps, styles); | ||
} | ||
} | ||
return (images[name] = img); | ||
} | ||
function createThemeableRules(key, img, setSvgProps, styles) { | ||
// find or create stylesheet as needed | ||
var styleEl = document.querySelector('style#injected-stylesheet-themeables'); | ||
if (!styleEl) { | ||
styleEl = document.createElement('style'); | ||
styleEl.id = 'injected-stylesheet-themeables'; | ||
document.head.appendChild(styleEl); | ||
} | ||
var sheet = styleEl.sheet; | ||
return (styles.length ? styles : ['background-image']).reduce(function(rules, styleName) { | ||
var selectorText = '.hypergrid-' + styleName + '-' + key; | ||
// find and delete existing rule, if any | ||
var ruleIndex = Array.prototype.findIndex.call(sheet.cssRules, function(rule) { | ||
return rule.selectorText === selectorText; | ||
}); | ||
if (ruleIndex !== -1) { | ||
sheet.deleteRule(ruleIndex); | ||
} | ||
// create and insert new rule consisting of selector + style "collection" | ||
var ruleStyles = {}; | ||
// add image data style | ||
ruleStyles[styleName] = 'url(' + img.src + ')'; | ||
// add dimensions if known | ||
if (img.width) { ruleStyles.width = img.width + 'px'; } | ||
if (img.height) { ruleStyles.height = img.height + 'px'; } | ||
// combine the above styles into a semi-colon-separated "collection" | ||
var styleCollection = Object.keys(ruleStyles).map(function(key) { | ||
return key + ':' + ruleStyles[key]; | ||
}).join(';'); | ||
var ruleText = '{' + styleCollection + '}'; | ||
sheet.insertRule(selectorText + ruleText); | ||
var themeableRule = { | ||
rule: sheet.cssRules[0] | ||
}; | ||
if (setSvgProps) { | ||
themeableRule.setSvgProps = setSvgProps; | ||
} | ||
rules.push(themeableRule); | ||
return rules; | ||
}, []); | ||
} | ||
/** | ||
* @param {object} theme | ||
* @memberOf module:images | ||
*/ | ||
function setTheme(theme) { | ||
Object.keys(images).forEach(function(name) { | ||
var img = images[name]; | ||
if (img.themeable) { | ||
svgThemer.setImgSvgProps.call(img, theme, img.setSvgProps); | ||
} | ||
if (img.themeableRules) { | ||
img.themeableRules.forEach(function(themeable) { | ||
var selectorText = themeable.rule.selectorText; | ||
// extract style name using list of possible names | ||
var regex = new RegExp('^\.hypergrid-(' + svgThemer.cssImagePropertyNames.join('|') + ')-.*$'); | ||
var styleName = selectorText.replace(regex, '$1'); | ||
svgThemer.setRuleSvgProps.call(themeable.rule, theme, img.setSvgProps, styleName); | ||
}); | ||
} | ||
}); | ||
} | ||
/** | ||
* Convenience function. | ||
* @name checkbox | ||
* @method | ||
* @param {boolean} state | ||
@@ -95,10 +199,8 @@ * @returns {HTMLImageElement} {@link module:images.checked|checked} when `state` is truthy or {@link module:images.unchecked|unchecked} otherwise. | ||
*/ | ||
images.checkbox = function(state) { | ||
function checkbox(state) { | ||
return images[state ? 'checked' : 'unchecked']; | ||
}; | ||
} | ||
/** | ||
* Convenience function. | ||
* @name filter | ||
* @method | ||
* @param {boolean} state | ||
@@ -108,6 +210,15 @@ * @returns {HTMLImageElement} {@link module:images.filter-off|filter-off} when `state` is truthy or {@link module:images.filter-on|filter-on} otherwise. | ||
*/ | ||
images.filter = function(state) { | ||
function filter(state) { | ||
return images[state ? 'filter-on' : 'filter-off']; | ||
}; | ||
} | ||
// add methods as non-enumerable members so member images can be enumerated | ||
Object.defineProperties(images, { | ||
add: { value: add }, | ||
setTheme: { value: setTheme }, | ||
checkbox: { value: checkbox }, | ||
filter: { value: filter } | ||
}); | ||
module.exports = images; |
{ | ||
"name": "fin-hypergrid", | ||
"version": "3.1.0", | ||
"version": "3.2.0", | ||
"description": "Canvas-based high-performance grid", | ||
@@ -35,2 +35,3 @@ "main": "src/Hypergrid", | ||
"sparse-boolean-array": "1.0.1", | ||
"svg-themer": "^1.1.2", | ||
"synonomous": "^2.1.2" | ||
@@ -45,3 +46,3 @@ }, | ||
"gulp-header": "^1.8.2", | ||
"gulp-imagine-64": "^1.0.1", | ||
"gulp-imagine-64": "^2.0.1", | ||
"gulp-load-plugins": "^1.1.0", | ||
@@ -48,0 +49,0 @@ "gulp-mocha": "^6.0.0", |
@@ -18,5 +18,5 @@ **fin-hypergrid** is an ultra-fast HTML5 grid presentation layer, achieving its speed by rendering (in a canvas tag) only the currently visible portion of your (virtual) grid, thus avoiding the latency and life-cycle issues of building, walking, and maintaining a complex DOM structure. Please be sure to checkout our [design overview](OVERVIEW.md) | ||
### Current Release (3.1.0 - 29 September 2018) | ||
### Current Release (3.2.0 - 17 November 2018) | ||
**Hypergrid 3.1 includes 3.0’s revised data model with some breaking changes.** | ||
> **CAUTION:** For those considering upgrading directly from v2, be advised Hypergrid v3 introduced a revised data model _with breaking changes._ The impact of these changes has been intentionally minimized and should not affect the vast majority of users. See the [v3.0.0 release notes](https://github.com/fin-hypergrid/core/releases/tag/v3.0.0) for more information. | ||
@@ -29,3 +29,3 @@ _For a complete list of changes, see the [release notes](https://github.com/fin-hypergrid/core/releases)._ | ||
Published as a CommonJS module to npmjs.org. | ||
Specify a <a href="https://semver.org/">SEMVER</a> of `"fin-hypergrid": "3.1.0"` (or `"^3.1.0"`) in your package.json file, | ||
Specify a <a href="https://semver.org/">SEMVER</a> of `"fin-hypergrid": "3.2.0"` (or `"^3.2.0"`) in your package.json file, | ||
issue the `npm install` command, and let your bundler (<a target="webpack" href="https://webpack.js.org/">wepback</a>, | ||
@@ -32,0 +32,0 @@ <a target="browserify" href="http://browserify.org/">Browserify</a>) create a single file containing both Hypergrid and your application. |
@@ -22,10 +22,12 @@ /* globals alert */ | ||
Base.prototype.atLeastVersion = function(neededVersion) { | ||
var neededParts = neededVersion.split('.'), | ||
thisParts = this.version.split('.'), | ||
delta; | ||
neededParts.find(function(neededPart, i) { | ||
return (delta = neededPart - thisParts[i]); | ||
}); | ||
return delta >= 0; | ||
Base.prototype.versionAtLeast = function(neededVersion) { | ||
var neededParts = neededVersion.split('.').map(Number), | ||
delta = this.version.split('.').map(function(part, i) { return Number(part) - neededParts[i]; }); | ||
return ( | ||
delta[0] > 0 || | ||
delta[0] === 0 && ( | ||
delta[1] > 0 || | ||
delta[1] === 0 && delta[2] >= 0 | ||
) | ||
); | ||
}; | ||
@@ -32,0 +34,0 @@ |
@@ -144,3 +144,3 @@ 'use strict'; | ||
ixoffset = width - Math.round((width - centerIcon.width) / 2) - centerIcon.width; | ||
gc.drawImage(centerIcon, x + ixoffset, y + iyoffset); | ||
gc.drawImage(centerIcon, x + ixoffset, y + iyoffset, centerIcon.width, centerIcon.height); // see [SIZE NOTE]! | ||
valWidth = iconPadding + centerIcon.width + iconPadding; | ||
@@ -156,3 +156,3 @@ if (config.hotIcon === 'center') { | ||
iyoffset = Math.round((height - leftIcon.height) / 2); | ||
gc.drawImage(leftIcon, x + iconPadding, y + iyoffset); | ||
gc.drawImage(leftIcon, x + iconPadding, y + iyoffset, leftIcon.width, leftIcon.height); // see [SIZE NOTE]! | ||
if (config.hotIcon === 'left') { | ||
@@ -176,3 +176,3 @@ config.clickRect = new Rectangle(iconPadding, iyoffset, leftIcon.width, leftIcon.height); | ||
iyoffset = Math.round((height - rightIcon.height) / 2); | ||
gc.drawImage(rightIcon, rightX, y + iyoffset); | ||
gc.drawImage(rightIcon, rightX, y + iyoffset, rightIcon.width, rightIcon.height); // see [SIZE NOTE]! | ||
if (config.hotIcon === 'right') { | ||
@@ -196,2 +196,12 @@ config.clickRect = new Rectangle(ixoffset, iyoffset, rightIcon.width, rightIcon.height); | ||
/* [SIZE NOTE] (11/1/2018): Always call `drawImage` with explicit width and height overload. | ||
* Possible browser bug: Although 3rd and 4th parameters to `drawImage` are optional, | ||
* when image data derived from SVG source, some browsers (e.g., Chrome 70) implementation | ||
* of `drawImage` only respects _implicit_ `width` x `height` specified in the root <svg> | ||
* element `width` & `height` attributes. Otherwise, image is copied into canvas using its | ||
* `naturalWidth` x `naturalHeight`. That is, _explict_ settings of `width` & `height` | ||
* (i.e, via property assignment, calling setAttribute, or in `new Image` call) have no | ||
* effect on `drawImage` in the case of SVGs on these browsers. | ||
*/ | ||
/** | ||
@@ -198,0 +208,0 @@ * @summary Renders single line text. |
@@ -461,2 +461,19 @@ /* eslint-env browser */ | ||
); | ||
// add some interesting mouse offsets | ||
var drilldown; | ||
if ((drilldown = primitiveEvent.primitiveEvent && primitiveEvent.primitiveEvent.detail)) { | ||
decoratedEvent.gridPoint = drilldown.mouse; | ||
if ((drilldown = drilldown.primitiveEvent)) { | ||
decoratedEvent.clientPoint = { | ||
x: drilldown.clientX, | ||
y: drilldown.clientY | ||
}; | ||
decoratedEvent.pagePoint = { | ||
x: drilldown.clientX + window.scrollX, | ||
y: drilldown.clientY + window.scrollY | ||
}; | ||
} | ||
} | ||
cb.call(grid, decoratedEvent); | ||
@@ -580,3 +597,3 @@ } | ||
this.addInternalEventListener('fin-canvas-context-menu', function(e) { | ||
handleMouseEvent(e, function(mouseEvent){ | ||
handleMouseEvent(e, function(mouseEvent) { | ||
grid.delegateContextMenu(mouseEvent); | ||
@@ -583,0 +600,0 @@ grid.fireSyntheticContextMenuEvent(mouseEvent); |
@@ -14,2 +14,3 @@ 'use strict'; | ||
var HypergridError = require('../lib/error'); | ||
var images = require('../../images'); | ||
@@ -77,11 +78,5 @@ var styles = [ | ||
}); | ||
var pseudopropAdvice = { | ||
showRowNumbers: 'rowHeaderCheckboxes and rowHeaderNumbers', | ||
lineColor: 'gridLinesHColor and gridLinesVColor', | ||
lineWidth: 'gridLinesHWidth and gridLinesVWidth', | ||
gridBorder: 'gridBorderLeft, gridBorderRight, gridBorderTop, and gridBorderBottom' | ||
}; | ||
function applyTheme(theme) { | ||
var themeLayer, grids, props; | ||
var themeLayer, grids, props, themeObject; | ||
@@ -122,20 +117,21 @@ if (theme && typeof theme === 'object' && !Object.getOwnPropertyNames(theme).length) { | ||
if (typeof theme === 'string') { | ||
if (!registry[theme]) { | ||
throw new HypergridError('Unknown theme "' + theme + '"'); | ||
} | ||
theme = registry[theme]; | ||
if (typeof theme === 'object') { | ||
themeObject = theme; | ||
} else if (!registry[theme]) { | ||
throw new HypergridError('Unknown theme "' + theme + '"'); | ||
} else { | ||
themeObject = registry[theme]; | ||
} | ||
if (theme) { | ||
if (themeObject) { | ||
// When no theme name, set it to explicit `undefined` (to mask defaults.themeName). | ||
if (!theme.themeName) { | ||
theme.themeName = undefined; | ||
if (!themeObject.themeName) { | ||
themeObject.themeName = undefined; | ||
} | ||
Object.keys(theme).forEach(function(key) { | ||
Object.keys(themeObject).forEach(function(key) { | ||
if (key in dynamicPropertyDescriptors) { | ||
if (key in dynamicCosmetics) { | ||
grids.forEach(function(grid) { | ||
grid.properties[key] = theme[key]; | ||
grid.properties[key] = themeObject[key]; | ||
}); | ||
@@ -145,8 +141,10 @@ } else { | ||
// r-values on the theme layer is ineffective so let's not allow it. | ||
var message = pseudopropAdvice[key]; | ||
message = message | ||
? 'Ignoring unexpected pseudo-prop ' + key + ' in theme object. Use actual props ' + message + ' instead.' | ||
: 'Ignoring invalid property ' + key + ' in theme object.'; | ||
console.warn(message); | ||
delete theme[key]; | ||
switch (key) { | ||
case 'lineColor': | ||
themeObject.gridLinesHColor = themeObject.gridLinesVColor = themeObject[key]; | ||
break; | ||
default: | ||
console.warn('Ignoring unexpected dynamic property ' + key + ' from theme object.'); | ||
} | ||
// delete themeObject[key]; | ||
} | ||
@@ -157,3 +155,3 @@ } | ||
// No .assign() because themeName is read-only in defaults layer | ||
Object.defineProperties(themeLayer, Object.getOwnPropertyDescriptors(theme)); | ||
Object.defineProperties(themeLayer, Object.getOwnPropertyDescriptors(themeObject)); | ||
} | ||
@@ -164,2 +162,4 @@ | ||
}); | ||
return themeObject; | ||
} | ||
@@ -194,4 +194,4 @@ | ||
* * **string:** A registered theme name. | ||
* * **object:** A unregistered (anonymous) theme object. Empty object removes grid theme, exposing global theme. | ||
* * _falsy value:_ Also removes grid theme. | ||
* * **object:** An anonymous (unregistered) theme object. Empty object removes grid theme, exposing global theme. | ||
* * _falsy value:_ Also removes grid theme (like empty object). | ||
* @param {string|undefined} [theme.themeName=undefined] | ||
@@ -223,3 +223,3 @@ * @memberOf Hypergrid# | ||
enumerable: true, | ||
set: mixin.applyTheme, | ||
set: applyTheme, | ||
get: mixin.getTheme | ||
@@ -245,3 +245,4 @@ }); | ||
* ``` | ||
* If omitted, the theme named in the first parameter is unregistered. | ||
* If omitted, unregister the theme named in the first parameter. | ||
* | ||
* Grid instances that have previously applied the named theme are unaffected by this action (whether re-registering or unregistering). | ||
@@ -296,3 +297,2 @@ * @memberOf Hypergrid. | ||
* the `defaults` layer at the bottom of the properties hierarchy. | ||
* @this {Hypergrid.} | ||
* @param {object|string} [theme=registry.default] - One of: | ||
@@ -305,7 +305,10 @@ * * **string:** A registered theme name. | ||
*/ | ||
applyTheme: applyTheme | ||
applyTheme: function(theme) { | ||
var themeObject = applyTheme.call(this, theme); | ||
images.setTheme(themeObject); | ||
} | ||
}; | ||
Object.defineProperty(sharedMixin, 'theme', { // global theme setter/getter | ||
enumerable: true, | ||
set: applyTheme, | ||
set: sharedMixin.applyTheme, | ||
get: function() { return defaults; } // the defaults layer *is* the global theme layer | ||
@@ -312,0 +315,0 @@ }); |
@@ -9,2 +9,5 @@ /* globals CustomEvent */ | ||
'mousePoint', | ||
'gridPoint', | ||
'clientPoint', | ||
'pagePoint', | ||
'keys', | ||
@@ -11,0 +14,0 @@ 'row' |
@@ -35,2 +35,3 @@ 'use strict'; | ||
// getClassName defined if new item derived from extend-me | ||
name = name || item.getClassName && item.getClassName(); | ||
@@ -65,2 +66,12 @@ | ||
/** | ||
* Create a new item extended from base class. For formal parameters, see {@link Registry#add add}. | ||
* @memberOf Registry# | ||
*/ | ||
make: function(name, prototype) { | ||
var last = arguments.length - 1; | ||
arguments[last] = this.BaseClass.extend(arguments[last]); | ||
this.add.apply(this, arguments); | ||
}, | ||
/** | ||
* Fetch a registered item. | ||
@@ -67,0 +78,0 @@ * @param {string} [name] |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
749043
18528
12
+ Addedsvg-themer@^1.1.2
+ Addedsvg-themer@1.1.2(transitive)