From 4b0eb1956590966b0fd64409963cf3e09adcf06f Mon Sep 17 00:00:00 2001 From: Nick Lin Date: Sun, 7 May 2023 09:58:17 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=A1=20feat:=20tab=20views=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- figma/code.ts | 66 ++++------- figma/date-time.ts | 78 +++++++++++++ figma/numeric.ts | 45 ++++++++ index.html | 2 +- package.json | 4 + pnpm-lock.yaml | 86 ++++++++++++++ src/App.vue | 182 +++++------------------------- src/components/AppTooltip.vue | 18 +++ src/components/DatetimeString.vue | 115 +++++++++++++++++++ src/components/HelloWorld.vue | 13 --- src/components/NumericString.vue | 154 +++++++++++++++++++++++++ src/components/OrderTypes.vue | 52 +++++++++ src/main.ts | 13 ++- src/style.css | 24 ++++ src/types/DateFormats.ts | 14 +++ src/types/Order.ts | 5 + uno.config.ts | 1 + v-calendar.d.ts | 1 + 18 files changed, 659 insertions(+), 214 deletions(-) create mode 100644 figma/date-time.ts create mode 100644 figma/numeric.ts create mode 100644 src/components/AppTooltip.vue create mode 100644 src/components/DatetimeString.vue delete mode 100644 src/components/HelloWorld.vue create mode 100644 src/components/NumericString.vue create mode 100644 src/components/OrderTypes.vue create mode 100644 src/types/DateFormats.ts create mode 100644 src/types/Order.ts create mode 100644 v-calendar.d.ts diff --git a/figma/code.ts b/figma/code.ts index cfb0b04..719f160 100644 --- a/figma/code.ts +++ b/figma/code.ts @@ -1,50 +1,30 @@ -figma.showUI(__html__, { - width: 420, - height: 220 -}); +/// +import handleDateTimeTab from './date-time' +import handleNumericTab from './numeric' -function setBaseNumber (base = '0', orderType = 'asc', length) { - const baseNumber = +base - let stack = 0 - let res - return () => { - if (orderType === 'asc') { - res = baseNumber + stack - } else if (orderType === 'desc') { - res = baseNumber - stack - } else { - res = Math.floor(Math.random() * Math.pow(10, length)) - } - stack += 1 - return res.toString().padStart(length, '0') - } +enum TabHeight { + numeric = 220, + dateTime = 550 } -figma.ui.onmessage = async payload => { - const { prefix, baseNumber, orderType, action, isReverse } = payload - const nodes = figma.currentPage.selection - const nodesLength = nodes.length - if (action === 'generate') { - const isInvalid = nodes.some(node => node.type !== 'TEXT'); - if (isInvalid) { - return "Select a single text node." - } - - const genNextNumber = setBaseNumber(baseNumber, orderType, baseNumber.length) +figma.showUI(__html__, { + width: 350, + height: TabHeight.numeric +}) - for (let i = 0; i < nodesLength; i += 1) { - //! Noted: order of page selections are not reliable. - await figma.loadFontAsync(nodes[i].fontName) - const result = isReverse - ? `${genNextNumber()}${prefix}` - : `${prefix}${genNextNumber()}` - nodes[i].characters = result - nodes[i].name = result +figma.ui.onmessage = payload => { + // TODO: should separate tabChange and others + if (payload.action === 'tabChange') { + const height = TabHeight[payload.tab] as unknown as number + figma.ui.resize(350, height) + } else { + const tabHandlers = { + numeric: handleNumericTab, + dateTime: handleDateTimeTab + } + const tabHandler = tabHandlers[payload.tab] + if (tabHandler) { + tabHandler(payload) } - } - - // on cancel - if (action === 'cancel') { - figma.closePlugin(); } }; \ No newline at end of file diff --git a/figma/date-time.ts b/figma/date-time.ts new file mode 100644 index 0000000..6b391e3 --- /dev/null +++ b/figma/date-time.ts @@ -0,0 +1,78 @@ +function addDays (date: Date, days: number): Date { + const result = new Date(date) + result.setDate(result.getDate() + days) + return result +} + +function formatDate(date: Date, format: string): string { + const month = date.getMonth() + 1 + const day = date.getDate() + const year = date.getFullYear() + const weekday = date.getDay() + const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] + + const patterns = { + 'YYYY-MM-DD': `${year}-${(month).toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`, + 'MM-DD-YYYY': `${(month).toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}-${year}`, + 'MM/DD/YYYY': `${month.toString().padStart(2, '0')}/${day.toString().padStart(2, '0')}/${year}`, + 'MMMM DD, YYYY': `${months[date.getMonth()]} ${day}, ${year}`, + 'MMMM DD, YYYY h:mm A': `${months[date.getMonth()]} ${day}, ${year} ${date.toLocaleTimeString('en-US', {hour: 'numeric', minute: 'numeric', hour12: true})}`, + 'dddd, MMMM DD, YYYY h:mm A': `${days[weekday]}, ${months[date.getMonth()]} ${day}, ${year} ${date.toLocaleTimeString('en-US', {hour: 'numeric', minute: 'numeric', hour12: true})}`, + 'M/D/YYYY': `${month}/${day}/${year}`, + 'MMM D, YYYY': `${months[date.getMonth()].slice(0, 3)} ${day}, ${year}`, + 'MMM D, YYYY h:mm A': `${months[date.getMonth()].slice(0, 3)} ${day}, ${year} ${date.toLocaleTimeString('en-US', {hour: 'numeric', minute: 'numeric', hour12: true})}`, + 'ddd, MMM D, YYYY h:mm A': `${days[weekday].slice(0, 3)}, ${months[date.getMonth()].slice(0, 3)} ${date.getDate()}, ${date.getFullYear()} ${date.toLocaleTimeString('en-US', {hour: 'numeric', minute: 'numeric', hour12: true})}` + } + + if (patterns[format]) { + return patterns[format] + } else { + throw new Error(`Unsupported format: ${format}`) + } +} + +function setBaseDate (base = '', orderType = 'asc', format) { + const baseDate = new Date(base) + let stack = 0 + let res + return () => { + if (orderType === 'asc') { + res = addDays(baseDate, stack) + } else if (orderType === 'desc') { + res = addDays(baseDate, -stack) + } + stack += 1 + return formatDate(res, format) + } +} + +export default async function handleDateTimeTab (payload) { + const { + action, + orderType, + selectedDate, + format + } = payload + const nodes = figma.currentPage.selection + const nodesLength = nodes.length + if (action === 'generate') { + const isInvalid = nodes.some(node => node.type !== 'TEXT'); + if (isInvalid) { + return "Select a single text node." + } + + const genNextDate = setBaseDate(selectedDate, orderType, format) + + for (let i = 0; i < nodesLength; i += 1) { + //! Noted: order of page selections are not reliable. + await figma.loadFontAsync(nodes[i].fontName) + nodes[i].characters = genNextDate() + } + } + + // on cancel + if (action === 'cancel') { + figma.closePlugin(); + } +} \ No newline at end of file diff --git a/figma/numeric.ts b/figma/numeric.ts new file mode 100644 index 0000000..9317ff0 --- /dev/null +++ b/figma/numeric.ts @@ -0,0 +1,45 @@ +function setBaseNumber (base = '0', orderType = 'asc', length) { + const baseNumber = +base + let stack = 0 + let res + return () => { + if (orderType === 'asc') { + res = baseNumber + stack + } else if (orderType === 'desc') { + res = baseNumber - stack + } else { + res = Math.floor(Math.random() * Math.pow(10, length)) + } + stack += 1 + return res.toString().padStart(length, '0') + } +} + +export default async function handleNumericTab (payload) { + const { prefix, baseNumber, orderType, action, isReverse } = payload + const nodes = figma.currentPage.selection + const nodesLength = nodes.length + if (action === 'generate') { + const isInvalid = nodes.some(node => node.type !== 'TEXT'); + if (isInvalid) { + return 'Select a single text node.' + } + + const genNextNumber = setBaseNumber(baseNumber, orderType, baseNumber.length) + + for (let i = 0; i < nodesLength; i += 1) { + //! Noted: order of page selections are not reliable. + await figma.loadFontAsync(nodes[i].fontName) + const result = isReverse + ? `${genNextNumber()}${prefix}` + : `${prefix}${genNextNumber()}` + nodes[i].characters = result + nodes[i].name = result + } + } + + // on cancel + if (action === 'cancel') { + figma.closePlugin(); + } +} \ No newline at end of file diff --git a/index.html b/index.html index 76216f3..4d97128 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ - +
diff --git a/package.json b/package.json index 1671a72..c71ebe8 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,10 @@ "preview": "vite preview" }, "dependencies": { + "@popperjs/core": "^2.11.7", + "dayjs": "^1.11.7", + "mixpanel-figma": "^2.0.1", + "v-calendar": "^3.0.3", "vue": "^3.2.47" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2714ab4..6a1e22f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,6 +1,18 @@ lockfileVersion: '6.0' dependencies: + '@popperjs/core': + specifier: ^2.11.7 + version: 2.11.7 + dayjs: + specifier: ^1.11.7 + version: 1.11.7 + mixpanel-figma: + specifier: ^2.0.1 + version: 2.0.1 + v-calendar: + specifier: ^3.0.3 + version: 3.0.3(@popperjs/core@2.11.7)(vue@3.2.47) vue: specifier: ^3.2.47 version: 3.2.47 @@ -71,6 +83,13 @@ packages: dependencies: '@babel/types': 7.21.4 + /@babel/runtime@7.21.5: + resolution: {integrity: sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.11 + dev: false + /@babel/types@7.21.4: resolution: {integrity: sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==} engines: {node: '>=6.9.0'} @@ -372,6 +391,10 @@ packages: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} dev: true + /@popperjs/core@2.11.7: + resolution: {integrity: sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==} + dev: false + /@rollup/plugin-node-resolve@13.3.0(rollup@2.79.1): resolution: {integrity: sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==} engines: {node: '>= 10.0.0'} @@ -430,10 +453,18 @@ packages: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} dev: true + /@types/lodash@4.14.194: + resolution: {integrity: sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==} + dev: false + /@types/node@18.16.2: resolution: {integrity: sha512-GQW/JL/5Fz/0I8RpeBG9lKp0+aNcXEaVL71c0D2Q0QHDTFvlYKT7an0onCUXj85anv7b4/WesqdfchLc0jtsCg==} dev: true + /@types/resize-observer-browser@0.1.7: + resolution: {integrity: sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg==} + dev: false + /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: @@ -866,6 +897,25 @@ packages: /csstype@2.6.21: resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} + /date-fns-tz@1.3.8(date-fns@2.30.0): + resolution: {integrity: sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==} + peerDependencies: + date-fns: '>=2.0.0' + dependencies: + date-fns: 2.30.0 + dev: false + + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.21.5 + dev: false + + /dayjs@1.11.7: + resolution: {integrity: sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==} + dev: false + /de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} dev: true @@ -1329,6 +1379,10 @@ packages: p-locate: 5.0.0 dev: true + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -1381,6 +1435,10 @@ packages: brace-expansion: 2.0.1 dev: true + /mixpanel-figma@2.0.1: + resolution: {integrity: sha512-p+xWU/dA04IqpjEOzdf3eZGCh3yWCZSc5SsmW2WZU4zTFh4p2h6OJmt7pqozYlXjY4p3+7BXmRFdoAand3uArw==} + dev: false + /mrmime@1.0.1: resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} engines: {node: '>=10'} @@ -1493,6 +1551,10 @@ packages: picomatch: 2.3.1 dev: true + /regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + dev: false + /resolve@1.22.2: resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} hasBin: true @@ -1679,6 +1741,22 @@ packages: - vite dev: true + /v-calendar@3.0.3(@popperjs/core@2.11.7)(vue@3.2.47): + resolution: {integrity: sha512-Skpp/nMoFqFadm94aWj0oOfazoux5T5Ug3/pbRbdolkoDrnVcL7Ronw1/SGFRUPGOwnLdYwhKPhrhSE1segW6w==} + peerDependencies: + '@popperjs/core': ^2.0.0 + vue: ^3.2.0 + dependencies: + '@popperjs/core': 2.11.7 + '@types/lodash': 4.14.194 + '@types/resize-observer-browser': 0.1.7 + date-fns: 2.30.0 + date-fns-tz: 1.3.8(date-fns@2.30.0) + lodash: 4.17.21 + vue: 3.2.47 + vue-screen-utils: 1.0.0-beta.13(vue@3.2.47) + dev: false + /vite-plugin-singlefile@0.7.1(vite@4.3.2): resolution: {integrity: sha512-DyiIJ5bg9UHC2o4/GtJV1rNZ0M3vu4hMovE8RMmPVfW5vyDEWkuwiX+eiVBdMpTWHocOJcXvtkcCLfJCLz2Rmw==} peerDependencies: @@ -1726,6 +1804,14 @@ packages: fsevents: 2.3.2 dev: true + /vue-screen-utils@1.0.0-beta.13(vue@3.2.47): + resolution: {integrity: sha512-EJ/8TANKhFj+LefDuOvZykwMr3rrLFPLNb++lNBqPOpVigT2ActRg6icH9RFQVm4nHwlHIHSGm5OY/Clar9yIg==} + peerDependencies: + vue: ^3.2.0 + dependencies: + vue: 3.2.47 + dev: false + /vue-template-compiler@2.7.14: resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==} dependencies: diff --git a/src/App.vue b/src/App.vue index b54a444..1098159 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,179 +1,49 @@ \ No newline at end of file diff --git a/src/components/AppTooltip.vue b/src/components/AppTooltip.vue new file mode 100644 index 0000000..ba38702 --- /dev/null +++ b/src/components/AppTooltip.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/components/DatetimeString.vue b/src/components/DatetimeString.vue new file mode 100644 index 0000000..20ec320 --- /dev/null +++ b/src/components/DatetimeString.vue @@ -0,0 +1,115 @@ + + + diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue deleted file mode 100644 index 2f5239e..0000000 --- a/src/components/HelloWorld.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/src/components/NumericString.vue b/src/components/NumericString.vue new file mode 100644 index 0000000..b98a1e8 --- /dev/null +++ b/src/components/NumericString.vue @@ -0,0 +1,154 @@ + + + diff --git a/src/components/OrderTypes.vue b/src/components/OrderTypes.vue new file mode 100644 index 0000000..858adc9 --- /dev/null +++ b/src/components/OrderTypes.vue @@ -0,0 +1,52 @@ + + + diff --git a/src/main.ts b/src/main.ts index 3221937..74f7844 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,5 +2,16 @@ import { createApp } from 'vue' import './style.css' import 'virtual:uno.css' import App from './App.vue' +// @ts-ignore +import { setupCalendar } from 'v-calendar' +// @ts-ignore +import * as mixpanel from 'mixpanel-figma' -createApp(App).mount('#app') +mixpanel.init('e497b74427cbb25c36631b7ca1cecf40', { + debug: true, + disable_cookie: true, + disable_persistence: true +}) + +const app = createApp(App) +app.use(setupCalendar, {}).mount('#app') diff --git a/src/style.css b/src/style.css index 4a5e4b8..1dd207c 100644 --- a/src/style.css +++ b/src/style.css @@ -4,5 +4,29 @@ html, body { * { box-sizing: border-box; + font-family: 'Inter', sans-serif; font-family: 'Roboto Condensed', sans-serif; } + +.vc-slope-primary { + --vc-accent-50: ##fdf4f4; + --vc-accent-100: #FFDAD8; + --vc-accent-200: #F3AAA5; + --vc-accent-300: #F3AAA5; + --vc-accent-400: #f19f9a; + --vc-accent-500: #ED8078; + --vc-accent-600: #eb756e; + --vc-accent-700: #eb756e; + --vc-accent-800: #e86057; + --vc-accent-900: #E6554C; +} + +::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ + opacity: .5; +} +:-ms-input-placeholder { /* Internet Explorer 10-11 */ + opacity: .5; +} +::-ms-input-placeholder { /* Microsoft Edge */ + opacity: .5; +} diff --git a/src/types/DateFormats.ts b/src/types/DateFormats.ts new file mode 100644 index 0000000..6cc46f0 --- /dev/null +++ b/src/types/DateFormats.ts @@ -0,0 +1,14 @@ +export enum DateFormats { + // custom + dL = 'YYYY-MM-DD', + Ld = 'MM-DD-YYYY', + // common + L = 'MM/DD/YYYY', + LL = 'MMMM DD, YYYY', + LLL = 'MMMM DD, YYYY h:mm A', + LLLL = 'dddd, MMMM DD, YYYY h:mm A', + l = 'M/D/YYYY', + ll = 'MMM D, YYYY', + lll = 'MMM D, YYYY h:mm A', + llll = 'ddd, MMM D, YYYY h:mm A' +} diff --git a/src/types/Order.ts b/src/types/Order.ts new file mode 100644 index 0000000..d1560d5 --- /dev/null +++ b/src/types/Order.ts @@ -0,0 +1,5 @@ +export enum ORDER { + ASC = 'asc', + DESC = 'desc', + RANDOM = 'random' +} diff --git a/uno.config.ts b/uno.config.ts index 128090b..04bc9ac 100644 --- a/uno.config.ts +++ b/uno.config.ts @@ -12,6 +12,7 @@ export default defineConfig({ ], theme: { colors: { + primaryLightest: '#FFDAD8', primary: '#E6554C', secondary: '#ED8078' } diff --git a/v-calendar.d.ts b/v-calendar.d.ts new file mode 100644 index 0000000..c311416 --- /dev/null +++ b/v-calendar.d.ts @@ -0,0 +1 @@ +declare module 'v-calendar'