const Div = "div" const Span = "span" const Img = "img" const Canvas = "canvas" const Button = "button" // const ELEMENT_NODE = 1 // const TEXT_NODE = 3 // const IS_NON_DIMENSIONAL = // /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i // function merge(...objects) { // return Object.assign({}, ...objects) // } // function isValidNodeType(obj) { // return obj != null && typeof obj !== "boolean" // } // function isSameNode(a, b) { // return a.tag === b.tag && a.key === b.key // } // function isSameNodeType(a, b) { // if (a.type !== b.type) { // return false // } // if (a.type === TEXT_NODE && a.text !== b.text) { // return false // } // if (a.tag !== b.tag) { // return false // } // return true // } // function createClass(value) { // if (typeof value === "string") { // return value // } // let output = "" // if (Array.isArray(value) && value.length > 0) { // for (let i = 0, len = value.length, tmp; i < len; i++) { // if ((tmp = createClass(value[i])) !== "") { // output += (output && " ") + tmp // } // } // } else { // for (const cls in value) { // if (value[cls]) { // output += (output && " ") + cls // } // } // } // return output // } // function createKeyToIndexMap(children, beginIndex, endIndex) { // const map = {} // for (let i = beginIndex; i <= endIndex; ++i) { // const child = children[i] // const key = child && child.key // if (key != null) { // map[key] = i // } // } // return map // } // function createVNode(tag, props, children, node = null) { // return { // type: ELEMENT_NODE, // tag, // props, // children, // key: props.key || null, // node, // } // } // function createTextVNode(text, node = null) { // return { // type: TEXT_NODE, // text: String(text), // node, // } // } // function normalizeChildren(children) { // return children.flat(Infinity).reduce((vnodes, vnode) => { // if (isValidNodeType(vnode)) { // const type = typeof vnode // if (type === "string" || type === "number") { // vnode = createTextVNode(vnode) // } // vnodes.push(vnode) // } // return vnodes // }, []) // } // function recycle(node) { // if (node.nodeType === 3) { // return createTextVNode(node.data, node) // } // if (node.nodeType === 1) { // return createVNode( // node.nodeName.toLowerCase(), // Array.from(node.attributes).reduce((map, attr) => { // const name = attr.name, // value = attr.value // if (name !== "style") { // map[name] = value // } // if (name === "key") { // node.removeAttribute("key") // } // return map // }, {}), // Array.from(node.childNodes).map(recycle), // node, // ) // } // } // function createElement(vnode, isSvg) { // let node // if (vnode.type === TEXT_NODE) { // node = document.createTextNode(vnode.text) // } else { // const { tag, props, children } = vnode // const length = children.length // isSvg = isSvg || tag === "svg" // node = isSvg // ? document.createElementNS("http://www.w3.org/2000/svg", tag) // : document.createElement(tag) // Object.keys(props).forEach((name) => // patchProperty(node, name, null, props[name], isSvg), // ) // if (length === 1) { // node.appendChild(createElement(children[0], isSvg)) // } else if (length > 1) { // node.appendChild( // children.reduce( // (frag, vchild) => // frag.appendChild(createElement(vchild, isSvg)) && frag, // document.createDocumentFragment(), // ), // ) // } // } // vnode.node = node // return node // } // function setStyle(element, name, value) { // if (name.startsWith("--")) { // element.style.setProperty(name, value == null ? "" : value) // } else if (value == null) { // element.style[name] = "" // } else if (typeof value != "number" || IS_NON_DIMENSIONAL.test(name)) { // element.style[name] = value // } else { // element.style[name] = value + "px" // } // } // function patchProperty(element, name, prevVal, nextVal, isSvg) { // if (name === "key" || name === "children") { // return // } // if (name === "style") { // if (typeof nextVal === "string") { // element.style.cssText = nextVal // } else { // if (typeof prevVal === "string") { // element.style.cssText = prevVal = "" // } // for (const key in merge(nextVal, prevVal)) { // setStyle(element, key, nextVal == null ? "" : nextVal[key]) // } // } // } else if ( // name.startsWith("on") && // (typeof prevVal === "function" || typeof nextVal === "function") // ) { // name = // name.toLowerCase() in element // ? name.toLowerCase().slice(2) // : name.slice(2) // if (nextVal) { // element.addEventListener(name, nextVal) // } // if (prevVal) { // element.removeEventListener(name, prevVal) // } // } else if (typeof nextVal !== "function") { // if (nextVal != null && (name === "class" || name === "className")) { // nextVal = createClass(nextVal) // } // if (!isSvg && name === "class") { // name = "className" // } // if ( // !isSvg && // name !== "width" && // name !== "height" && // name !== "href" && // name !== "list" && // name !== "form" && // name !== "tabIndex" && // name !== "download" && // name !== "rowSpan" && // name !== "colSpan" && // name !== "role" && // name in element // ) { // try { // element[name] = nextVal == null ? "" : nextVal // return // } catch (e) {} // eslint-disable-line no-empty // } // if (nextVal != null && (nextVal !== false || name.indexOf("-") != -1)) { // element.setAttribute(name, nextVal) // } else { // element.removeAttribute(name) // } // } // } // function patchChildren(parent, prevChildren, nextChildren, isSvg) { // let prevStartIndex = 0 // let prevEndIndex = prevChildren.length - 1 // let prevStartChild = prevChildren[0] // let prevEndChild = prevChildren[prevEndIndex] // let nextStartIndex = 0 // let nextEndIndex = nextChildren.length - 1 // let nextStartChild = nextChildren[0] // let nextEndChild = nextChildren[nextEndIndex] // let prevKeyToIdx // while (prevStartIndex <= prevEndIndex && nextStartIndex <= nextEndIndex) { // if (!prevStartChild) { // prevStartChild = prevChildren[++prevStartIndex] // } else if (!prevEndChild) { // prevEndChild = prevChildren[--prevEndIndex] // } else if (isSameNode(prevStartChild, nextStartChild)) { // patchElement(parent, prevStartChild, nextStartChild, isSvg) // prevStartChild = prevChildren[++prevStartIndex] // nextStartChild = nextChildren[++nextStartIndex] // } else if (isSameNode(prevEndChild, nextEndChild)) { // patchElement(parent, prevEndChild, nextEndChild, isSvg) // prevEndChild = prevChildren[--prevEndIndex] // nextEndChild = nextChildren[--nextEndIndex] // } else if (isSameNode(prevStartChild, nextEndChild)) { // patchElement(parent, prevStartChild, nextEndChild, isSvg) // parent.insertBefore( // prevStartChild.node, // prevEndChild.node.nextSibling, // ) // prevStartChild = prevChildren[++prevStartIndex] // nextEndChild = nextChildren[--nextEndIndex] // } else if (isSameNode(prevEndChild, nextStartChild)) { // patchElement(parent, prevEndChild, nextStartChild, isSvg) // parent.insertBefore(prevEndChild.node, prevStartChild.node) // prevEndChild = prevChildren[--prevEndIndex] // nextStartChild = nextChildren[++nextStartIndex] // } else { // if (!prevKeyToIdx) { // prevKeyToIdx = createKeyToIndexMap( // prevChildren, // prevStartIndex, // prevEndIndex, // ) // } // const key = nextStartChild.key // const prevIndex = key ? prevKeyToIdx[key] : null // if (prevIndex == null) { // parent.insertBefore( // createElement(nextStartChild, isSvg), // prevStartChild.node, // ) // nextStartChild = nextChildren[++nextStartIndex] // } else { // let prevChildToMove = prevChildren[prevIndex] // patchElement(parent, prevChildToMove, nextStartChild, isSvg) // prevChildren[prevIndex] = undefined // parent.insertBefore(prevChildToMove.node, prevStartChild.node) // nextStartChild = nextChildren[++nextStartIndex] // } // } // } // if (prevStartIndex > prevEndIndex) { // const subsequentElement = nextChildren[nextEndIndex + 1] // ? nextChildren[nextEndIndex + 1].node // : null // for (let i = nextStartIndex; i <= nextEndIndex; i++) { // parent.insertBefore( // createElement(nextChildren[i], isSvg), // subsequentElement, // ) // } // } else if (nextStartIndex > nextEndIndex) { // for (let i = prevStartIndex; i <= prevEndIndex; i++) { // const child = prevChildren[i] // if (child && child.node) { // parent.removeChild(child.node) // } // } // } // return nextChildren // } // function patchElement(parent, prevVNode, nextVNode, isSvg) { // if (prevVNode === nextVNode) { // if (prevVNode == null) { // return null // } // return prevVNode.node // } // if (prevVNode == null) { // return parent.appendChild(createElement(nextVNode, isSvg)) // } // let element = prevVNode.node // if (nextVNode == null) { // return parent.removeChild(element) && null // } // if (prevVNode.type === TEXT_NODE && nextVNode.type === TEXT_NODE) { // if (prevVNode.text !== nextVNode.text) { // element.data = nextVNode.text // } // } else if (!isSameNodeType(nextVNode, prevVNode)) { // const nextElement = createElement(nextVNode, isSvg) // parent.replaceChild(nextElement, element) // element = nextElement // } else { // isSvg = isSvg || nextVNode.tag === "svg" // const activeElement = document.activeElement // const prevVProps = prevVNode.props // const nextVProps = nextVNode.props // for (const name in merge(nextVProps, prevVProps)) { // if ( // (name === "value" || name === "selected" || name === "checked" // ? element[name] // : prevVProps[name]) !== nextVProps[name] // ) { // patchProperty( // element, // name, // prevVProps[name], // nextVProps[name], // isSvg, // ) // } // } // patchChildren(element, prevVNode.children, nextVNode.children, isSvg) // if (activeElement !== document.body) { // activeElement.focus() // } // } // nextVNode.node = element // return element // } // function render(parent, nextVNode) { // let prevVNode = parent._vnode || null // if (!prevVNode && parent.childNodes.length > 0) { // prevVNode = Array.from(parent.childNodes).map(recycle) // } // const prevIsArray = Array.isArray(prevVNode) // const nextIsArray = Array.isArray(nextVNode) // parent._vnode = nextVNode // if (prevIsArray || nextIsArray) { // const prevVChildren = (prevIsArray ? prevVNode : [prevVNode]).filter( // isValidNodeType, // ) // const nextVChildren = (nextIsArray ? nextVNode : [nextVNode]).filter( // isValidNodeType, // ) // const root = patchChildren(parent, prevVChildren, nextVChildren) // return root.length === 0 // ? null // : root.length === 1 // ? root[0].node // : root.map((vnode) => vnode.node) // } // return patchElement(parent, prevVNode, nextVNode) // } // function h(tag, props, ...children) { // if ( // !props || // props.type === ELEMENT_NODE || // props.type === TEXT_NODE || // typeof props.concat === "function" // ) { // children = [].concat(props || [], ...children) // props = null // } // props = props || {} // children = normalizeChildren(children) // if (typeof tag === "function") { // return tag({ children, ...props }) // } // return createVNode(tag, props, children) // } // function text(str) { // return createTextVNode(str) // } // VirtualNode // = { tag : string // , properties : { property: string } // , children : [VirtualNode] // } // | { text : string } // // Diff // = { replace : VirtualNode } // | { remove : true } // | { create : VirtualNode } // | { modify : { remove :: string[], set :: { property : value }, children :: Diff[] } } // | { noop : true } // const SMVC = (function () { function assert(predicate, ...args) { if (!predicate) { console.error(...args) throw new Error("fatal") } } const props = new Set([ "autoplay", "checked", "checked", "contentEditable", "controls", "default", "hidden", "loop", "selected", "spellcheck", "value", "id", "title", "accessKey", "dir", "dropzone", "lang", "src", "alt", "preload", "poster", "kind", "label", "srclang", "sandbox", "srcdoc", "type", "value", "accept", "placeholder", "acceptCharset", "action", "autocomplete", "enctype", "method", "name", "pattern", "htmlFor", "max", "min", "step", "wrap", "useMap", "shape", "coords", "align", "cite", "href", "target", "download", "download", "hreflang", "ping", "start", "headers", "scope", "span", ]) function setProperty(prop, value, el) { if (props.has(prop)) { el[prop] = value } else { el.setAttribute(prop, value) } } function listener(event) { const el = event.currentTarget const handler = el._ui.listeners[event.type] const enqueue = el._ui.enqueue assert(typeof enqueue == "function", "Invalid enqueue") const msg = handler(event) if (msg !== undefined) { enqueue(msg) } } function setListener(el, event, handle) { assert( typeof handle == "function", "Event listener is not a function for event:", event, ) if (el._ui.listeners[event] === undefined) { el.addEventListener(event, listener) } el._ui.listeners[event] = handle } function eventName(str) { if (str.indexOf("on") == 0) { return str.slice(2).toLowerCase() } return null } // diff two virtual nodes function diffOne(l, r) { assert( r instanceof VirtualNode, "Expected an instance of VirtualNode, found", r, ) const isText = l.text !== undefined if (isText) { return l.text !== r.text ? { replace: r } : { noop: true } } if (l.tag !== r.tag) { return { replace: r } } const remove = [] const set = {} for (const prop in l.properties) { if (r.properties[prop] === undefined) { remove.push(prop) } } for (const prop in r.properties) { if (r.properties[prop] !== l.properties[prop]) { set[prop] = r.properties[prop] } } const children = diffList(l.children, r.children) const noChildrenChange = children.every((e) => e.noop) const noPropertyChange = remove.length === 0 && Array.from(Object.keys(set)).length == 0 return noChildrenChange && noPropertyChange ? { noop: true } : { modify: { remove, set, children } } } function diffList(ls, rs) { assert(rs instanceof Array, "Expected an array, found", rs) const length = Math.max(ls.length, rs.length) return Array.from({ length }).map((_, i) => ls[i] === undefined ? { create: rs[i] } : rs[i] == undefined ? { remove: true } : diffOne(ls[i], rs[i]), ) } function create(enqueue, vnode) { assert( vnode instanceof VirtualNode, "Expected an instance of VirtualNode, found", vnode, ) if (vnode.text !== undefined) { const el = document.createTextNode(vnode.text) return el } const el = document.createElement(vnode.tag) el._ui = { listeners: {}, enqueue } for (const prop in vnode.properties) { const event = eventName(prop) const value = vnode.properties[prop] event === null ? setProperty(prop, value, el) : setListener(el, event, value) } for (const childVNode of vnode.children) { const child = create(enqueue, childVNode) el.appendChild(child) } return el } function modify(el, enqueue, diff) { for (const prop of diff.remove) { const event = eventName(prop) if (event === null) { el.removeAttribute(prop) } else { el._ui.listeners[event] = undefined el.removeEventListener(event, listener) } } for (const prop in diff.set) { const value = diff.set[prop] const event = eventName(prop) event === null ? setProperty(prop, value, el) : setListener(el, event, value) } assert( diff.children.length >= el.childNodes.length, "unmatched children lengths", ) apply(el, enqueue, diff.children) } function apply(el, enqueue, childrenDiff) { const children = Array.from(el.childNodes) childrenDiff.forEach((diff, i) => { const action = Object.keys(diff)[0] switch (action) { case "remove": children[i].remove() break case "modify": modify(children[i], enqueue, diff.modify) break case "create": { assert( i >= children.length, "adding to the middle of children", i, children.length, ) const child = create(enqueue, diff.create) el.appendChild(child) break } case "replace": { const child = create(enqueue, diff.replace) children[i].replaceWith(child) break } case "noop": break default: throw new Error( "Unexpected diff option: " + Object.keys(diff), ) } }) } class VirtualNode { constructor(any) { Object.assign(this, any) } } // Create an HTML element description (a virtual node) function h(tag, properties, children) { assert(typeof tag === "string", "Invalid tag value:", tag) assert( typeof properties === "object", "Expected properties object. Found:", properties, ) assert( Array.isArray(children), "Expected children array. Found:", children, ) return new VirtualNode({ tag, properties, children }) } // Create a text element description (a virtual text node) function text(content) { return new VirtualNode({ text: content }) } // Start managing the contents of an HTML element. function init(root, initialState, update, view, postDraw) { let state = initialState // client application state let nodes = [] // virtual DOM nodes let queue = [] // msg queue function enqueue(msg) { queue.push(msg) } // draws the current state function draw() { let newNodes = view(state) apply(root, enqueue, diffList(nodes, newNodes)) nodes = newNodes state = postDraw(state) } function updateState() { if (queue.length > 0) { let msgs = queue queue = [] msgs.forEach((msg) => { try { state = update(state, msg, enqueue) } catch (e) { console.error(e) } }) draw() } window.requestAnimationFrame(updateState) } draw() updateState() return { enqueue } } return { init, h, text } })() if (typeof define !== "undefined" && define.amd) { // AMD define([], function () { return SMVC }) } else if (typeof module !== "undefined" && module.exports) { // CommonJS module.exports = SMVC } else if (typeof window !== "undefined") { // Script tag window.SMVC = SMVC } let Css = { Stylesheet: undefined, ClassPrefix: "s", StyleId: 0, } function CssAppend(css) { if (!Css.Stylesheet) { Css.Stylesheet = document.createElement("style") document.head.appendChild(Css.Stylesheet) } Css.Stylesheet.textContent += css + "\n\n" } function CssApply(className, table) { let styles = "" for (let key in table) { let value = table[key] styles = styles.concat("\t", key, ": ", value, ";", "\n") } CssAppend(`${className} {\n${styles}}`) return className } function CssNew(table) { Css.StyleId += 1 let className = "s" + Css.StyleId let styles = "" for (let key in table) { let value = table[key] styles = styles.concat("\t", key, ": ", value, ";", "\n") } CssAppend(`.${className} {\n${styles}}`) return className } function CssPseudo(className, pseudo, table) { let styles = "" for (let key in table) { let value = table[key] styles = styles.concat("\t", key, ": ", value, ";", "\n") } CssAppend(`.${className}:${pseudo} {\n${styles}}`) return className } function CssApplyPseudo(className, pseudo, table) { let styles = "" for (let key in table) { let value = table[key] styles = styles.concat("\t", key, ": ", value, ";", "\n") } CssAppend(`${className}:${pseudo} {\n${styles}}`) return className } function CssJoin() { return Array.from(arguments).join(" ") } let NoImageSvg = `url('data:image/svg+xml,')` CssApply("body", { "-webkit-user-select": "none", "background-color": "var(--block-bg-color)", "color-scheme": "var(--tg-color-scheme)", "color": "var(--text-color)", "cursor": "default", "font-family": "var(--default-font)", "font-size": "13px", "font-weight": "400", "line-height": "16px", // "padding": "20px", "min-width": "100%", "padding": "0", "user-select": "none", "width": "1px", "display": "flex", "justify-content": "center", "align-items": "center", "margin-top": "20px", }) CssApply(":root", { "--default-font": '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', "--accent-text-color": "var(--tg-theme-accent-text-color)", "--animation": "0.15s ease", "--bg-color": "var(--tg-theme-bg-color)", "--bottom-bar-bg-color": "var(--tg-theme-bottom-bar-bg-color)", "--button-color": "var(--tg-theme-button-color)", "--button-text-color": "var(--tg-theme-button-text-color)", "--destructive-text-color": "var(--tg-theme-destructive-text-color)", "--header-bg-color": "var(--tg-theme-header-bg-color)", "--hint-color": "var(--tg-theme-hint-color)", "--link-color": "var(--tg-theme-link-color)", "--page-animation-duration": "0.3s", "--page-animation": "var(--page-animation-duration) ease", "--page-hint-color": "#83878a", "--placeholder-color": "#999da1", "--ripple-duration": "0.45s", "--ripple-end-duration": "0.3s", "--secondary-bg-color": "var(--tg-theme-secondary-bg-color)", "--section-bg-color": "var(--tg-theme-section-bg-color)", "--section-header-text-color": "var(--tg-theme-section-header-text-color)", "--section-separator-color": "var(--tg-theme-section-separator-color)", "--status-height": "48px", "--subtitle-text-color": "var(--tg-theme-subtitle-text-color)", "--text-color": "var(--tg-theme-text-color)", }) CssApply("*", { "-webkit-tap-highlight-color": "transparent", }) CssApply("button", { "cursor": "pointer", }) let StyleStoreItem = CssNew({ "background-color": "var(--secondary-bg-color)", "border-radius": "7px", "box-sizing": "border-box", "overflow": "hidden", "padding": "8px", "position": "relative", "text-align": "center", "width": "calc(100% / 2.05)", }) let StyleStoreBlock = CssNew({ "-webkit-box-shadow": "0 0.5px rgba(0, 0, 0, 0.07)", "background-color": "var(--block-bg-color)", "box-shadow": "0 0.5px rgba(0, 0, 0, 0.07)", }) let StyleStoreItems = CssNew({ "align-content": "flex-start", "display": "flex", "flex-wrap": "wrap", "justify-content": "space-between", "margin": "10px", "max-width": "480px", "overflow": "hidden", "row-gap": "8px", "transition": "max-height var(--page-animation), opacity var(--page-animation)", // "width": "fit-content", }) let StyleStoreCategory = CssNew({ "background-color": "var(--secondary-bg-color)", "border-radius": "7px", "box-sizing": "border-box", "height": "182px", "overflow": "hidden", "padding": "8px", "position": "relative", "text-align": "center", "transition": "all var(--animation)", "width": "calc(100% / 2.05)", }) let StyleSelected = CssNew({ "color": "#ff0000", }) let StyleStoreCategorySelected = CssJoin(StyleStoreCategory, StyleSelected) CssPseudo(StyleStoreCategory, "hover", { "transform": "scale(1.05)", }) CssPseudo(StyleStoreCategory, "active", { "transform": "scale(1.05)", }) let StyleStoreItemPhoto = CssNew({ "height": "150px", "position": "relative", }) let StyleStoreItemImage = CssNew({ "bottom": "0", "display": "inline-block", "height": "100%", "left": "0", "margin": "0 auto", "position": "relative", "right": "0", "top": "0", "vertical-align": "top", "width": "100%", // 'position': 'absolute', // 'width': '146px', }) let StyleStoreItemBadge = CssNew({ "border-radius": "4px", "border": "none", "box-sizing": "border-box", "color": "#fff", "display": "inline-block", "font-family": "var(--default-font)", "font-size": "13px", "font-weight": "500", "left": "5px", "line-height": "14px", "outline": "none", "padding": "2px 4px", "position": "absolute", "text-transform": "uppercase", "top": "5px", "transform": "rotate(-12deg)", }) let StyleStoreItemLabel = CssNew({ "align-items": "flex-start", "display": "flex", "flex-direction": "column", "font-size": "15px", "gap": "6px", "margin": "0 1px", "overflow": "hidden", "text-overflow": "ellipsis", "white-space": "nowrap", }) let StyleStoreItemTitle = CssNew({}) let StyleStoreItemPrice = CssNew({ "font-weight": "700", "white-space": "nowrap", }) let StyleStoreItemButtons = CssNew({ "display": "flex", "margin": "8px auto 0", "position": "relative", "transition": "all var(--animation)", }) let StyleStoreItemCounter = CssNew({ "animation": "var(--animation) both", "font-size": "20px", "height": "32px", "line-height": "32px", "margin": "12px", "min-width": "32px", "pointer-events": "none", "position": "absolute", "right": "0", "text-align": "center", "top": "0", "transform": "scale3d(0, 0, 1)", "vertical-align": "middle", "z-index": "3", }) let StyleNumberInput = CssNew({ "border-radius": "0", "border": "none", "box-sizing": "border-box", "color": "var(--text-color)", "cursor": "auto", "display": "block", "font-family": "var(--default-font)", "font-size": "17px", "height": "32px", "line-height": "21px", "outline": "none", "padding": "0px 10px", "resize": "none", "user-select": "auto", "width": "48px", }) let StyleLoadingContainer = CssNew({ "-webkit-transform": "translate(-50%, -50%)", "left": "50%", "position": "absolute", "top": "50%", "transform": "translate(-50%, -50%)", }) let StyleItemTitle = CssNew({ "font-size": "18px", "font-weight": "700", "line-height": "18px", "overflow": "hidden", "padding": "3px 0", "white-space": "nowrap", "width": "200px", }) let StyleStatusWrap = CssNew({ "left": "0", "position": "fixed", "right": "0", "top": "0", "transform": "translateY(var(--tg-viewport-height, 100vh))", "z-index": "1", }) CssApply(".cafe-order-header-wrap", { "align-items": "center", "display": "flex", "padding": "21px 20px 14px", }) CssApply(".cafe-order-header", { "flex-grow": "1", "font-size": "17px", "line-height": "21px", "margin": "0", "padding": "0", "text-transform": "uppercase", }) CssApply(".cafe-item-buy-button", { "background-color": "var(--accent-color)", "position": "absolute !important", "right": "0", "top": "0", "transition": "width var(--animation)", "width": "80px", }) CssApply(".cafe-item-incr-button", { "transition": "all var(--animation)", "width": "100%", "position": "absolute !important", "right": "0", "top": "0", }) CssApply(".cafe-item-decr-button", { "background-color": "#e64d44", "pointer-events": "none", "transform": "scale3d(0.9, 0.9, 1)", "transition": "transform var(--animation), visibility var(--animation)", "visibility": "hidden", }) CssApply(".cafe-status", { "position": "fixed", "bottom": "0", "left": "0", "right": "0", "display": "block", "width": "100%", "padding": "8px 20px", "display": "flex", "justify-content": "center", "align-items": "center", "border-radius": "0", "background-color": "#e64d44", "box-shadow": "0 var(--status-height) #e64d44", "color": "#fff", "height": "var(--status-height)", "transition": "transform var(--animation), visibility var(--animation)", "transform": "translateY(var(--status-height))", "box-sizing": "border-box", "pointer-events": "none", "visibility": "hidden", "z-index": "11", }) // .cafe-item-counter let StyleItemCounter = CssNew({ "display": "inline-block", "font-family": "var(--default-font)", "font-weight": "700", "font-size": "13px", "line-height": "18px", "height": "30px", "border-radius": "7px", "box-sizing": "border-box", "background-color": "var(--link-color)", "color": "#fff", "outline": "none", "border": "none", "white-space": "nowrap", "text-overflow": "ellipsis", "overflow": "hidden", "display": "flex", "justify-content": "center", "align-items": "center", }) // css` // .cafe-item-incr-button:active { // transform: scale(1.1); // } // ` // css` // .selected .cafe-item-incr-button { // width: 48%; // } // ` // css` // .cafe-item-decr-button { // position: relative; // width: 48%; // } // ` // css` // .selected .cafe-item-decr-button { // pointer-events: auto; // transform: scale3d(1, 1, 1); // visibility: visible; // transition: all 0.1s; // } // ` // css` // .cafe-item-decr-button:active { // transform: scale(1.1); // } // ` // css` // .cafe-item-incr-button:before, // .cafe-item-incr-button:after { // background-color: rgba(255, 255, 255, 0); // transition: background-color var(--animation); // } // ` // css` // .selected .cafe-item-incr-button:before, // .selected .cafe-item-incr-button:after { // background-color: #fff; // } // ` // css` // .cafe-item-incr-button:after { // width: 3px; // height: 14px; // } // ` // css` // .cafe-status.shown { // pointer-events: auto; // visibility: visible; // transform: translateY(0); // } // ` // css` // .cafe-order-overview { // font-family: var(--default-font); // background-color: var(--bg-color); // transition: opacity var(--page-animation); // } // ` // css` // .cafe-order-edit { // font-size: 15px; // line-height: 18px; // font-weight: 500; // color: var(--link-color); // } // ` // css` // .cafe-order-item { // display: flex; // padding: 5px 20px 5px 14px; // } // ` // css` // .cafe-order-item-label { // flex-grow: 1; // } // ` // css` // .cafe-order-item-photo { // text-align: center; // width: 50px; // height: 50px; // margin-right: 11px; // } // ` // css` // .cafe-order-item-photo .cafe-item-emoji { // font-size: 38px; // line-height: 50px; // } // ` // css` // .cafe-order-item-photo .cafe-item-lottie { // width: 40px; // margin: 4px 0; // } // ` // css` // .cafe-order-item-counter { // color: var(--link-color); // } // ` // css` // .cafe-order-item-description { // font-size: 14px; // line-height: 17px; // -webkit-font-smoothing: antialiased; // color: var(--page-hint-color); // padding: 2px 0; // } // ` // css` // .cafe-order-item-price { // font-size: 14px; // line-height: 17px; // font-weight: 500; // padding: 4px 0; // } // ` // css` // .cafe-text-field { // font-family: var(--default-font); // font-size: 17px; // line-height: 21px; // -webkit-font-smoothing: antialiased; // padding: 12px 20px 13px; // box-sizing: border-box; // display: block; // outline: none; // border: none; // border-radius: 0; // resize: none; // color: var(--text-color); // -webkit-user-select: auto; // user-select: auto; // cursor: auto; // width: 100%; // } // ` // css` // .cafe-text-field::-webkit-input-placeholder { // transition: color var(--animation); // color: var(--placeholder-color); // text-overflow: ellipsis; // } // ` // css` // .cafe-text-field::-moz-placeholder { // transition: color var(--animation); // color: var(--placeholder-color); // text-overflow: ellipsis; // } // ` // css` // .cafe-text-field:-ms-input-placeholder { // transition: color var(--animation); // color: var(--placeholder-color); // text-overflow: ellipsis; // } // ` // css` // .cafe-text-field::placeholder { // text-overflow: ellipsis; // } // ` // css` // .cafe-text-field-hint { // font-family: var(--default-font); // font-size: 14px; // line-height: 18px; // -webkit-font-smoothing: antialiased; // padding: 10px 20px 24px; // color: var(--hint-color); // } // ` let StyleButtonItemLabel = CssNew({ "display": "inline-block", "max-width": "100%", "overflow": "hidden", "text-overflow": "ellipsis", "vertical-align": "top", "position": "relative", "z-index": 1, }) // .cafe-item-incr-button .button-item-label CssPseudo(StyleButtonItemLabel, { "transition": "transform var(--animation)", }) // css` // .selected .cafe-item-incr-button .button-item-label { // transform: scale3d(0.4, 0, 1); // display: none; // } // ` // css` // .cafe-switch-block { // height: 32px; // padding: 10px 20px; // display: flex; // align-items: center; // justify-content: space-between; // } // ` // css` // .cafe-switch-label { // font-family: var(--default-font); // font-size: 17px; // } // ` // css` // .switch { // position: relative; // display: inline-block; // width: 60px; // height: 32px; // } // ` // css` // .switch input { // opacity: 0; // width: 0; // height: 0; // } // ` // css` // .slider { // position: absolute; // cursor: pointer; // top: 0; // left: 0; // right: 0; // bottom: 0; // background-color: var(--tg-theme-bg-color); // -webkit-transition: 0.2s; // transition: 0.2s; // } // ` // css` // .slider:before { // position: absolute; // content: ""; // height: 24px; // width: 24px; // left: 5px; // bottom: 4px; // background-color: var(--bg-color); // -webkit-transition: 0.2s; // transition: 0.2s; // } // ` // css` // input:checked + .slider { // background-color: var(--link-color); // } // ` // css` // input:focus + .slider { // box-shadow: 0 0 1px var(--link-color); // } // ` // css` // input:checked + .slider:before { // -webkit-transform: translateX(25px); // -ms-transform: translateX(25px); // transform: translateX(25px); // } // ` // css` // @keyframes rotate-circle { // from { // transform: rotateZ(-90deg); // } // to { // transform: rotateZ(630deg); // } // } // ` // css` // @keyframes resize-circle { // from { // stroke-dashoffset: 0; // } // to { // stroke-dashoffset: 12500px; // } // } // ` // css` // @keyframes badge-incr { // from, // to { // transform: scale3d(1, 1, 1); // } // 40% { // transform: scale3d(1.2, 1.2, 1); // } // } // ` // css` // @keyframes badge-incr2 { // from, // to { // transform: scale3d(1, 1, 1); // } // 40% { // transform: scale3d(1.2, 1.2, 1); // } // } // ` // css` // @keyframes badge-decr { // from, // to { // transform: scale3d(1, 1, 1); // } // 40% { // transform: scale3d(0.8, 0.8, 1); // } // } // ` // css` // @keyframes badge-decr2 { // from, // to { // transform: scale3d(1, 1, 1); // } // 40% { // transform: scale3d(0.8, 0.8, 1); // } // } // ` // css` // @keyframes badge-show { // from { // transform: scale3d(0.5, 0.5, 1); // opacity: 0; // visibility: hidden; // } // 30% { // transform: scale3d(1.2, 1.2, 1); // } // to { // transform: scale3d(1, 1, 1); // opacity: 1; // visibility: visible; // } // } // ` // css` // @keyframes badge-hide { // from { // transform: scale3d(1, 1, 1); // opacity: 1; // visibility: visible; // } // to { // transform: scale3d(0.5, 0.5, 1); // opacity: 0; // visibility: hidden; // } // } // ` let StyleLoader = CssNew({ "width": "4px", "color": "var(--link-color)", "aspect-ratio": 1, "box-shadow": ` 19px -19px 0 0px, 38px -19px 0 0px, 57px -19px 0 0px, 19px 0 0 5px, 38px 0 0 5px, 57px 0 0 5px, 19px 19px 0 0px, 38px 19px 0 0px, 57px 19px 0 0px`, "transform": "translateX(-38px)", "animation": "l26 2s infinite ease", }) CssAppend(` @keyframes l26 { 12.5% { box-shadow: 19px -19px 0 0px, 38px -19px 0 0px, 57px -19px 0 5px, 19px 0 0 5px, 38px 0 0 0px, 57px 0 0 5px, 19px 19px 0 0px, 38px 19px 0 0px, 57px 19px 0 0px; } 25% { box-shadow: 19px -19px 0 5px, 38px -19px 0 0px, 57px -19px 0 5px, 19px 0 0 0px, 38px 0 0 0px, 57px 0 0 0px, 19px 19px 0 0px, 38px 19px 0 5px, 57px 19px 0 0px; } 50% { box-shadow: 19px -19px 0 5px, 38px -19px 0 5px, 57px -19px 0 0px, 19px 0 0 0px, 38px 0 0 0px, 57px 0 0 0px, 19px 19px 0 0px, 38px 19px 0 0px, 57px 19px 0 5px; } 62.5% { box-shadow: 19px -19px 0 0px, 38px -19px 0 0px, 57px -19px 0 0px, 19px 0 0 5px, 38px 0 0 0px, 57px 0 0 0px, 19px 19px 0 0px, 38px 19px 0 5px, 57px 19px 0 5px; } 75% { box-shadow: 19px -19px 0 0px, 38px -19px 0 5px, 57px -19px 0 0px, 19px 0 0 0px, 38px 0 0 0px, 57px 0 0 5px, 19px 19px 0 0px, 38px 19px 0 0px, 57px 19px 0 5px; } 87.5% { box-shadow: 19px -19px 0 0px, 38px -19px 0 5px, 57px -19px 0 0px, 19px 0 0 0px, 38px 0 0 5px, 57px 0 0 0px, 19px 19px 0 5px, 38px 19px 0 0px, 57px 19px 0 0px; } } `) // css` // .gg-math-plus { // display: inline-block; // width: 2em; // height: 2em; // background-repeat: no-repeat; // background-size: 100% 100%; // background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='white' d='M19 12.998h-6v6h-2v-6H5v-2h6v-6h2v6h6z'/%3E%3C/svg%3E"); // } // ` // css` // .gg-math-minus { // display: inline-block; // width: 2em; // height: 2em; // background-repeat: no-repeat; // background-size: 100% 100%; // background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='white' d='M19 12.998H5v-2h14z'/%3E%3C/svg%3E"); // } // ` CssApplyPseudo(Img, "before", { "background-color": "var(--link-color)", "content": `""`, "display": "inline-block", "height": "100%", "mask-image": NoImageSvg, "mask-repeat": "no-repeat", "mask-size": "100% 100%", "opacity": "0.25", "width": "75%", }) let StyleButtonItemIcon = CssNew({ "display": "none", }) // css` // .selected .button-item-icon { // display: inline-block; // } // ` // css` // .checkcontainer { // display: block; // position: relative; // padding-left: 35px; // margin-bottom: 12px; // cursor: pointer; // font-size: 22px; // -webkit-user-select: none; // -moz-user-select: none; // -ms-user-select: none; // user-select: none; // } // ` // css` // .checkcontainer input { // position: absolute; // opacity: 0; // cursor: pointer; // height: 0; // width: 0; // } // ` // css` // .radiobtn { // position: absolute; // top: 0; // left: 0; // height: 25px; // width: 25px; // background-color: var(--block-bg-color); // border-radius: 50%; // } // ` // css` // .checkcontainer:hover input ~ .radiobtn { // background-color: var(--accent-color); // } // ` // css` // .checkcontainer input:checked ~ .radiobtn { // background-color: var(--link-color); // } // ` // css` // .radiobtn:after { // content: ""; // position: absolute; // display: none; // } // ` // css` // .checkcontainer input:checked ~ .radiobtn:after { // display: block; // } // ` // css` // .checkcontainer .radiobtn:after { // top: 9px; // left: 9px; // width: 8px; // height: 8px; // border-radius: 50%; // background: white; // } // ` let StyleBackButton = CssNew({ // "position": "absolute", // "left": "50%", // "top": "50%", // "transform": "translate(-50%, -50%)", "box-sizing": "border-box", "width": "95%", "background-color": "var(--button-color)", "border": "0", "outline": "border", // "margin": "1rem", "padding": "1rem", "font-size": "2rem", "font-family": "monospace", "box-shadow": "0px 5px 10px rgba(darken(dodgerblue, 40%))", "transition": "all .3s", "cursor": "pointer", "border-radius": "0px", "border-bottom": "4px solid lighten(gray, 70%)", "transform": "scale(0.95)", }) CssPseudo(StyleBackButton, "hover", { "box-shadow": "0px 15px 25px -5px rgba(darken(dodgerblue, 40%))", "transform": "scale(1.0)", }) CssPseudo(StyleBackButton, "active", { "box-shadow": "0px 4px 8px rgba(darken(dodgerblue, 30%))", "transform": "scale(0.9)", }) let { init, h, text } = SMVC function ChartLoadData(enqueue) { enqueue({ "Preloaded": true }) fetch( "/api/chart?" + new URLSearchParams({ UserId: LoginData.UserId, Token: Base64UrlEncodeArray(LoginData.Token), StoreId: LoginData.StoreId, }), ) .then((response) => response.json()) .then((data) => { enqueue({ Loaded: { ChartData: data } }) }) .catch(() => {}) } let ChartState = { Object: undefined, ChartData: undefined, PreparedChartData: undefined, Position: Number.MAX_SAFE_INTEGER, TimePeriod: "monthly", MaxPerPage: 8, } function ChartUpdate(s, msg) { if ("Preloaded" in msg) { } if ("Loaded" in msg) { let chartData = msg.Loaded.ChartData s.ChartData = chartData s.Position = Number.MAX_SAFE_INTEGER s.TimePeriod = "monthly" } if ("Monthly" in msg) { s.Position = Number.MAX_SAFE_INTEGER s.TimePeriod = "monthly" } if ("Weekly" in msg) { s.Position = Number.MAX_SAFE_INTEGER s.TimePeriod = "weekly" } if ("Daily" in msg) { s.Position = Number.MAX_SAFE_INTEGER s.TimePeriod = "daily" } if ("Prev" in msg) { s.Position -= 1 } if ("Next" in msg) { s.Position += 1 } let data if (s.ChartData) { if (s.TimePeriod == "monthly") { data = s.ChartData.Monthly } if (s.TimePeriod == "weekly") { data = s.ChartData.Weekly } if (s.TimePeriod == "daily") { data = s.ChartData.Daily } } if (!data) { data = [] } let dataLength = Object.keys(data).length let keys = Object.keys(data) let values = Object.values(data) let newKeys = keys.map((key) => { let date = new Date(Number(key) / 1_000_000) if (s.TimePeriod == "monthly") { return date.toLocaleString("ru-RU", { year: "2-digit", month: "long", }) } return date.toLocaleDateString("ru-RU") }) let newValues = values.map((value) => value / 100) let maxPosition = Math.floor(Math.max(dataLength - 1, 0) / s.MaxPerPage) function clamp(n, min, max) { return Math.max(min, Math.min(max, n)) } s.Position = clamp(s.Position, 0, maxPosition) let start = s.MaxPerPage * (s.Position + 0) let end = s.MaxPerPage * (s.Position + 1) start = Math.max(0, start) end = Math.max(0, end) start = Math.min(newKeys.length, start) end = Math.min(newKeys.length, end) newKeys = newKeys.slice(start, end) newValues = newValues.slice(start, end) s.PreparedChartData = { labels: newKeys, datasets: [ { label: "Turkish lira", data: newValues, }, ], } return s } let StyleChartButtonBlock = CssNew({ "display": "flex", "align-items": "center", "justify-content": "space-between", }) let StyleChartButton = CssNew({ "box-sizing": "border-box", "width": "95%", "background-color": "var(--button-color)", "border": "0", "outline": "border", "padding": "0.5rem", "font-size": "1rem", "box-shadow": "0px 5px 10px rgba(darken(dodgerblue, 40%))", "transition": "all .3s", "cursor": "pointer", "border-radius": "0px", "border-bottom": "4px solid lighten(gray, 70%)", "transform": "scale(0.95)", }) function ChartRender(s) { if (s.ChartData) { return [ h(Div, {}, [ h(Canvas, { id: "chart" }, []), h(Div, { class: StyleChartButtonBlock }, [ h( Button, { class: StyleChartButton, onclick: () => ({ Prev: true }), }, [text("Назад")], ), h( Button, { class: StyleChartButton, onclick: () => ({ Next: true }), }, [text("Вперёд")], ), ]), h(Div, { class: StyleChartButtonBlock }, [ h( Button, { class: StyleChartButton, onclick: () => ({ Daily: true }), }, [text("День")], ), h( Button, { class: StyleChartButton, onclick: () => ({ Weekly: true }), }, [text("Неделя")], ), h( Button, { class: StyleChartButton, onclick: () => ({ Monthly: true }), }, [text("Месяц")], ), ]), ]), ] } else { return [ h(Div, { class: StyleLoadingContainer }, [ h(Div, { class: StyleLoader }, [ // text("Загрузка данных...") ]), ]), ] } } function ChartPostRender(s) { let chartCtx = document.getElementById("chart") if (chartCtx && !s.Object) { s.Object = new Chart(chartCtx, { type: "bar", options: { plugins: { legend: { display: false, }, }, scales: { y: { suggestedMin: 0, }, }, }, }) } if (s.Object) { Chart.defaults.backgroundColor = Telegram.WebApp.themeParams.button_color Chart.defaults.borderColor = Telegram.WebApp.themeParams.hint_color Chart.defaults.color = Telegram.WebApp.themeParams.text_color s.Object.data = s.PreparedChartData s.Object.update() } return s } function FormatPrice(price) { return Math.trunc(price / 100) + " " + StoreData.CurrencySign } let StoreData = {} function StoreLoadData() { fetch("/api/store_info") .then((response) => response.json()) .then((data) => { StoreData = data Render() }) .catch(() => {}) } let ShoppingCard = [] function GetShoppingCardTotal() { let total = 0 // for (i in scores) { // } return total } function IsSelected(categoryId) { if (categoryId % 2 == 0) { return StyleStoreCategory } else { return StyleStoreCategorySelected } } function StoreCategory(categoryId, category) { return h( Div, { class: IsSelected(categoryId), onclick: function () { PushScreen(ScreenId.Category, { CategoryId: categoryId }) }, }, h( Div, { class: StyleStoreItemPhoto }, h(Img, { class: StyleStoreItemImage, src: StoreData.Categories[categoryId].FileUrl, }), ), h( Div, { class: StyleStoreItemLabel }, h(Span, { class: StyleStoreItemTitle }, category.Name), ), ) } function StoreItemOld() { return h( Div, { class: "cafe-item" }, h( StyleStoreItemCounter, { onupdate: function (_vnode) { if (!vnode.state.dirty) { return } vnode.state.dirty = false delta = vnode.state.count - vnode.state.oldCount var cur_anim_name = _vnode.dom.style["animation-name"] var anim_name = "" if (vnode.state.count > 0) { anim_name = "badge-show" if (delta > 0) { anim_name = "badge-incr" } else if (delta < 0) { anim_name = "badge-decr" } if ( (anim_name == "badge-incr" || anim_name == "badge-decr") && anim_name == cur_anim_name ) { anim_name += "2" } } else if (cur_anim_name != "") { anim_name = "badge-hide" } _vnode.dom.style["animation-name"] = anim_name }, }, vnode.state.count, ), h( ".cafe-item-photo", h( "picture.cafe-item-lottie", h(Img, { src: Data.general.items[vnode.state.id].file_url }), ), h( "span.cafe-item-new", { style: { "background-color": Data.general.items[vnode.state.id].badge_color, }, }, Data.general.items[vnode.state.id].badge_text, ), ), ) } function StoreItemOld(itemId, item) { return h( "div", { class: StyleStoreItem, onclick: function () { if (StoreData.Items[itemId].OptionsGroupIds) { PushScreen(ScreenId.Item, { ItemId: itemId }) } else { ShoppingCard.push(itemId) Render() } }, }, h( "div", { class: StyleStoreItemPhoto }, h("img", { class: StyleStoreItemImage, src: StoreData.Items[itemId].FileUrl, }), ), h( "div", { class: StyleStoreItemLabel }, h( "span", { class: "cafe-item-price" }, FormatPrice(StoreData.Items[itemId].Price), ), h( "span", { class: "cafe-item-title" }, StoreData.Items[itemId].Name, ), ), h( Div, { class: "cafe-item-buttons" }, h( Button, { class: "cafe-item-decr-button button-item", onclick: function () { ShoppingCard.pop() }, }, h(Span, { class: "gg-math-minus" }), ), h( Button, { class: "cafe-item-incr-button button-item" }, h(Span, { class: "button-item-icon gg-math-plus" }), h(Span, { class: StyleButtonItemLabel }, "В корзину"), ), ), ) } function StoreItemUpdate(itemEl, delta) { var price = +itemEl.data("item-price") var count = +itemEl.data("item-count") || 0 var counterEl = $(".js-item-counter", itemEl) counterEl.text(count ? count : 1) var isSelected = itemEl.hasClass("selected") var anim_name = isSelected ? delta > 0 ? "badge-incr" : count > 0 ? "badge-decr" : "badge-hide" : "badge-show" var cur_anim_name = counterEl.css("animation-name") if ( (anim_name == "badge-incr" || anim_name == "badge-decr") && anim_name == cur_anim_name ) { anim_name += "2" } counterEl.css("animation-name", anim_name) itemEl.toggleClass("selected", count > 0) var orderItemEl = Store.GetOrderItem(itemEl) var orderCounterEl = $(".js-order-item-counter", orderItemEl) orderCounterEl.text(count ? count : 1) orderItemEl.toggleClass("selected", count > 0) var orderPriceEl = $(".js-order-item-price", orderItemEl) var item_price = count * price orderPriceEl.text(Store.FormatPrice(item_price)) Store.UpdateTotalPrice() } function StoreItemLine(id) { return h( Div, { class: "cafe-order-item" }, h( Div, { class: "cafe-order-item-photo" }, h( // what is this tag??? Picture, { class: "cafe-item-lottie" }, h(Img, { src: Data.general.items[id].file_url }), ), ), h( Div, { class: "cafe-order-item-label" }, h( Div, { class: StyleItemTitle }, Data.general.items[id].name, " ", h( Span, { class: "cafe-order-item-counter" }, `${Data.ShoppingCard} шт`, ), ), h(Div, { class: "cafe-order-item-description" }, "test"), ), h( Div, { class: "cafe-order-item-price" }, FormatPrice(Data.general.items[id].price), ), ) } function StoreTakeout() { return h( Div, { class: "cafe-order-overview" }, h( Div, { class: ".cafe-block" }, h( { class: ".cafe-order-items" }, Object.entries(Data.general.items).map(function (d) { shopItemId = d[0] return h(StoreItemLine, { id: shopItemId }) }), ), ), h( ".cafe-switch-block", h(".cafe-switch-label", "Забрать на кассе"), h( ".cafe-switch", h( "label.switch", h("input", { type: "checkbox" }), h("span.slider"), ), ), ), h( ".cafe-switch-block", h(".cafe-switch-label", "Номер столика"), h("input", { class: StyleNumberInput }, { type: "text" }), ), h( ".cafe-text-field-wrap", h("textarea.cafe-text-field cafe-block", { rows: 1, placeholder: "Добавить комментарий…", style: "overflow: hidden visible; overflow-wrap: break-word;", oninput: function (_vnode) { autosize(this) }, }), ), ) } function StoreDelivery() { return h( "div.cafe-order-overview", h(".cafe-block"), h( ".cafe-switch-block", h(".cafe-switch-label", "Забрать на кассе"), h( ".cafe-switch", h( "label.switch", h("input", { type: "checkbox" }), h("span.slider"), ), ), ), h( ".cafe-switch-block", h(".cafe-switch-label", "Номер столика"), h("input", { class: StyleNumberInput }, { type: "text" }), ), h( ".cafe-text-field-wrap", h("textarea.cafe-text-field cafe-block", { rows: 1, placeholder: "Добавить комментарий…", style: "overflow: hidden visible; overflow-wrap: break-word;", oninput: function (_vnode) { autosize(this) }, }), ), ) } function StoreCashier() { return h( "div.cafe-order-overview", h(".cafe-block"), h( ".cafe-switch-block", h(".cafe-switch-label", "Наличные"), h( "label.checkcontainer", h("input", { type: "radio", name: "radio" }), h("span.radiobtn"), ), ), h( ".cafe-switch-block", h(".cafe-switch-label", "Безналичные"), h( "label.checkcontainer", h("input", { type: "radio", name: "radio" }), h("span.radiobtn"), ), ), h( ".cafe-switch-block", h(".cafe-switch-label", "Баланс"), h( "label.checkcontainer", h("input", { type: "radio", name: "radio" }), h("span.radiobtn"), ), ), h( ".cafe-switch-block", h(".cafe-switch-label", "Скидка"), h("input", { class: StyleNumberInput }, { type: "text" }), ), h( ".cafe-text-field-wrap", h("textarea.cafe-text-field cafe-block", { rows: 1, placeholder: "Добавить комментарий…", style: "overflow: hidden visible; overflow-wrap: break-word;", oninput: function (_vnode) { autosize(this) }, }), ), ) } function StoreOptions() { return h(".xxx") } function OldMain() { categoryId = "" len = NavigationState.ScreenStateStack.length if (len > 0) { categoryId = NavigationState.ScreenStateStack[len - 1].CategoryId } // if (NavigationState.ScreenStateStack.length > 0) { // Telegram.WebApp.BackButton.show() // } else { // Telegram.WebApp.BackButton.hide() // } UpdateMainButton() if (Data.error) { return h( "div", { class: StyleStatusWrap }, m(".cafe-status .shown", Data.error), ) } else if (Data.general.categories) { // return m(StoreTakeout); return m( ".cafe-items start", Object.entries(Data.general.categories).map(function (d) { shopCategoryId = d[0] parent_category_id = Data.general.categories[shopCategoryId] .parent_category_id || "" if (parent_category_id != categoryId) { return } return h(StoreCategory, { id: shopCategoryId }) }), Object.entries(Data.general.items).map(function (d) { shopItemId = d[0] if (Data.general.items[shopItemId].category_id != categoryId) { return } return h(StoreItemOld, { id: shopItemId }) }), ) } else { return h("div", { class: StyleLoadingContainer }, m(".loader")) } } function Main() { let currentScreen = GetCurrentScreen() switch (currentScreen.Id || ScreenId.Category) { case ScreenId.Unknown: break case ScreenId.Category: if (Object.keys(StoreData).length > 0) { let categories = [] let currentCategoryId = currentScreen.CategoryId for (let [_categoryId, category] of Object.entries( StoreData.Categories, )) { let categoryId = Number(_categoryId) if (category.ParentCategoryId != currentCategoryId) { continue } categories.push(StoreCategory(categoryId, category)) } let items = [] for (let [_itemId, item] of Object.entries(StoreData.Items)) { let itemId = Number(_itemId) if (item.CategoryId != currentCategoryId) { continue } items.push(StoreItemOld(itemId, item)) } return h( Div, { class: StyleStoreItems }, ...categories, ...items, ) } break } return h( "div", { class: StyleLoadingContainer }, h("div", { class: "loader" }), ) } let Utf8Encoder = new TextEncoder() let Utf8Decoder = new TextDecoder("utf-8") function Base64UrlEncodeString(s) { return Base64UrlEncodeArray(Utf8Encoder.encode(s)) } function Base64UrlDecodeString(s) { return Utf8Decoder.decode(Base64UrlDecodeArray(s)) } function Base64UrlEncodeArray(buffer) { return btoa( Array.from(new Uint8Array(buffer), (b) => String.fromCharCode(b)).join( "", ), ) .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/, "") } function Base64UrlDecodeArray(value) { let m = value.length % 4 return Uint8Array.from( atob( value .replace(/-/g, "+") .replace(/_/g, "/") .padEnd(value.length + (m === 0 ? 0 : 4 - m), "="), ), (c) => c.charCodeAt(0), ).buffer } let LoginData = {} function LoadLoginData() { let base64Decoded = window.location.search base64Decoded = base64Decoded.substring(1) let jsonDecoded try { jsonDecoded = Base64UrlDecodeString(base64Decoded) LoginData = JSON.parse(jsonDecoded) } catch (error) { return } } let NavigationState = { ScreenStack: [], } function RestoreNavigationState() { let base64Decoded = window.location.hash base64Decoded = base64Decoded.substring(1) let jsonDecoded try { jsonDecoded = Base64UrlDecodeString(base64Decoded) NavigationState = JSON.parse(jsonDecoded) } catch (error) { return } } function SaveNavigationState() { let jsonEncoded = JSON.stringify(NavigationState) let base64Encoded = Base64UrlEncodeString(jsonEncoded) window.history.pushState(null, "", "#" + base64Encoded) } let ScreenId = { Unknown: 0, Category: 1, Item: 2, } function PushScreen(id, data) { NavigationState.ScreenStack.push({ Id: id, ...data }) SaveNavigationState() window.scrollTo({ top: 0, behavior: "instant" }) Render() } function PopScreen() { NavigationState.ScreenStack.pop() SaveNavigationState() window.scrollTo({ top: 0, behavior: "instant" }) Render() } function FirstScreen() { NavigationState.ScreenStack = [] PushScreen({ id: ScreenId.Main }) } function GetCurrentScreen() { let length = NavigationState.ScreenStack.length if (length > 0) { return NavigationState.ScreenStack[length - 1] } return {} } let Page = { Store: 1, Chart: 2, } function Render() { let startTime = performance.now() let root = document.getElementById("app") TelegramUpdate() if (!LoginData.Page) { LoginData.Page = Page.Store } if (LoginData.Page == Page.Store) { // render(document.body, Main()) // // // StoreLoadData() } if (LoginData.Page == Page.Chart) { let { enqueue } = init( root, ChartState, ChartUpdate, ChartRender, ChartPostRender, ) ChartLoadData(enqueue) // render(document.body, ChartRender()) } // XXX // remove? // PostRender() let endTime = performance.now() let t = Math.round((endTime - startTime) * 1000) console.log(`redraw took ${t} microseconds`) } // XXX // remove? // function PostRender() { // switch (LoginData.Page) { // case Page.Store: // break // case Page.Chart: // ChartPostRender() // break // } // } function TelegramSetThemeClass() { // document.documentElement.className = Telegram.WebApp.colorScheme; } function TelegramUpdate() { Telegram.WebApp.isClosingConfirmationEnabled = !!ShoppingCard.length // if (NavigationState.ScreenStack.length > 0) { // Telegram.WebApp.BackButton.show() // } else { // Telegram.WebApp.BackButton.hide() // } if (ShoppingCard.length > 0) { if (false) { Telegram.WebApp.MainButton.setParams({ is_visible: true, has_shine_effect: true, }) } else { Telegram.WebApp.MainButton.setParams({ // is_visible: !!Data.CanPay, text: "Оплатить " + FormatPrice(GetShoppingCardTotal()), }).hideProgress() } } else { Telegram.WebApp.MainButton.setParams({ // is_visible: !!Data.CanPay, text: "Оформить заказ", }).hideProgress() } } function Init() { // console.log(Data.userId); // console.log(Data.token); // if (!Data.token) { // Data.isClosed = true; // Data.error = "Заведение временно закрыто"; // // ShowStatus("Заведение временно закрыто"); // return; // } // $(".js-item").each(function (i) { // if (i == 0) { // return; // } // var itemEl = $(this); // itemEl.hide(); // }); // $('.js-order-edit').on('click', Store.EditClicked); // $(".js-status").on("click", Store.StatusClicked); // Telegram.WebApp.MainButton.setParams({ // // text_color: '#fff', // shine_effect: true, // }).onClick(Store.MainBtnClicked); Telegram.WebApp.ready() Telegram.WebApp.setHeaderColor("bg_color") Telegram.WebApp.expand() Telegram.WebApp.onEvent("themeChanged", TelegramSetThemeClass) TelegramSetThemeClass() // document.documentElement.style = document.documentElement.style + variables; // document.head.appendChild(bodyStyle); // Telegram.WebApp.BackButton.onClick(function () { // PopScreen() // }) RestoreNavigationState() LoadLoginData() // ChartInit() } // addEventListener("popstate", (event) => { // PopScreen() // }) Init() Render()