SHARE
TWEET

Untitled

a guest Oct 23rd, 2019 111 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * @fires Event#change when checked is changed
  3.  * @fires Event#change-mod when mod is changed
  4.  * @fires Event#next when the mod was left-clicked or selected with enter
  5.  * @fires Event#prev when the mod was right-clicked
  6.  */
  7. export class OsuModCheckbox extends HTMLElement {
  8.     protected wrapper: HTMLLabelElement;
  9.     protected cb: HTMLInputElement;
  10.     protected span: HTMLElement;
  11.     protected shadow: ShadowRoot;
  12.     protected css: HTMLStyleElement;
  13.  
  14.     constructor() {
  15.         // Always call super first in constructor
  16.         super();
  17.         this.shadow = this.attachShadow({ mode: "open" });
  18.  
  19.         this.css = document.createElement("style");
  20.  
  21.         this.css.textContent = `
  22.             :host {
  23.                 display: inline-block;
  24.                 max-width: calc(4.1282em * var(--icon-scale, 1));
  25.                 margin: 0 0.3em;
  26.  
  27.                 -webkit-touch-callout: none;
  28.                 -webkit-user-select: none;
  29.                 -khtml-user-select: none;
  30.                 -moz-user-select: none;
  31.                 -ms-user-select: none;
  32.                 user-select: none;
  33.             }
  34.  
  35.             .mapmod:focus-within span:before {
  36.                 filter: drop-shadow(0 2px 4px white);
  37.             }
  38.  
  39.             .mapmod {
  40.                 display: block;
  41.                 position: relative;
  42.                 z-index: 11;
  43.             }
  44.  
  45.             .mapmod input {
  46.                 width: 0;
  47.                 height: 0;
  48.                 opacity: 0;
  49.                 position: absolute;
  50.                 left: 0;
  51.                 top: 0;
  52.             }
  53.  
  54.             .mapmod input + span {
  55.                 display: block;
  56.                 text-align: center;
  57.             }
  58.  
  59.             .mapmod input + span:before {
  60.                 content: "";
  61.                 display: block;
  62.                 text-align: center;
  63.                 width: calc(4.1282em * var(--icon-scale, 1));
  64.                 height: calc(2.8em * var(--icon-scale, 1));
  65.                 opacity: 0.6;
  66.                 background-image: url(/img/mod_fallback.min.svg), url(/img/modbg.min.svg);
  67.                 background-size: auto 70%, 100%;
  68.                 background-repeat: no-repeat;
  69.                 background-position: center center;
  70.                 transition: transform 0.1s ease-out, opacity 0.1s ease-out;
  71.                 z-index: -5;
  72.             }
  73.  
  74.             .mapmod:hover input + span:before {
  75.                 opacity: 0.8;
  76.             }
  77.  
  78.             .mapmod input:checked + span:before {
  79.                 opacity: 1;
  80.                 transform: scale(1.15) rotate(6deg);
  81.                 opacity: 1;
  82.             }
  83.  
  84.             .mapmod.mod_ez input + span:before { background-image: url(/img/mod_ez.min.svg), url(/img/modbg.min.svg); }
  85.             .mapmod.mod_nf input + span:before { background-image: url(/img/mod_nf.min.svg), url(/img/modbg.min.svg); }
  86.             .mapmod.mod_ht input + span:before { background-image: url(/img/mod_ht.min.svg), url(/img/modbg.min.svg); }
  87.             .mapmod.mod_so input + span:before { background-image: url(/img/mod_so.min.svg), url(/img/modbg.min.svg); }
  88.             .mapmod.mod_hr input + span:before { background-image: url(/img/mod_hr.min.svg), url(/img/modbg.min.svg); }
  89.             .mapmod.mod_sd input + span:before { background-image: url(/img/mod_sd.min.svg), url(/img/modbg.min.svg); }
  90.             .mapmod.mod_dt input + span:before { background-image: url(/img/mod_dt.min.svg), url(/img/modbg.min.svg); }
  91.             .mapmod.mod_nc input + span:before { background-image: url(/img/mod_nc.min.svg), url(/img/modbg.min.svg); }
  92.             .mapmod.mod_hd input + span:before, .mapmod.mod_fi input + span:before { background-image: url(/img/mod_hd.min.svg), url(/img/modbg.min.svg); }
  93.             .mapmod.mod_fl input + span:before {background-image: url(/img/mod_fl.min.svg), url(/img/modbg.min.svg);}
  94.             .mapmod.mod_rx input + span:before { background-image: url(/img/mod_rx.min.svg), url(/img/modbg.min.svg); }
  95.             .mapmod.mod_ap input + span:before { background-image: url(/img/mod_ap.min.svg), url(/img/modbg.min.svg); }
  96.             .mapmod.mod_rn input + span:before { background-image: url(/img/mod_rn.min.svg), url(/img/modbg.min.svg); }
  97.  
  98.             .mapmod.hidden {
  99.                 pointer-events: none;
  100.                 opacity: 0;
  101.             }
  102.  
  103.             @keyframes modentry {
  104.                 0% {
  105.                     opacity: 1;
  106.                     transform: translate(7%, 17%) scale(1.15) rotate(6deg);
  107.                     transform-origin: 100% 100%;
  108.                     z-index: -10;
  109.                     filter: brightness(0.8);
  110.                 }
  111.                 40% {
  112.                     transform: translate(7%, 17%) scale(1.15) rotate(-11deg);
  113.                     transform-origin: 100% 100%;
  114.                     z-index: -1;
  115.                     filter: brightness(0.8);
  116.                 }
  117.                 60% {
  118.                     transform: translate(4%, 15%) scale(1.15) rotate(-11deg);
  119.                     transform-origin: 100% 100%;
  120.                     z-index: -1;
  121.                     filter: brightness(1);
  122.                 }
  123.                 100% {
  124.                     transform: translate(4%, 15%) scale(1.15) rotate(6deg);
  125.                     transform-origin: 100% 100%;
  126.                     z-index: -5;
  127.                 }
  128.             }
  129.  
  130.             .mapmod.multi.entry span:before {
  131.                 animation: 0.2s modentry linear;
  132.             }
  133.  
  134.             .mapmod.multi.entry.reverse span:before {
  135.                 animation: 0.2s modentry linear reverse;
  136.             }
  137.  
  138.             @keyframes modexit {
  139.                 0% {
  140.                     opacity: 1;
  141.                     transform: translate(7%, 17%) scale(1.15) rotate(6deg);
  142.                     transform-origin: 100% 100%;
  143.                     filter: brightness(1);
  144.                 }
  145.                 40% {
  146.                     transform: translate(7%, 17%) scale(1.15) rotate(17deg);
  147.                     transform-origin: 100% 100%;
  148.                     filter: brightness(1);
  149.                 }
  150.                 60% {
  151.                     transform: translate(4%, 15%) scale(1.15) rotate(17deg);
  152.                     transform-origin: 100% 100%;
  153.                     filter: brightness(0.8);
  154.                 }
  155.                 100% {
  156.                     opacity: 1;
  157.                     transform: translate(4%, 15%) scale(1.15) rotate(6deg);
  158.                     transform-origin: 100% 100%;
  159.                     filter: brightness(0.8);
  160.                 }
  161.             }
  162.  
  163.             @keyframes opacityexit {
  164.                 from { opacity: 1; }
  165.                 to { opacity: 0; }
  166.             }
  167.  
  168.             .mapmod.multi.exit.hidden {
  169.                 animation: 0.2s opacityexit step-end;
  170.                 color: transparent;
  171.             }
  172.  
  173.             .mapmod.multi.exit span:before {
  174.                 animation: 0.2s modexit linear;
  175.             }
  176.  
  177.             .mapmod.multi.exit.reverse span:before {
  178.                 animation: 0.2s modexit linear reverse;
  179.             }
  180.         `;
  181.  
  182.         // attach the created elements to the shadow dom
  183.  
  184.         this.shadow.appendChild(this.css);
  185.  
  186.         this.wrapper = document.createElement("label");
  187.         this.wrapper.classList.add("mapmod", "checkbox");
  188.         this.shadow.appendChild(this.wrapper);
  189.  
  190.         this.cb = document.createElement("input");
  191.         this.cb.setAttribute("type", "checkbox");
  192.         this.cb.setAttribute("aria-label", "Mod");
  193.         this.cb.onchange = () => {
  194.             this.checked = this.cb.checked;
  195.             this.dispatchEvent(new CustomEvent("next", {
  196.                 bubbles: true,
  197.                 cancelable: false
  198.             }));
  199.             if (this.wrapper.classList.contains("reverse"))
  200.                 this.wrapper.classList.remove("reverse");
  201.         };
  202.         this.wrapper.appendChild(this.cb);
  203.  
  204.         this.span = document.createElement("span");
  205.         this.wrapper.appendChild(this.span);
  206.  
  207.         this.oncontextmenu = (e) => {
  208.             e.preventDefault();
  209.             this.checked = !this.checked;
  210.  
  211.             this.dispatchEvent(new CustomEvent("prev", {
  212.                 bubbles: true,
  213.                 cancelable: false
  214.             }));
  215.             if (!this.wrapper.classList.contains("reverse"))
  216.                 this.wrapper.classList.add("reverse");
  217.         };
  218.     }
  219.  
  220.     toggle() {
  221.         this.checked = !this.checked;
  222.     }
  223.  
  224.     static get observedAttributes() { return ["mod", "checked", "multi", "data-hidden"]; }
  225.  
  226.     attributeChangedCallback(name: string, oldValue: string, newValue: string) {
  227.         switch (name) {
  228.             case "mod":
  229.                 if (oldValue)
  230.                     this.wrapper.classList.remove("mod_" + oldValue.toLowerCase());
  231.                 this.wrapper.classList.add("mod_" + newValue.toLowerCase());
  232.                 this.span.textContent = newValue;
  233.                 this.cb.setAttribute("aria-label", "Mod " + newValue);
  234.  
  235.                 this.dispatchEvent(new CustomEvent("change-mod", {
  236.                     bubbles: true,
  237.                     cancelable: false
  238.                 }));
  239.                 break;
  240.             case "checked":
  241.                 if (this.cb.checked != this.checked)
  242.                     this.cb.checked = this.checked;
  243.  
  244.                 this.dispatchEvent(new CustomEvent("change", {
  245.                     bubbles: true,
  246.                     cancelable: false
  247.                 }));
  248.                 break;
  249.             case "multi":
  250.                 if (newValue) {
  251.                     this.wrapper.classList.add("multi");
  252.                     this.wrapper.classList.remove("single");
  253.                 }
  254.                 else {
  255.                     this.wrapper.classList.remove("multi");
  256.                     this.wrapper.classList.add("single");
  257.                 }
  258.                 break;
  259.             case "data-hidden":
  260.                 if (newValue) {
  261.                     this.wrapper.classList.add("hidden");
  262.                     this.wrapper.classList.remove("entry");
  263.                     if (this.checked)
  264.                         this.wrapper.classList.add("exit");
  265.                     this.cb.setAttribute("tabindex", "-1");
  266.                     this.cb.setAttribute("readonly", "readonly");
  267.                 }
  268.                 else {
  269.                     this.wrapper.classList.remove("hidden");
  270.                     this.wrapper.classList.remove("exit");
  271.                     if (this.checked)
  272.                         this.wrapper.classList.add("entry");
  273.                     this.cb.removeAttribute("tabindex");
  274.                     this.cb.removeAttribute("readonly");
  275.                     this.cb.focus();
  276.                 }
  277.                 break;
  278.             default:
  279.                 console.warn("unknown property change", name, oldValue, newValue);
  280.                 break;
  281.         }
  282.     }
  283.  
  284.     get checked(): boolean { return !!this.getAttribute("checked"); }
  285.     set checked(checked: boolean) { checked ? this.setAttribute("checked", "checked") : this.removeAttribute("checked"); }
  286.  
  287.     /**
  288.      * Turns on fancy flip on/off animations when hiding/showing
  289.      */
  290.     get multi(): boolean { return !!this.getAttribute("multi"); }
  291.     set multi(multi: boolean) { multi ? this.setAttribute("multi", "multi") : this.removeAttribute("multi"); }
  292.  
  293.     /**
  294.      * Plays a hide/show animation
  295.      */
  296.     get hidden(): boolean { return !!this.getAttribute("data-hidden"); }
  297.     set hidden(hidden: boolean) { hidden ? this.setAttribute("data-hidden", "data-hidden") : this.removeAttribute("data-hidden"); }
  298.  
  299.     get mod(): string { return this.getAttribute("mod") || ""; }
  300.     set mod(mod: string) { this.setAttribute("mod", mod || ""); }
  301.  
  302.     get value(): string | null { return this.checked ? this.mod : null; }
  303. }
  304.  
  305. window.customElements.define("osu-mod-checkbox", OsuModCheckbox);
  306.  
  307. export class OsuModsButton extends HTMLElement {
  308.     mods: OsuModCheckbox[];
  309.     css: HTMLStyleElement;
  310.     shadow: ShadowRoot;
  311.  
  312.     constructor() {
  313.         // Always call super first in constructor
  314.         super();
  315.         this.shadow = this.attachShadow({ mode: "open" });
  316.  
  317.  
  318.         this.css = document.createElement("style");
  319.  
  320.         this.css.textContent = `
  321.             :host {
  322.                 display: inline-block;
  323.                 position: relative;
  324.                 width: calc(4.2em * var(--icon-scale, 1));
  325.                 height: calc(2.8em * var(--icon-scale, 1) + 1.5em);
  326.             }
  327.  
  328.             osu-mod-checkbox {
  329.                 position: absolute;
  330.                 top: 0;
  331.                 left: 0;
  332.                 z-index: 10;
  333.                 transition: z-index 0.2s linear;
  334.             }
  335.  
  336.             osu-mod-checkbox[data-hidden] {
  337.                 pointer-events: none;
  338.                 z-index: 0;
  339.             }
  340.         `;
  341.  
  342.         // attach the created elements to the shadow dom
  343.  
  344.         this.shadow.appendChild(this.css);
  345.  
  346.         this.mods = [];
  347.     }
  348.  
  349.     static get observedAttributes() { return ["modset"]; }
  350.  
  351.     attributeChangedCallback(name: string, oldValue: string, newValue: string) {
  352.         switch (name) {
  353.             case "modset":
  354.                 var newMods = newValue.split(/\s+/g);
  355.                 while (this.mods.length < newMods.length) {
  356.                     var cb = <OsuModCheckbox>document.createElement("osu-mod-checkbox");
  357.                     this.mods.push(cb);
  358.                     this.shadow.appendChild(cb);
  359.  
  360.                     cb.addEventListener("next", () => this.next());
  361.                     cb.addEventListener("prev", () => this.prev());
  362.                 }
  363.  
  364.                 while (this.mods.length > newMods.length) {
  365.                     this.shadow.removeChild(<any>this.mods.pop());
  366.                 }
  367.  
  368.                 for (let index = 0; index < newMods.length; index++) {
  369.                     this.mods[index].checked = false;
  370.                     this.mods[index].mod = newMods[index];
  371.                     this.mods[index].hidden = true;
  372.                     this.mods[index].multi = true;
  373.                 }
  374.  
  375.                 this.mods[0].hidden = false;
  376.                 break;
  377.             default:
  378.                 console.warn("unknown property change", name, oldValue, newValue);
  379.                 break;
  380.         }
  381.     }
  382.  
  383.     get index(): number {
  384.         for (let i = 0; i < this.mods.length; i++) {
  385.             if (!this.mods[i].hidden)
  386.                 return i;
  387.         }
  388.         return 0;
  389.     }
  390.  
  391.     get value(): string | null {
  392.         return this.mods[this.index].checked ? this.mods[this.index].mod : null;
  393.     }
  394.  
  395.     next() {
  396.         let i = this.index;
  397.         let j = (i + 1) % this.mods.length;
  398.         if (!this.mods[i].checked) {
  399.             this.mods[i].checked = true;
  400.             this.mods[i].hidden = true;
  401.             this.mods[i].checked = false;
  402.  
  403.             this.mods[j].checked = true;
  404.             this.mods[j].hidden = false;
  405.             this.mods[j].checked = j != 0;
  406.         }
  407.  
  408.         this.dispatchEvent(new CustomEvent("change", {
  409.             bubbles: true,
  410.             cancelable: false
  411.         }));
  412.     }
  413.  
  414.     prev() {
  415.         let i = this.index;
  416.         let j = (i + this.mods.length - 1) % this.mods.length;
  417.         if (i != 0 || this.mods[i].checked) {
  418.             this.mods[i].checked = true;
  419.             this.mods[i].hidden = true;
  420.             this.mods[i].checked = false;
  421.  
  422.             this.mods[j].checked = true;
  423.             this.mods[j].hidden = false;
  424.         }
  425.  
  426.         this.dispatchEvent(new CustomEvent("change", {
  427.             bubbles: true,
  428.             cancelable: false
  429.         }));
  430.     }
  431. }
  432.  
  433. window.customElements.define("osu-mods-button", OsuModsButton);
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top