Last active
January 28, 2022 01:22
-
-
Save runspired/1bdaaf166d0ed07642afacd4bdcdba0d to your computer and use it in GitHub Desktop.
Box and Mask
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Component from '@glimmer/component'; | |
export default class extends Component {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Controller from '@ember/controller'; | |
import { tracked } from "@glimmer/tracking"; | |
export default class ApplicationController extends Controller { | |
appName = 'Ember Twiddle'; | |
@tracked | |
value = null; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { helper } from '@ember/component/helper'; | |
import { tracked } from "@glimmer/tracking"; | |
import { dependentKeyCompat } from "@ember/object/compat"; | |
class Box { | |
constructor(context, path) { | |
this.context = context; | |
this.path = path; | |
} | |
@dependentKeyCompat | |
get value() { | |
return this.context[this.path]; | |
} | |
set value(v) { | |
this.context[this.path] = v; | |
} | |
update(value) { | |
} | |
} | |
export default helper(function box([context, path]/*, hash*/) { | |
return new Box(context, path); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { helper } from '@ember/component/helper'; | |
export default helper(function inc(params/*, hash*/) { | |
return params[0] + params[1]; | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { helper } from '@ember/component/helper'; | |
import { tracked } from "@glimmer/tracking"; | |
import { dependentKeyCompat } from "@ember/object/compat"; | |
const Masks = new WeakMap(); | |
class Mask { | |
@tracked current; | |
constructor(context, path, config = {}) { | |
this.context = context; | |
this.path = path; | |
this.pattern = config.pattern || "MM/DD/YYYY"; | |
this.current = this.context[this.path] || ""; | |
} | |
get upstream() { | |
return this.context[this.path]; | |
} | |
get isValid() { | |
return this.validate(this.current); | |
} | |
format(value) { | |
const { pattern } = this; | |
let val = ""; | |
if (!value) { | |
return val; | |
} | |
for (let i = 0; i < pattern.length; i++) { | |
let char = value.length > i ? value.charAt(i) : ""; | |
let expected = pattern.charAt(i); | |
if (expected === "M" || expected === "D" || expected === "Y") { | |
val += char; | |
} else if (char === expected) { | |
val += char; | |
} else if (value.length === i) { | |
val += expected; | |
} | |
} | |
return val; | |
} | |
validate(value) { | |
const { pattern } = this; | |
if (this.partialMatch(value) && value.length === pattern.length) { | |
let year = ""; | |
let month = ""; | |
let day = ""; | |
for (let i = 0; i < pattern.length; i++) { | |
let expected = pattern.charAt(i); | |
let char = value.charAt(i); | |
if (expected === "M") { | |
month += char; | |
} else if (expected === "D") { | |
day += char; | |
} else if (expected === "Y") { | |
year += char; | |
} | |
} | |
let monthNum = Number(month); | |
let dayNum = Number(day); | |
let isFebruary = monthNum === 2; | |
let isShortMonth = [4, 6, 9, 11].includes(monthNum); | |
if (isFebruary) { | |
let isLeapYear = Number(year) % 4 === 0; | |
let isCenturyYear = year.endsWith("00"); | |
let isCenturyLeapYear = isCenturyYear && Number(year) % 400 === 0; | |
if (isCenturyYear) { | |
return isCenturyLeapYear? dayNum <= 29 : dayNum <= 28; | |
} else { | |
return isLeapYear ? dayNum <= 29 : dayNum <= 28; | |
} | |
} else if (isShortMonth) { | |
return dayNum <= 30; | |
} | |
return true; | |
} | |
return false; | |
} | |
partialMatch(value) { | |
const { pattern } = this; | |
const isFullMonth = pattern.includes("MM"); | |
const isFullDay = pattern.includes("DD"); | |
const isFullYear = pattern.includes("YYYY"); | |
let val = ""; | |
if (!value) { | |
return true; | |
} | |
if (value.length > pattern.length) { | |
return false; | |
} | |
let buff = ""; | |
for (let i = 0, j = 0; i < pattern.length; j++, i++) { | |
if (value.length <= i) { | |
return true; | |
} | |
let char = value.charAt(j); | |
let expected = pattern.charAt(i); | |
console.log({ expected, char, buff }); | |
// handle months | |
if (expected === "M") { | |
if (isFullMonth) { | |
if (buff === "M") { | |
let prev = value.charAt(j - 1); | |
if (prev === "0") { | |
if (!/[1-9]/.test(char)) { | |
return false; // 01 09 but not 00 0a etc. | |
} | |
} else { | |
if (!/[0-2]/.test(char)) { | |
return false; // 10-12 but not 1a 13 etc. | |
} | |
} | |
buff = ""; | |
} else { | |
if (!/[0-1]/.test(char)) { | |
return false; // 0 1 but not 2 a etc. | |
} | |
buff = "M"; | |
} | |
} else { | |
if (i > 0) { | |
let prev = value.charAt(j - 1); | |
if (prev === "1") { | |
if (!/[0-2]/.test(char)) { | |
return false; // 10-12 but not 1a 13 etc. | |
} | |
} else { | |
if (!/[1-9]/.test(char)) { | |
return false; // 1 9 but not 0 a etc. | |
} | |
} | |
} else { | |
if (!/[1-9]/.test(char)) { | |
return false; // 1 9 but not 0 a etc. | |
} | |
} | |
} | |
// handle days | |
} else if (expected === "D") { | |
if (isFullDay) { | |
if (buff === "D") { | |
let prev = value.charAt(j - 1); | |
let num = Number(prev + char); | |
if (!/[0-9]/.test(char) || isNaN(num) || num === 0 || num > 31) { | |
return false; | |
} | |
buff = ""; | |
} else { | |
if (!/[0-3]/.test(char)) { | |
return false; | |
} | |
buff = "D"; | |
} | |
} else { | |
let num; | |
if (i > 0) { | |
let prev = value.charAt(j - 1); | |
if (/[1-3]/.test(prev)) { | |
num = Number(prev + char); | |
} | |
} else { | |
num = Number(char); | |
} | |
if (!/[0-9]/.test(char) || isNaN(num) || num === 0 || num > 31) { | |
return false; | |
} | |
} | |
} else if (expected === "Y") { | |
if (buff === "") { | |
if (!/[1-2]/.test(char)) { | |
return false; | |
} | |
buff = "Y"; | |
} else if (buff === "Y") { | |
let prev = value.charAt(i - 1); | |
if (prev === "1") { | |
if (char !== "9") { | |
return false; | |
} | |
} else if (prev === "2") { | |
if (char !== "0") { | |
return false; | |
} | |
} | |
buff = "YY"; | |
} else if (buff === "YY" || buff === "YYY") { | |
if (!/[0-9]/.test(char)) { | |
return false; | |
} | |
buff = buff === "YY" ? "YYY" : ""; | |
} else { | |
return false; | |
} | |
} else if (expected !== char) { | |
return false; | |
} | |
} | |
return true; | |
} | |
@dependentKeyCompat | |
get value() { | |
return this.current; | |
} | |
set value(v) { | |
const partialMatch = this.partialMatch(v); | |
if (partialMatch && this.validate(v)) { | |
// update if we fully match | |
this.current = this.context[this.path] = v; | |
return; | |
} else if (partialMatch) { | |
// buffer partial inputs | |
if (v.length > this.current.length) { | |
this.current = this.format(v); | |
return; | |
} else { | |
this.current = v; | |
return; | |
} | |
} else { | |
// reject invalid inputs other than deletes | |
return; | |
} | |
} | |
} | |
export default helper(function mask([context, path], config) { | |
let masksFor = Masks.get(context); | |
if (!masksFor) { | |
masksFor = new Map(); | |
Masks.set(context, masksFor); | |
} | |
let mask = masksFor.get(path); | |
if (!mask) { | |
mask = new Mask(context, path, config); | |
masksFor.set(path, mask); | |
} | |
return mask; | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": "0.17.1", | |
"EmberENV": { | |
"FEATURES": {}, | |
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": false, | |
"_APPLICATION_TEMPLATE_WRAPPER": true, | |
"_JQUERY_INTEGRATION": true | |
}, | |
"options": { | |
"use_pods": false, | |
"enable-testing": false | |
}, | |
"dependencies": { | |
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js", | |
"ember": "3.18.1", | |
"ember-template-compiler": "3.18.1", | |
"ember-testing": "3.18.1" | |
}, | |
"addons": { | |
"@glimmer/component": "1.0.0" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment