Skip to content

Instantly share code, notes, and snippets.

@sukima
Last active July 1, 2019 19:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sukima/2a453515e5dc18b77849f36c39e624f6 to your computer and use it in GitHub Desktop.
Save sukima/2a453515e5dc18b77849f36c39e624f6 to your computer and use it in GitHub Desktop.
big numbers concept
import Component from '@ember/component';
import { computed } from '@ember/object';
import { scheduleOnce } from '@ember/runloop';
const { max, pow } = Math;
const FONT_SIZES = Object.freeze([null, null, null, 34, 26]);
export default Component.extend({
tagName: '',
maxFontSize: computed(function() {
let numbers = this.bigNumberRegistry.values();
let largestNumber = max(...numbers);
for (let index = 0; index < FONT_SIZES.length; index++) {
let cutoff = pow(10, index + 1);
if (largestNumber < cutoff) {
return FONT_SIZES[index];
}
}
return FONT_SIZES[FONT_SIZES.length - 1];
}),
init() {
this._super(...arguments);
this.set('bigNumberRegistry', new Map());
},
updateComputeds() {
this.notifyPropertyChange('maxFontSize');
},
actions: {
registerBigNumber(id, number) {
let oldNumber = this.bigNumberRegistry.get(id);
if (oldNumber !== number) {
this.bigNumberRegistry.set(id, number);
scheduleOnce('afterRender', this, 'updateComputeds');
}
},
unregisterBigNumber(id) {
this.bigNumberRegistry.delete(id);
scheduleOnce('afterRender', this, 'updateComputeds');
}
}
});
import Component from '@ember/component';
import { computed } from '@ember/object';
import { guidFor } from '@ember/object/internals';
import { htmlSafe } from '@ember/string';
export default Component.extend({
classNames: ['big-number'],
attributeBindings: ['style'],
fontSize: null,
style: computed('fontSize', function() {
return htmlSafe(this.fontSize ? `font-size: ${this.fontSize}px` : '');
}),
formatedNumber: computed('number', function() {
return this.number ? this.number.toLocaleString() : '0';
}),
didReceiveAttrs() {
this._super(...arguments);
if (this.styleManagerRegister) {
this.styleManagerRegister(guidFor(this), this.number);
}
},
willDestroyElement() {
this._super(...arguments);
if (this.styleManagerUnregister) {
this.styleManagerUnregister(guidFor(this));
}
}
});
import Component from '@ember/component';
import { computed } from '@ember/object';
import { reads } from '@ember/object/computed';
import { guidFor } from '@ember/object/internals';
import { htmlSafe } from '@ember/string';
import { inject as service } from '@ember/service';
export default Component.extend({
styleManager: service(),
classNames: ['big-number'],
attributeBindings: ['style'],
fontSize: reads('currentStyleManager.maxFontSize'),
style: computed('fontSize', function() {
return htmlSafe(this.fontSize ? `font-size: ${this.fontSize}px` : '');
}),
formatedNumber: computed('number', function() {
return this.number ? this.number.toLocaleString() : '0';
}),
didReceiveAttrs() {
this._super(...arguments);
if (!this.currentStyleManager) {
this.set('currentStyleManager', this.styleManager.currentStyleManager);
}
this.currentStyleManager.registerBigNumber(guidFor(this), this.number);
},
willDestroyElement() {
this._super(...arguments);
this.currentStyleManager.unregisterBigNumber(guidFor(this));
}
});
import Component from '@ember/component';
import { assert } from '@ember/debug';
import { inject as service } from '@ember/service';
export default Component.extend({
styleManager: service(),
tagName: '',
init() {
this._super(...arguments);
assert('StyleManagerScope cannot be nested', this.styleManager.currentStyleManager.isNull);
this.styleManager.createStyleManager();
},
didInsertElement() {
this._super(...arguments);
this.styleManager.resetStyleManager();
}
});
import Controller from '@ember/controller';
import { reads } from '@ember/object/computed';
export default Controller.extend({
numbers: reads('model')
});
import EmberObject, { computed } from '@ember/object';
import { scheduleOnce } from '@ember/runloop';
const { max, pow } = Math;
const FONT_SIZES = Object.freeze([null, null, null, 34, 26]);
export const StyleManager = EmberObject.extend({
maxFontSize: computed(function() {
let numbers = this.bigNumberRegistry.values();
let largestNumber = max(...numbers);
for (let index = 0; index < FONT_SIZES.length; index++) {
let cutoff = pow(10, index + 1);
if (largestNumber < cutoff) {
return FONT_SIZES[index];
}
}
return FONT_SIZES[FONT_SIZES.length - 1];
}),
init() {
this._super(...arguments);
this.set('bigNumberRegistry', new Map());
},
updateComputeds() {
this.notifyPropertyChange('maxFontSize');
},
registerBigNumber(id, number) {
let oldNumber = this.bigNumberRegistry.get(id);
if (oldNumber !== number) {
this.bigNumberRegistry.set(id, number);
scheduleOnce('afterRender', this, 'updateComputeds');
}
},
unregisterBigNumber(id) {
this.bigNumberRegistry.delete(id);
scheduleOnce('afterRender', this, 'updateComputeds');
}
});
export const NullStyleManager = EmberObject.extend({
isNull: true,
registerBigNumber() {},
unregisterBigNumber() {}
});
import Route from '@ember/routing/route';
export default Route.extend({
model() {
return [200, 35987, 1002, 678, 102556];
}
});
import Service from '@ember/service';
import { getOwner } from '@ember/application';
import { StyleManager, NullStyleManager } from '../lib/style-manager';
export default Service.extend({
createStyleManager() {
this.currentStyleManager = this.buildStyleManager(StyleManager);
},
resetStyleManager() {
this.currentStyleManager = this.buildStyleManager(NullStyleManager);
},
buildStyleManager(StyleManager) {
return StyleManager.create(getOwner(this).ownerInjection());
},
init() {
this._super(...arguments);
this.resetStyleManager();
}
});
body {
background-color: lightgray;
}
.flexbox {
display: flex;
flex-wrap: wrap;
}
.big-number {
font-size: 44px;
background-color: white;
margin: 10px;
padding: 10px;
height: 50px;
min-width: 100px;
max-width: 100px;
}
<h1>Current Usage</h1>
<div class="flexbox">
{{#each this.numbers as |number|}}
<BigNumber @number={{number}} />
{{/each}}
</div>
<h1>Concept Usage</h1>
<BigNumberStyleManager as |manager|>
<div class="flexbox">
{{#each this.numbers as |number|}}
<manager.big-number @number={{number}} />
{{!--
<BigNumber
@number={{number}}
@fontSize={{manager.maxFontSize}}
@styleManagerRegister={{action manager.register}}
@styleManagerUnregister={{action manager.unregister}}
/>
--}}
{{/each}}
</div>
</BigNumberStyleManager>
<h1>Service Concept Usage</h1>
<StyleManagerScope>
<div class="flexbox">
{{#each this.numbers as |number|}}
<SharedBigNumber @number={{number}} />
{{/each}}
</div>
</StyleManagerScope>
{{yield (hash
maxFontSize=this.maxFontSize
register=(action "registerBigNumber")
unregister=(action "unregisterBigNumber")
big-number=(component "big-number"
fontSize=this.maxFontSize
styleManagerRegister=(action "registerBigNumber")
styleManagerUnregister=(action "unregisterBigNumber")
)
)}}
import { run } from '@ember/runloop';
export default function destroyApp(application) {
run(application, 'destroy');
}
import Resolver from '../../resolver';
import config from '../../config/environment';
const resolver = Resolver.create();
resolver.namespace = {
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix
};
export default resolver;
import Ember from 'ember';
import Application from '../../app';
import config from '../../config/environment';
const { run } = Ember;
const assign = Ember.assign || Ember.merge;
export default function startApp(attrs) {
let application;
let attributes = assign({rootElement: "#test-root"}, config.APP);
attributes.autoboot = true;
attributes = assign(attributes, attrs); // use defaults, but you can override;
run(() => {
application = Application.create(attributes);
application.setupForTesting();
application.injectTestHelpers();
});
return application;
}
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { find, render } from '@ember/test-helpers';
import { run } from '@ember/runloop';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | big number style manager', function(hooks) {
setupRenderingTest(hooks);
test('adjusts fontSize when number changes', async function(assert) {
function getMaxFontSize() {
return find('#max-font-size').textContent.trim();
}
this.set('testNumber', 1);
await render(hbs`
<BigNumberStyleManager as |manager|>
<span id="max-font-size">{{manager.maxFontSize}}</span>
<manager.big-number @number={{this.testNumber}} />
</BigNumberStyleManager>
`);
assert.strictEqual(getMaxFontSize(), '');
run(() => this.set('testNumber', 10));
assert.strictEqual(getMaxFontSize(), '');
run(() => this.set('testNumber', 100));
assert.strictEqual(getMaxFontSize(), '');
run(() => this.set('testNumber', 1000));
assert.strictEqual(getMaxFontSize(), '34');
run(() => this.set('testNumber', 10000));
assert.strictEqual(getMaxFontSize(), '26');
run(() => this.set('testNumber', 100000));
assert.strictEqual(getMaxFontSize(), '26');
run(() => this.set('testNumber', 1000000));
assert.strictEqual(getMaxFontSize(), '26');
run(() => this.set('testNumber', 10000000));
assert.strictEqual(getMaxFontSize(), '26');
});
test('adjusts fontSize when multiple numbers are added', async function(assert) {
function getMaxFontSize() {
return find('#max-font-size').textContent.trim();
}
this.set('testNumbers', [1]);
await render(hbs`
<BigNumberStyleManager as |manager|>
<span id="max-font-size">{{manager.maxFontSize}}</span>
{{#each this.testNumbers as |testNumber|}}
<manager.big-number @number={{testNumber}} />
{{/each}}
</BigNumberStyleManager>
`);
assert.strictEqual(getMaxFontSize(), '');
run(() => this.testNumbers.pushObject(10));
assert.strictEqual(getMaxFontSize(), '');
run(() => this.testNumbers.pushObject(100));
assert.strictEqual(getMaxFontSize(), '');
run(() => this.testNumbers.pushObject(1000));
assert.strictEqual(getMaxFontSize(), '34');
run(() => this.testNumbers.pushObject(10000));
assert.strictEqual(getMaxFontSize(), '26');
run(() => this.testNumbers.pushObject(100000));
assert.strictEqual(getMaxFontSize(), '26');
run(() => this.testNumbers.pushObject(1000000));
assert.strictEqual(getMaxFontSize(), '26');
run(() => this.testNumbers.pushObject(10000000));
assert.strictEqual(getMaxFontSize(), '26');
});
});
import resolver from './helpers/resolver';
import {
setResolver, start
} from 'ember-qunit';
setResolver(resolver);
start();
{
"version": "0.15.1",
"EmberENV": {
"FEATURES": {}
},
"ENV": {
"APP": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js",
"ember": "3.4.3",
"ember-template-compiler": "3.4.3",
"ember-testing": "3.4.3"
},
"addons": {
"ember-data": "3.4.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment