├── .browserslistrc ├── postcss.config.js ├── docs ├── favicon.ico ├── assets │ ├── getting-started-2he4Cxjr.css │ ├── materialdesignicons-webfont-B7mPwVP_.ttf │ ├── materialdesignicons-webfont-CSr8KVlo.eot │ ├── materialdesignicons-webfont-Dp5v-WZN.woff2 │ ├── materialdesignicons-webfont-PXm3-2wK.woff │ ├── date-prototypes-BljSbJCC.css │ ├── dom-events-BAj3WMub.css │ ├── date-and-time-DndIvGUZ.css │ ├── 404-CXnXg_9k.js │ ├── api-Db6GCvAy.css │ ├── isolated-test-view-BfiHdRV7.css │ ├── ja-B8msjcsp.js │ ├── ko-AocIJJnA.js │ ├── zh-cn-C_KCVIlc.js │ ├── zh-hk-D2YpcFnT.js │ ├── ky-BD3JJAAP.js │ ├── uz-cryl-CHkFNWUU.js │ ├── uz-7_H-3_ey.js │ ├── kaa-DaMxLk9y.js │ ├── tr-C8S6NMuF.js │ ├── bn-0ENUmuHY.js │ ├── bg-B9bio1qW.js │ ├── cs-BKu9qpIc.js │ ├── en-gb-BN6UPcbr.js │ ├── no-BGhZJ8qx.js │ ├── sl-BREPxRb5.js │ ├── sr-Tj68iq_e.js │ ├── sv-DytNY891.js │ ├── bs-LrTpqKaV.js │ ├── da-Bzq3aJPY.js │ ├── de-DxvdjJyS.js │ ├── id-C4V9iQKh.js │ ├── sk-DWxkOgqG.js │ ├── es-B09aix2b.js │ ├── mn-BJnxERff.js │ ├── et-BN0u-wFS.js │ ├── fr-GKuwB6u6.js │ ├── it-BYE8Pv4L.js │ ├── hr-DzfVcLED.js │ ├── nl-CF0acleB.js │ ├── pl-CbNmkzff.js │ ├── ro-jPnrebdN.js │ ├── hu-D5f_-VfF.js │ ├── is-BWBbdtI7.js │ ├── ka-DQi2yEGJ.js │ ├── pt-br-CNJ2qZf8.js │ ├── pt-pt-CNJ2qZf8.js │ ├── fi-C3bvOL2F.js │ ├── view-CH4fUjt-.css │ ├── he-BHiXGmJj.js │ ├── lt-CSQtJejA.js │ ├── sq-txfbjBxq.js │ ├── kk-4mxrIsUV.js │ ├── ru-Dg56i-yU.js │ ├── ar-BgL0e-US.js │ ├── fa-CpAJGCrY.js │ ├── uk-Ca_EQAdD.js │ ├── vi-0TvgrJx7.js │ ├── ca-BNzwY8AQ.js │ ├── introduction-3QtqfCXC.css │ ├── el-BijjcpDW.js │ ├── customization-5ve9KxJN.css │ ├── index-BnYfN0Uw.js │ ├── index-Vt70yi7W.css │ ├── road-map-DJuegIkf.css │ ├── playground-CAP3nagG.css │ ├── schedules-BjW2sphf.css │ ├── calendar-events-interactions-t-zWtkbW.css │ ├── release-notes-DcMJcxtH.css │ ├── isolated-test-view-BgMJ4XGg.js │ └── road-map-ObK9ptzE.js ├── images │ ├── vue-cal-5.webp │ ├── click-and-drag.webp │ └── calendar-events-display-overlapping-events.webp ├── 404.html ├── ghspa.js └── index.html ├── public ├── favicon.ico ├── images │ ├── vue-cal-5.webp │ ├── click-and-drag.webp │ └── calendar-events-display-overlapping-events.webp ├── 404.html └── ghspa.js ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.yml │ └── bug_report.yml ├── src ├── scss │ ├── index.scss │ ├── _variables.scss │ ├── _dark-theme.scss │ └── _typography.scss ├── documentation │ ├── 404.vue │ ├── components │ │ ├── issue-link.vue │ │ ├── title-link.vue │ │ ├── alert.vue │ │ └── example.vue │ └── examples │ │ └── index.vue ├── app.vue ├── vue-cal │ ├── i18n │ │ ├── ko.json │ │ ├── ja.json │ │ ├── zh-cn.json │ │ ├── zh-hk.json │ │ ├── ky.json │ │ ├── uz-cryl.json │ │ ├── uz.json │ │ ├── kaa.json │ │ ├── tr.json │ │ ├── bn.json │ │ ├── cs.json │ │ ├── no.json │ │ ├── sv.json │ │ ├── bg.json │ │ ├── sk.json │ │ ├── sl.json │ │ ├── da.json │ │ ├── de.json │ │ ├── en-gb.json │ │ ├── es.json │ │ ├── sr.json │ │ ├── bs.json │ │ ├── id.json │ │ ├── mn.json │ │ ├── ro.json │ │ ├── et.json │ │ ├── fr.json │ │ ├── hu.json │ │ ├── it.json │ │ ├── nl.json │ │ ├── pl.json │ │ ├── hr.json │ │ ├── is.json │ │ ├── ka.json │ │ ├── pt-br.json │ │ ├── pt-pt.json │ │ ├── en-us.json │ │ ├── fi.json │ │ ├── he.json │ │ ├── sq.json │ │ ├── kk.json │ │ ├── fa.json │ │ ├── ar.json │ │ ├── lt.json │ │ ├── ru.json │ │ ├── uk.json │ │ ├── vi.json │ │ ├── ca.json │ │ └── el.json │ ├── index.js │ ├── utils │ │ └── conversions.js │ ├── core │ │ └── index.js │ ├── index.scss │ └── components │ │ ├── time-column.vue │ │ └── header.vue ├── store.js ├── main.js └── router │ └── index.js ├── .editorconfig ├── .gitignore ├── dist └── i18n │ ├── ja.js │ ├── ko.js │ ├── zh-cn.js │ ├── zh-hk.js │ ├── bn.js │ ├── tr.js │ ├── bg.js │ ├── cs.js │ ├── no.js │ ├── sl.js │ ├── sr.js │ ├── sv.js │ ├── de.js │ ├── en-gb.js │ ├── id.js │ ├── sk.js │ ├── bs.js │ ├── da.js │ ├── es.js │ ├── et.js │ ├── fr.js │ ├── mn.js │ ├── it.js │ ├── hr.js │ ├── nl.js │ ├── ro.js │ ├── hu.js │ ├── is.js │ ├── pl.js │ ├── ka.js │ ├── pt-br.js │ ├── pt-pt.js │ ├── fi.js │ ├── lt.js │ ├── sq.js │ ├── he.js │ ├── ru.js │ ├── uk.js │ ├── vi.js │ ├── ca.js │ ├── ar.js │ ├── fa.js │ └── el.js ├── LICENSE ├── index.html ├── biome.json ├── vite.config.js └── package.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 3 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export const plugins = { 2 | autoprefixer: {} 3 | } 4 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: antoniandre 4 | -------------------------------------------------------------------------------- /docs/assets/getting-started-2he4Cxjr.css: -------------------------------------------------------------------------------- 1 | .main--getting-started .w-tabs__content-wrap{flex-grow:1} 2 | -------------------------------------------------------------------------------- /docs/images/vue-cal-5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/docs/images/vue-cal-5.webp -------------------------------------------------------------------------------- /public/images/vue-cal-5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/public/images/vue-cal-5.webp -------------------------------------------------------------------------------- /docs/images/click-and-drag.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/docs/images/click-and-drag.webp -------------------------------------------------------------------------------- /public/images/click-and-drag.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/public/images/click-and-drag.webp -------------------------------------------------------------------------------- /src/scss/index.scss: -------------------------------------------------------------------------------- 1 | @forward './variables'; 2 | @forward './base'; 3 | @forward './typography'; 4 | @forward './dark-theme'; 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /docs/assets/materialdesignicons-webfont-B7mPwVP_.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/docs/assets/materialdesignicons-webfont-B7mPwVP_.ttf -------------------------------------------------------------------------------- /docs/assets/materialdesignicons-webfont-CSr8KVlo.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/docs/assets/materialdesignicons-webfont-CSr8KVlo.eot -------------------------------------------------------------------------------- /docs/assets/materialdesignicons-webfont-Dp5v-WZN.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/docs/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 -------------------------------------------------------------------------------- /docs/assets/materialdesignicons-webfont-PXm3-2wK.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/docs/assets/materialdesignicons-webfont-PXm3-2wK.woff -------------------------------------------------------------------------------- /docs/images/calendar-events-display-overlapping-events.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/docs/images/calendar-events-display-overlapping-events.webp -------------------------------------------------------------------------------- /public/images/calendar-events-display-overlapping-events.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antoniandre/vue-cal/HEAD/public/images/calendar-events-display-overlapping-events.webp -------------------------------------------------------------------------------- /src/documentation/404.vue: -------------------------------------------------------------------------------- 1 | 2 | title-link.mt2(h1 anchor="not-found") 404 - Not found 3 | p The page you are looking for was not found on the server. 4 | 5 | -------------------------------------------------------------------------------- /src/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | // Breakpoints. 2 | // -------------------------------------------------------- 3 | $xxs: 450px; 4 | $xs: 600px; 5 | $sm: 900px; 6 | $md: 1200px; 7 | $lg: 1400px; 8 | $xl: 1700px; 9 | 10 | $page-max-width: 1600px; 11 | $container-padding-md: 20px; 12 | $container-padding-sm: 12px; 13 | -------------------------------------------------------------------------------- /docs/assets/date-prototypes-BljSbJCC.css: -------------------------------------------------------------------------------- 1 | .main--date-prototypes .root-accordion>.w-accordion__item>.w-accordion__item-title{border-bottom:1px solid color-mix(in srgb,var(--w-contrast-bg-color) 12%,transparent)}.main--date-prototypes .root-accordion>.w-accordion__item>.w-accordion__item-title>.w-accordion__expand-icon{margin-left:-12px} 2 | -------------------------------------------------------------------------------- /docs/assets/dom-events-BAj3WMub.css: -------------------------------------------------------------------------------- 1 | .main--examples-dom-events .logs-box{background-color:color-mix(in srgb,var(--w-contrast-bg-color) 5%,var(--w-base-bg-color))}.main--examples-dom-events .logs-box .scrollable{border:none;overflow:auto;margin-top:2px;padding-bottom:4px}.main--examples-dom-events .logs-box .scrollable:before{display:none} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .cursorrc 26 | -------------------------------------------------------------------------------- /docs/assets/date-and-time-DndIvGUZ.css: -------------------------------------------------------------------------------- 1 | .example--today-current-time .vuecal__now-line{border-color:#06c}.example--disable-days .vuecal__cell--disabled{text-decoration:line-through;color:#bbb}.example--min-max-dates .vuecal__cell--disabled .vuecal__cell-date{text-decoration:line-through;opacity:1}.example--min-max-dates .vuecal__cell--before-min{color:#b6d6c7}.example--min-max-dates .vuecal__cell--after-max{color:#008b8b} 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions & Discussions 4 | url: https://github.com/antoniandre/vue-cal/discussions 5 | about: Use GitHub discussions for message-board style questions and discussions. 6 | - name: Sponsor 7 | url: https://github.com/sponsors/antoniandre 8 | about: Love Vue Cal? Please consider supporting us via Github Sponsor. 9 | -------------------------------------------------------------------------------- /src/documentation/components/issue-link.vue: -------------------------------------------------------------------------------- 1 | 2 | a(:href="`https://github.com/antoniandre/vue-cal/issues/${number}`" target="_blank" v-text="`#${number}`") 3 | 4 | 5 | 14 | -------------------------------------------------------------------------------- /docs/assets/404-CXnXg_9k.js: -------------------------------------------------------------------------------- 1 | import{b as t,f as n,e as a,w as r,h as s,a as l,F as d,d as f,v as c}from"./index-7bpj6pqg.js";function u(m,e){const o=l("title-link");return f(),t(d,null,[n(o,{class:"mt2",h1:"",anchor:"not-found"},{default:r(()=>[...e[0]||(e[0]=[s("404 - Not found",-1)])]),_:1}),e[1]||(e[1]=a("p",null,"The page you are looking for was not found on the server.",-1))],64)}const i={},_=c(i,[["render",u]]);export{_ as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/api-Db6GCvAy.css: -------------------------------------------------------------------------------- 1 | .main--api .w-accordion__item-title{flex-wrap:wrap}.main--api .w-accordion__item-title code,.main--api .w-accordion__item-title .code{background-color:transparent;padding:0}.main--api .type{margin-left:2px;color:#33c;font:600 .8em monospace;letter-spacing:-.5px;word-spacing:-3px}.main--api .default{color:#df5151}[data-theme=dark] .main--api .type{color:#e67ad2}[data-theme=dark] .main--api .default{color:#adcfa4} 2 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |
8 | * @link http://websemantics.ca
9 | * @author Adnan M.Sagar, PhD.
10 | *
11 | * @param {Object} l, the document current location
12 | */
13 | ;(function(l) {
14 | var repo = '/' + l.pathname.split('/')[1]
15 |
16 | /* redirect all 404 trafic to index.html */
17 | function redirect() {
18 | l.replace(l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') + repo + '/?' +
19 | (l.pathname ? 'p=' + l.pathname.replace(/&/g, '~and~').replace(repo, '') : '') +
20 | (l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
21 | (l.hash))
22 | }
23 |
24 | /* resolve 404 redirects into internal routes */
25 | function resolve() {
26 | if (l.search) {
27 | var q = {}
28 | l.search.slice(1).split('&').forEach(function(v) {
29 | var a = v.split('=')
30 | q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&')
31 | })
32 | if (q.p !== undefined) {
33 | window.history.replaceState(null, null,
34 | repo + (q.p || '') +
35 | (q.q ? ('?' + q.q) : '') +
36 | l.hash
37 | )
38 | }
39 | }
40 | }
41 |
42 | /* if current document is 404 page page, redirect to index.html otherwise resolve */
43 | document.title === '404' ? redirect() : resolve()
44 | }(window.location))
45 |
--------------------------------------------------------------------------------
/public/ghspa.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Easy way to enable Single Page Applications for GitHub Pages
3 | *
4 | * This project was released under MIT license.
5 | *
6 | * @link https://github.com/rafrex/spa-github-pages
7 | * @author Rafael Pedicini
8 | * @link http://websemantics.ca
9 | * @author Adnan M.Sagar, PhD.
10 | *
11 | * @param {Object} l, the document current location
12 | */
13 | ;(function(l) {
14 | var repo = '/' + l.pathname.split('/')[1]
15 |
16 | /* redirect all 404 trafic to index.html */
17 | function redirect() {
18 | l.replace(l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') + repo + '/?' +
19 | (l.pathname ? 'p=' + l.pathname.replace(/&/g, '~and~').replace(repo, '') : '') +
20 | (l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
21 | (l.hash))
22 | }
23 |
24 | /* resolve 404 redirects into internal routes */
25 | function resolve() {
26 | if (l.search) {
27 | var q = {}
28 | l.search.slice(1).split('&').forEach(function(v) {
29 | var a = v.split('=')
30 | q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&')
31 | })
32 | if (q.p !== undefined) {
33 | window.history.replaceState(null, null,
34 | repo + (q.p || '') +
35 | (q.q ? ('?' + q.q) : '') +
36 | l.hash
37 | )
38 | }
39 | }
40 | }
41 |
42 | /* if current document is 404 page page, redirect to index.html otherwise resolve */
43 | document.title === '404' ? redirect() : resolve()
44 | }(window.location))
45 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Vue Cal - Documentation
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Vue Cal - Documentation
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "organizeImports": {
4 | "enabled": false
5 | },
6 | "linter": {
7 | "enabled": true,
8 | "rules": {
9 | "recommended": true,
10 | "correctness": {
11 | "noUnusedVariables": "error",
12 | "noConstructorReturn": "off"
13 | },
14 | "suspicious": {
15 | "noConsoleLog": "warn",
16 | "noDebugger": "warn",
17 | "noAssignInExpressions": "off",
18 | "noGlobalIsNan": "off"
19 | },
20 | "style": {
21 | "useTemplate": "error",
22 | "useBlockStatements": "off",
23 | "noParameterAssign": "off",
24 | "useNumberNamespace": "off"
25 | },
26 | "complexity": {
27 | "noForEach": "off"
28 | },
29 | "performance": {
30 | "noDelete": "off"
31 | }
32 | }
33 | },
34 | "formatter": {
35 | "enabled": false,
36 | "formatWithErrors": true,
37 | "indentStyle": "space",
38 | "indentWidth": 2,
39 | "lineWidth": 320,
40 | "lineEnding": "lf",
41 | "ignore": ["dist", "node_modules", "docs"]
42 | },
43 | "javascript": {
44 | "formatter": {
45 | "quoteStyle": "single",
46 | "quoteProperties": "asNeeded",
47 | "trailingCommas": "none",
48 | "semicolons": "asNeeded",
49 | "arrowParentheses": "asNeeded"
50 | }
51 | },
52 | "files": {
53 | "ignore": ["dist", "node_modules", "*.min.js", "public", "src/assets", "coverage", "package-lock.json", "pnpm-lock.yaml", "yarn.lock", "docs"]
54 | },
55 | "overrides": [
56 | {
57 | "include": ["*.svelte", "*.astro", "*.vue"],
58 | "linter": {
59 | "rules": {
60 | "correctness": {
61 | "noUnusedVariables": "off"
62 | }
63 | }
64 | }
65 | }
66 | ]
67 | }
68 |
--------------------------------------------------------------------------------
/docs/assets/road-map-DJuegIkf.css:
--------------------------------------------------------------------------------
1 | .page--road-map{height:calc(100dvh - 71px)}.main--road-map{display:flex;flex-direction:column}.kanban{list-style-type:none;margin:0;display:flex;flex-grow:1;gap:8px;overflow-y:auto;align-items:flex-start}.kanban__column{position:sticky;top:0;display:flex;flex-direction:column;flex-grow:1;flex-basis:0;border-radius:8px;min-height:100%}.kanban__column .title{font-size:1.2rem;color:var(--w-base-color);padding:12px 8px 4px;display:flex;align-items:center;position:sticky;top:0;z-index:1;background-color:var(--w-base-bg-color)}.kanban__column .title:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--w-base-bg-color);z-index:-1;border:1px solid;border-width:4px 1.5px 0}.kanban__column--todo .column,.kanban__column--todo .title:before{background-color:#bfbfbf0f;border-color:#bfbfbf1f;border-top:4px solid hsla(0,0%,75%,.6)}.kanban__column--doing .column,.kanban__column--doing .title:before{background-color:#f2e18c14;border-color:#f2e18c33;border-top:4px solid hsla(50,100%,80%,.6)}.kanban__column--done .column,.kanban__column--done .title:before{background-color:#80ffaa14;border-color:#80ffaa33;border-top:4px solid hsla(140,100%,70%,.5)}.kanban__column--later .column,.kanban__column--later .title:before{background-color:#ff80ff0f;border-color:#ff80ff33;border-top:4px solid hsla(300,100%,75%,.6)}.kanban__column .column{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit;border-style:solid;border-width:4px 1px1px;border-top:none}.kanban .task{padding:6px 8px;border-radius:6px;line-height:1.2;font-size:13px;border:1px solid color-mix(in srgb,var(--w-contrast-bg-color) 5%,transparent);background-color:#ffffff1a}[data-theme=light] .kanban .task{background-color:#ffffffe6}.kanban__column--todo .task{border-left:4px solid hsla(0,0%,75%,.62)}.kanban__column--doing .task{border-left:4px solid hsla(50,100%,80%,.8)}.kanban__column--done .task{border-left:4px solid hsla(140,80%,75%,.66)}.kanban__column--later .task{border-left:4px solid hsla(300,80%,75%,.62)}
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: Feature Request
2 | description: Suggest a new feature or improvement.
3 | title: "[Feature] "
4 | labels: ["enhancement"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | > THIS FEAT. REQUEST IS ONLY FOR THE V5. v4 issues should be reported [here](https://github.com/antoniandre/vue-cal-v4/issues).
10 |
11 | ## Feature Request Guidelines
12 | Thank you for taking the time to suggest a feature! Please provide as much detail as possible to help us evaluate and implement your idea.
13 |
14 | - type: input
15 | id: feature-title
16 | attributes:
17 | label: "Feature Title"
18 | description: "Provide a concise title for your feature request."
19 | placeholder: "e.g., Add dark mode support"
20 | validations:
21 | required: true
22 |
23 | - type: textarea
24 | id: feature-description
25 | attributes:
26 | label: "Feature Description"
27 | description: "Describe the feature or enhancement you'd like to see. Include details on how it would be used and its benefits."
28 | placeholder: "Explain your feature request in detail..."
29 | validations:
30 | required: true
31 |
32 | - type: textarea
33 | id: use-case
34 | attributes:
35 | label: "Use Case"
36 | description: "Explain why this feature is important and provide specific use cases where it would be helpful."
37 | placeholder: "Describe how this feature will help you or others..."
38 | validations:
39 | required: true
40 |
41 | - type: checkboxes
42 | id: additional-context
43 | attributes:
44 | label: "Additional Context"
45 | description: "Check all that apply."
46 | options:
47 | - label: "I can help implement this feature"
48 | - label: "This feature is critical to my project"
49 |
50 | - type: textarea
51 | id: alternative-solutions
52 | attributes:
53 | label: "Alternative Solutions"
54 | description: "Have you considered any alternative solutions or workarounds?"
55 | placeholder: "List alternative approaches, if any..."
56 |
--------------------------------------------------------------------------------
/docs/assets/playground-CAP3nagG.css:
--------------------------------------------------------------------------------
1 | .page--playground{padding:40px 0 8px;border-left:none;overflow:hidden;max-width:none;height:100dvh}.page--playground~footer,.page--playground aside,.page--playground h1{display:none}.page--playground .main--examples{display:flex;flex-direction:column;flex-grow:1;overflow:hidden}.page--playground main{display:flex;flex-direction:column;border:none;padding:0;flex-grow:1}.page--playground main aside{display:flex;flex-direction:column;justify-content:flex-start;width:215px;padding:0}.page--playground .config-panel{padding:12px;background-color:color-mix(in srgb,var(--w-contrast-bg-color) 5%,transparent);border-bottom:1px solid color-mix(in srgb,var(--w-contrast-bg-color) 8%,transparent)}.page--playground .vue-cal--main{--vuecal-height: 100%}.page--playground .vuecal__special-hours{text-align:center}.page--playground .vuecal__special-hours.doctor-1{background-color:#6dc57726;color:#81d58b}.page--playground .vuecal__special-hours.doctor-2{background-color:#6d8fc526;color:#689bee}.page--playground .vuecal__special-hours.doctor-3{background-color:#b26dc526;color:#d168ee}.page--playground .vuecal__special-hours.closed{background:repeating-linear-gradient(-45deg,#fff0 0,#fff0 6px,#ffa25726 6px,#ffa25726 20px);color:#f6984c}.page--playground .vuecal__special-hours em{font-size:.9em;color:#999;line-height:1.15}.page--playground .vuecal__schedule.dr-1{background-color:#86c0fd1a}.page--playground .vuecal__schedule.dr-2{background-color:#bb94ff26}.vuecal--dark .page--playground .vuecal__schedule.dr-1{background-color:#8f9ec41a}.vuecal--dark .page--playground .vuecal__schedule.dr-2{background-color:#83b8ff1a}.page--playground .vuecal__event.leisure{background-color:#fd9c42d9;border-color:#e9882e}.page--playground .vuecal__event.health{background-color:#57cea9cc;border-color:#90d2be}.page--playground .vuecal__event.sport{background-color:#ff6666d9;border-color:#eb5252}@media screen and (max-width: 900px){.main--playground aside{position:absolute;bottom:5px;left:5px;z-index:10;background:var(--w-base-bg-color)}.vue-cal--main{margin-left:80px}}@media screen and (max-width: 600px){.main--playground aside{bottom:-8px;left:-8px;transform:scale(.9)}.vue-cal--main{margin-left:0}}
2 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import { resolve } from 'node:path'
4 | import autoprefixer from 'autoprefixer'
5 | import pkg from './package.json'
6 |
7 | const banner = `/**
8 | * ${pkg.name} v${pkg.version}
9 | * (c) 2024-${new Date().getFullYear()} ${pkg.author}
10 | * @license MIT
11 | */\n`
12 |
13 | const bundlingConf = {
14 | minify: true,
15 | lib: {
16 | entry: resolve(__dirname, 'src/vue-cal/index.js'),
17 | name: 'vuecal', // The global name of the library.
18 | fileName: format => `vue-cal.${format}.js` // Output filename pattern.
19 | },
20 | rollupOptions: {
21 | // Make sure to externalize deps that shouldn't be bundled into library.
22 | external: id => {
23 | if (id === 'vue') return true // Externalize vue.
24 | return false
25 | },
26 | output: {
27 | banner,
28 | globals: { vue: 'Vue' }, // Vue should be treated as external and available as a global variable.
29 | chunkFileNames: chunkInfo => {
30 | if (chunkInfo.facadeModuleId.endsWith('.json')) return 'i18n/[name].js' // Match JSON to JS name without a hash.
31 | return '[name]-[hash].js' // Default behavior.
32 | }
33 | }
34 | },
35 | copyPublicDir: false // Prevent copying `public/` to `dist` folder.
36 | }
37 |
38 | export default defineConfig({
39 | define: {
40 | 'process.env': {
41 | VITE_APP_VERSION: process.env.npm_package_version,
42 | __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false
43 | }
44 | },
45 | plugins: [
46 | vue({
47 | template: {
48 | compilerOptions: {
49 | whitespace: 'preserve'
50 | }
51 | }
52 | })
53 | ], // https://vitejs.dev/config/
54 | resolve: {
55 | alias: {
56 | '@': resolve(__dirname, 'src')
57 | }
58 | },
59 | css: {
60 | preprocessorOptions: {
61 | scss: {
62 | api: 'modern-compiler',
63 | additionalData: '@use "@/scss/variables" as *;'
64 | }
65 | },
66 | postcss: {
67 | plugins: [autoprefixer]
68 | }
69 | },
70 | build: process.env.BUNDLE ? bundlingConf : { outDir: 'docs' }
71 | })
72 |
--------------------------------------------------------------------------------
/docs/assets/schedules-BjW2sphf.css:
--------------------------------------------------------------------------------
1 | .main--examples-schedules .example--special-hours .business-hours{background-color:#00daff21}.main--examples-schedules .example--special-hours .vuecal__special-hours{text-align:center}.main--examples-schedules .example--special-hours .vuecal__special-hours.doctor-1{background-color:#6dc57726;color:#81d58b}.main--examples-schedules .example--special-hours .vuecal__special-hours.doctor-2{background-color:#6d8fc526;color:#689bee}.main--examples-schedules .example--special-hours .vuecal__special-hours.doctor-3{background-color:#b26dc526;color:#d168ee}.main--examples-schedules .example--special-hours .vuecal__special-hours.closed{background:repeating-linear-gradient(-45deg,#fff0 0,#fff0 6px,#ffa25726 6px,#ffa25726 20px);color:#f6984c}.main--examples-schedules .example--special-hours .vuecal__special-hours em{font-size:.9em;color:#999;line-height:1.15}.main--examples-schedules .vuecal__schedule.dad{background-color:#ddeeff80}.main--examples-schedules .vuecal__schedule.mom{background-color:#ffe8fb80}.main--examples-schedules .vuecal__schedule.kid1{background-color:#ddffef80}.main--examples-schedules .vuecal__schedule.kid2{background-color:#fffac480}.main--examples-schedules .vuecal__schedule.kid3{background-color:#ffceb280}.main--examples-schedules .vuecal__schedule--heading{color:#00000080;font-size:14px;font-weight:500}.main--examples-schedules .vuecal__time-cell-line.hours:before{border-color:var(--w-primary-color)}.main--examples-schedules .ex--custom-schedules-headings .schedule-heading{font-size:11px}.main--examples-schedules .ex--custom-schedules-headings .vuecal__body .schedule1{background-color:#e2f2fdb3}.main--examples-schedules .ex--custom-schedules-headings .vuecal__body .schedule2{background-color:#e8f5e9b3}.main--examples-schedules .ex--custom-schedules-headings .vuecal__body .schedule3{background-color:#fff3e0b3}.main--examples-schedules .ex--custom-schedules-headings .vuecal__body .schedule4{background-color:#ffebeeb3}@media screen and (max-width: 800px){.main--examples-schedules .vuecal--week-view.ex--custom-schedules-headings .vuecal__schedule--heading .w-icon{display:none}.main--examples-schedules .ex--custom-schedules-headings .vuecal__schedule--heading strong{overflow:hidden;width:.9em;font-size:13px;letter-spacing:10px}}
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: Report a bug to help us improve.
3 | title: "[Bug] "
4 | labels: ["bug"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | ## IS THIS BUG FOR V5 OR V4?
10 | > THIS BUG REPORT IS ONLY FOR THE V5. v4 issues should be reported [here](https://github.com/antoniandre/vue-cal-v4/issues).
11 |
12 | - type: input
13 | id: vue-cal-version
14 | attributes:
15 | label: "Vue Cal Version"
16 | description: "Specify the version of Vue Cal you are using (e.g., 5.0.0)."
17 | placeholder: "e.g., 5.0.0"
18 | validations:
19 | required: true
20 |
21 | - type: input
22 | id: vue-version
23 | attributes:
24 | label: "Vue Version"
25 | description: "Specify the version of Vue you are using (e.g., 3.3.0)."
26 | placeholder: "e.g., 3.3.0"
27 | validations:
28 | required: true
29 |
30 | - type: checkboxes
31 | id: additional-settings
32 | attributes:
33 | label: "Additional Settings"
34 | description: "Check all that apply."
35 | options:
36 | - label: "Using Composition API"
37 | - label: "Using SSR"
38 |
39 | - type: textarea
40 | id: bug-description
41 | attributes:
42 | label: "Describe the Bug & Expected Behavior"
43 | description: "Provide a clear and concise description of the bug and what you expected to happen. Add screenshots if helpful."
44 | placeholder: "Describe the bug and expected behavior here..."
45 | validations:
46 | required: true
47 |
48 | - type: textarea
49 | id: reproduction-link
50 | attributes:
51 | label: "Provide a Reproduction Link"
52 | description: |
53 | Provide a link to reproduce the issue by forking one of our examples on StackBlitz:
54 |
55 | - [Vue Cal 5: Basic (Vue 3 + Vite)](https://stackblitz.com/edit/vuecal5?file=src%2FApp.vue)
56 | - [Vue Cal 5: Events (Vue 3 + Vite)](https://stackblitz.com/edit/vuecal5-events?file=src/App.vue)
57 |
58 | If not possible to reproduce, describe the detailed steps to reproduce the behavior:
59 | 1. Go to '...'
60 | 2. Click on '...'
61 | 3. See error
62 | placeholder: "Paste reproduction link here"
63 | validations:
64 | required: true
65 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-cal",
3 | "version": "5.0.1-rc.33",
4 | "description": "A Vue JS full calendar, no dependency, no BS. :metal:",
5 | "author": "Antoni Andre ",
6 | "homepage": "https://antoniandre.github.io/vue-cal",
7 | "repository": "https://github.com/antoniandre/vue-cal",
8 | "license": "MIT",
9 | "funding": "https://github.com/sponsors/antoniandre",
10 | "main": "dist/vue-cal.umd.js",
11 | "types": "dist/vue-cal.d.ts",
12 | "module": "dist/vue-cal.es.js",
13 | "type": "module",
14 | "files": [
15 | "dist",
16 | "package.json",
17 | "README.md",
18 | "LICENSE"
19 | ],
20 | "exports": {
21 | ".": {
22 | "import": "./dist/vue-cal.es.js",
23 | "require": "./dist/vue-cal.cjs.js"
24 | },
25 | "./style": {
26 | "default": "./dist/vue-cal.css"
27 | },
28 | "./style.css": {
29 | "default": "./dist/vue-cal.css"
30 | },
31 | "./i18n/*": "./dist/i18n/*",
32 | "./package.json": "./package.json"
33 | },
34 | "keywords": [
35 | "vuecal",
36 | "vue cal",
37 | "vue calendar",
38 | "full calendar",
39 | "calendar events",
40 | "vue",
41 | "vuejs",
42 | "vue 3",
43 | "ui"
44 | ],
45 | "scripts": {
46 | "dev": "vite",
47 | "build": "vite build --base /vue-cal/",
48 | "build-bundle": "BUNDLE=true vite build && cp src/types/vue-cal.ts dist/vue-cal.d.ts",
49 | "preview": "vite preview --base /vue-cal/",
50 | "lint": "vite lint",
51 | "publish-doc": "npm run build && npm run build-bundle && git add . && git commit -m 'Publish documentation on Github.' && git push && git push --tag"
52 | },
53 | "devDependencies": {
54 | "@babel/core": "^7.28.4",
55 | "@babel/eslint-parser": "^7.28.4",
56 | "@mdi/font": "^7.4.47",
57 | "@vitejs/plugin-vue": "^5.2.4",
58 | "autoprefixer": "^10.4.21",
59 | "eslint": "^8.57.1",
60 | "pinia": "^3.0.3",
61 | "postcss": "^8.5.6",
62 | "pug": "^3.0.3",
63 | "rollup": "^4.52.4",
64 | "sass": "^1.93.2",
65 | "simple-syntax-highlighter": "^3.1.1",
66 | "vite": "^6.3.6",
67 | "vite-plugin-static-copy": "^2.3.2",
68 | "vue": "^3.5.22",
69 | "vue-router": "^4.5.1",
70 | "wave-ui": "^3.25.4"
71 | },
72 | "peerDependencies": {
73 | "vue": "^3.5.0"
74 | },
75 | "packageManager": "pnpm@10.11.0"
76 | }
77 |
--------------------------------------------------------------------------------
/src/documentation/examples/index.vue:
--------------------------------------------------------------------------------
1 |
2 | .examples-container.w-flex.grow.gap12(:class="{ 'examples-container--has-aside': showAside }")
3 | .grow.w-flex.grow.column
4 | h1.title1
5 | | Examples
6 | template(v-if="$route.name !== 'examples-intro'")
7 | w-icon.caption.mx1(lg) wi-chevron-right
8 | span(v-html="$route.meta.title")
9 |
10 | .main--examples
11 | router-view
12 |
13 | aside.aside(v-if="showAside")
14 | .aside__content
15 | .title.grey ON THIS PAGE
16 | examples-menu(only-active-page)
17 |
18 |
19 |
34 |
35 |
93 |
--------------------------------------------------------------------------------
/docs/assets/calendar-events-interactions-t-zWtkbW.css:
--------------------------------------------------------------------------------
1 | .main--examples-events-interactions .vuecal__event{text-align:center}.main--examples-events-interactions .vuecal__event.leisure{background-color:#fd9c42d9;border-color:#e9882e}.main--examples-events-interactions .vuecal__event.health{background-color:#57cea9cc;border-color:#90d2be}.main--examples-events-interactions .vuecal__event.sport{background-color:#ff6666d9;border-color:#eb5252}.main--examples-events-interactions .vuecal__event.pink-event{background-color:#ff3a8fb3;border-color:#eb267b}.main--examples-events-interactions .vuecal__event.blue-event{background-color:#64c8ffcc;border-color:#50b4eb}.main--examples-events-interactions .vuecal__event.yellow-event{background-color:#ffc85abf;border-color:#ffc356}.main--examples-events-interactions .vuecal__event.lunch{background:repeating-linear-gradient(45deg,transparent,transparent 10px,color-mix(in srgb,var(--w-contrast-bg-color) 6%,transparent) 10px,color-mix(in srgb,var(--w-contrast-bg-color) 6%,transparent) 20px);border:none;z-index:-1}.main--examples-events-interactions .vuecal__event.lunch .vuecal__event-time{display:none}.main--examples-events-interactions .vuecal__event i{margin:2px 0;font-size:23px}.main--examples-events-interactions .vuecal__event-title{font-weight:700}.main--examples-events-interactions .ex--create-events .vuecal__event{background-color:#4cacaf59}.main--examples-events-interactions .example--drag-and-drop .override-drag-css .vuecal__event--dragging-ghost{opacity:1;background-color:#adff2f;border:none;color:#000}.main--examples-events-interactions .example--drag-and-drop .override-drag-css .vuecal__event--dragging-original{opacity:.8;border:1px dashed var(--vuecal-event-border-color);transform:scale(.8);transition:transform .2s ease-in-out}.main--examples-events-interactions .example--external-events-drag-and-drop{flex-basis:0!important;min-width:285px;--vuecal-primary-color: #316191}[data-theme=light] .main--examples-events-interactions .example--external-events-drag-and-drop{--vuecal-primary-color: #1976D2}.main--examples-events-interactions .example--external-events-drag-and-drop .external-events{max-width:150px;background-color:color-mix(in srgb,var(--w-contrast-bg-color) 3%,transparent);border:1px solid color-mix(in srgb,var(--w-contrast-bg-color) 6%,transparent);padding:4px;border-radius:4px}.main--examples-events-interactions .example--external-events-drag-and-drop .external-event{background-color:var(--vuecal-primary-color);color:#fff;cursor:grab;border-radius:4px;font-size:13px;padding:2px 4px}.main--examples-events-interactions .example--external-events-drag-and-drop .external-event .caption{color:#ffffffb3}.main--examples-events-interactions .example--events-reactivity .vuecal__event{transition:background-color 1s}
2 |
--------------------------------------------------------------------------------
/src/vue-cal/core/index.js:
--------------------------------------------------------------------------------
1 | import { reactive } from 'vue'
2 | import { defaults, useConfig } from './config'
3 | import { useDragAndDrop } from '../modules/drag-and-drop'
4 | import { useDateUtils } from '../utils/date'
5 | import { useEvents } from './events'
6 | import { useView } from './view'
7 | import EnUs from '../i18n/en-us.json'
8 |
9 | // Shared global reactive store: common to all the VueCal instances.
10 | // The global store is also used when the user wants to use Date prototypes with localized texts
11 | // before or without the Vue Cal component.
12 | export const globalState = reactive({
13 | texts: { ...defaults.texts }, // Make texts reactive before a locale is loaded.
14 | dateUtils: useDateUtils(defaults.texts, EnUs) // Some Date utils functions need localized texts.
15 | })
16 |
17 | /**
18 | * This is the main composable of the calendar - the heart :)
19 | * It is used one single time, from the index.vue and it's inject-provided to all the components.
20 | *
21 | * GLOBAL IMPORTANT NOTES
22 | * ----------------------
23 | * - There is no (and there shouldn't be) any use of Date prototypes in the codebase: even if using them
24 | * would simplify things a lot, the user may choose to disable them and nothing would work anymore.
25 | *
26 | * - Computed variables should only manage one thing (or a small group of vars) at a time:
27 | * Every recomputing can become very expensive when handling a large amount of cells per view
28 | * with a large amount of calendar events. So the more a computed is specific, the less it will have
29 | * expensive impact.
30 | * E.g. we definitely don't want that switching locale, or xs/sm prop would redraw the cells and
31 | * recalculate all the events rendering in each cell.
32 | *
33 | * @param {object} props The Vue props definition from the root VueCal component (index.vue).
34 | * @param {function} emit The Vue emit function from the root VueCal component (index.vue).
35 | */
36 | export const useVueCal = ({ props, emit, attrs, vuecalEl, uid }) => {
37 | // This reactive store is the one and only source of truth.
38 | const state = reactive({
39 | uid, // The Vuecal instance unique ID, used for dnd source-target identification.
40 | emit,
41 | texts: { ...globalState.texts }, // Make texts reactive before a locale is loaded.
42 | // The date utils composable.
43 | // A class/composable is needed in order to access the user locale in all the methods, and
44 | // independently of other potential Vue Cal instances on the same page.
45 | dateUtils: { ...globalState.dateUtils },
46 | now: new Date(),
47 | config: {},
48 | eventsManager: {},
49 | view: {}, // At any time this object will be filled with current view details and visible events.
50 | dnd: {}, // Drag and drop module.
51 | // stores the gesture related states. E.g. dragging event, resizing event, etc.
52 | touch: {
53 | isDraggingCell: false,
54 | isDraggingEvent: false,
55 | isResizingEvent: false,
56 | currentHoveredCell: null // Track the cell currently being hovered during event resizing.
57 | }
58 | })
59 |
60 | state.dateUtils = useDateUtils(Object.assign(defaults.texts, state.texts), EnUs)
61 | state.config = useConfig(state, props, attrs)
62 | state.eventsManager = useEvents(state)
63 | state.view = useView(state, vuecalEl)
64 | state.dnd = useDragAndDrop(state)
65 |
66 | return state
67 | }
68 |
--------------------------------------------------------------------------------
/docs/assets/release-notes-DcMJcxtH.css:
--------------------------------------------------------------------------------
1 | .main--release-notes .history>li{padding-left:24px}.main--release-notes .history>li+li{margin-top:28px}.main--release-notes .history li{position:relative;list-style-type:none}.main--release-notes .history h2{margin-top:0}.main--release-notes .history .view-more>div{bottom:2px;position:relative}.main--release-notes .history>li:before{content:"";position:absolute;top:11px;left:0;background-color:var(--w-base-bg-color);border-radius:1em;border:1px solid currentColor;width:1em;aspect-ratio:1;transform:translate(-50%,-50%);z-index:1}.main--release-notes .history.history--more>li:first-child:before{display:none}.main--release-notes .history>li:after{content:"";position:absolute;top:11px;bottom:-39px;left:-.5px;border-left:1px solid var(--w-base-color);opacity:.25}.main--release-notes .history>li:last-child:after{display:none}.main--release-notes .history>li.dashed:after{border-left-style:dashed}.main--release-notes .history>li.patch:before{font-size:7px;border-style:dashed;width:1.1rem;animation:spin 10s linear infinite}.main--release-notes .history>li.minor:before{font-size:11px;width:1.1rem;animation:pulse 3s ease-in-out infinite}.main--release-notes .history>li.major:before{font-size:14px;box-shadow:0 0 #09c;border-color:var(--w-primary-color);animation:pulse-sonar 3s infinite cubic-bezier(.25,.46,.45,.94)}.main--release-notes .history .version{font:700 1.2rem monospace;display:block}.main--release-notes .history>li.patch:before,.main--release-notes .history>li.patch .version{color:color-mix(in srgb,var(--w-base-color) 40%,transparent)}.main--release-notes .history>li.minor:before,.main--release-notes .history>li.minor .version{color:color-mix(in srgb,var(--w-base-color) 60%,transparent)}.main--release-notes .history>li.major:before,.main--release-notes .history>li.major .version{color:#09c;font-size:1.4rem}.main--release-notes .history ul{margin-left:-2px}.main--release-notes .history li li{padding-left:20px;margin-top:2px}.main--release-notes .history li li:before{content:"";font-family:wave-ui!important;font-style:normal!important;font-weight:400!important;font-variant:normal!important;text-transform:none!important;speak:none;line-height:1;-webkit-font-smoothing:antialiased;position:absolute;top:3px;left:0;width:1em;aspect-ratio:1}.main--release-notes .history li li li:before{content:""}.main--release-notes .history p{margin:.2em 0 0;line-height:1.2}.main--release-notes .history code{padding:0 4px}.main--release-notes .history strong.code:first-child:not(.black){color:var(--w-primary-color);font-size:1.1em}.main--release-notes .vue-green{color:#42b883}.main--release-notes .vue-green--bg{background-color:#42b883}.main--release-notes span.tag{border-radius:99em;padding:2px 5px;color:#fff;font-weight:700;font-size:9px;position:relative;top:-1px}.main--release-notes span.new{background-color:#3698e5}.main--release-notes span.new:before{content:"NEW"}.main--release-notes span.deprecated{background-color:#000}.main--release-notes span.deprecated:before{content:"DEPRECATED"}.main--release-notes span.removed{background-color:#f02c2c}.main--release-notes span.removed:before{content:"REMOVED"}@keyframes spin{0%{transform:translate(-50%,-50%) rotate(0)}to{transform:translate(-50%,-50%) rotate(360deg)}}@keyframes pulse{0%{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.1)}to{transform:translate(-50%,-50%) scale(1)}}@keyframes pulse-sonar{0%{box-shadow:0 0 #09c;transform:translate(-50%,-50%) scale(1);border-color:var(--w-primary-color);opacity:.5}50%{box-shadow:0 0 0 15px #09cccc00;transform:translate(-50%,-50%) scale(1.05);border-color:#09c}to{box-shadow:0 0 #09cccc00;transform:translate(-50%,-50%) scale(1);border-color:var(--w-primary-color)}}
2 |
--------------------------------------------------------------------------------
/src/documentation/components/alert.vue:
--------------------------------------------------------------------------------
1 |
2 | w-alert(:class="`w-alert--${type}`" :icon-outside="!!icon")
3 | w-icon(v-if="icon") {{ icon }}
4 | slot
5 |
6 |
7 |
41 |
42 |
108 |
--------------------------------------------------------------------------------
/docs/assets/isolated-test-view-BgMJ4XGg.js:
--------------------------------------------------------------------------------
1 | import{b as x,s as p,c as M}from"./index-MbPhGSu7.js";import{u as S,r as n,x as g,c as D,b as T,e as r,f as i,w as u,h as d,a as N,t as y,y as $,g as B,d as O}from"./index-7bpj6pqg.js";import{_ as P}from"./index-D1aoba1K.js";const A={class:"test-view"},U={class:"w-flex gap2"},W={class:"w-flex column gap2 mt4 ovh"},I={__name:"isolated-test-view",setup(F){x();const m=S(),w=n(null);g({datePicker:!0,dark:D(()=>m.darkMode),selectedDate:D(()=>s.selectedDate),locale:D(()=>s.locale)});const s=g({view:n("week"),dark:D(()=>m.darkMode),selectedDate:n(null),viewDate:n(new Date),locale:n(""),startWeekOnSunday:n(!1),todayButton:n(!0),xs:n(!1),sm:n(!1),timeStep:60,twelveHour:n(!1),hideWeekends:n(!1),hideWeekdays:n([]),viewDayOffset:n(0),clickToNavigate:n(!1),watchRealTime:n(!0),editableEvents:n(!0),allDayEvents:n(!0),events:n([]),eventsOnMonthView:!0,horizontal:n(!0)});setTimeout(()=>{s.events=[{title:"Event 1",start:new Date(new Date().setHours(10,0,0,0)),end:new Date(new Date().setHours(11,30,0,0)),allDay:!0},{title:"Event 2",start:new Date(new Date().addDays(1).setHours(11,0,0,0)),end:new Date(new Date().addDays(1).setHours(13,30,0,0)),allDay:!0},{title:"Event 3",start:new Date(new Date().setHours(10,0,0,0)),end:new Date(new Date().setHours(11,30,0,0))},{title:"Event 4",start:new Date(new Date().addDays(1).setHours(11,0,0,0)),end:new Date(new Date().addDays(1).setHours(13,30,0,0))}]},0);const k=()=>{s.events.push({title:`Event ${s.events.length}`,start:new Date().subtractHours(4),end:new Date().subtractHours(3),schedule:2})},C=()=>{w.value.view.createEvent({title:"New Event!",start:new Date().subtractHours(2),end:new Date().subtractHours(1),schedule:1})},H=()=>{w.value.view.deleteEvent({id:123})},a=(...o)=>console.log(...o),z=o=>{b(o.start.format(),o.end.format())},b=async(o,e)=>{console.log("fetchEvents",o,e),await new Promise(v=>setTimeout(v,500));const l=p(o),t=p(e);s.events=R(l,t)},R=(o,e)=>{const l=M(o,e),t=[];for(let v=0;v{const l=N("w-button");return O(),T("div",A,[r("div",U,[i(l,{onClick:k},{default:u(()=>[...e[17]||(e[17]=[d("Add event",-1)])]),_:1}),i(l,{onClick:C},{default:u(()=>[...e[18]||(e[18]=[d("Add event",-1)])]),_:1}),i(l,{onClick:H},{default:u(()=>[...e[19]||(e[19]=[d("Delete event",-1)])]),_:1}),i(l,{onClick:e[0]||(e[0]=t=>s.horizontal=!s.horizontal)},{default:u(()=>[...e[20]||(e[20]=[d("Horizontal",-1)])]),_:1}),i(l,{onClick:e[1]||(e[1]=t=>w.value.view.switch("day",new Date))},{default:u(()=>[...e[21]||(e[21]=[d("Switch to today",-1)])]),_:1})]),r("div",null,[r("pre",null,[e[22]||(e[22]=r("strong",{class:"mr2"},"Selected date:",-1)),d(y(s.selectedDate),1)]),r("pre",null,[e[23]||(e[23]=r("strong",{class:"mr2"},"View date:",-1)),d(y(s.viewDate),1)])]),r("div",W,[i(B(P),$({class:"grow",ref_key:"vueCalRef",ref:w,view:s.view,"onUpdate:view":e[2]||(e[2]=t=>s.view=t),"selected-date":s.selectedDate,"onUpdate:selectedDate":e[3]||(e[3]=t=>s.selectedDate=t),"view-date":s.viewDate,"onUpdate:viewDate":e[4]||(e[4]=t=>s.viewDate=t)},s,{onReady:e[5]||(e[5]=t=>a("ready",t)),onViewChange:z,onEventCreate:e[6]||(e[6]=t=>a("event-create",t)),onEventClick:e[7]||(e[7]=t=>a("event-click",t)),onEventDrag:e[8]||(e[8]=t=>a("event-drag",t)),onEventDragEnd:e[9]||(e[9]=t=>a("event-drag",t)),onEventDrop:e[10]||(e[10]=t=>a("event-drop",t)),onEventResizeStart:e[11]||(e[11]=t=>a("event-resize-start",t)),onEventResize:e[12]||(e[12]=t=>a("event-resize",t)),onEventResizeEnd:e[13]||(e[13]=t=>a("event-resize-end",t)),onCellClick:e[14]||(e[14]=t=>a("cell-click",t)),onCellDrag:e[15]||(e[15]=t=>a("cell-drag",t)),onCellDragEnd:e[16]||(e[16]=t=>a("cell-drag-end",t))}),null,16,["view","selected-date","view-date"])])])}}};export{I as default};
2 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from 'vue-router'
2 | import Documentation from '@/documentation/index.vue'
3 |
4 | const routes = [
5 | {
6 | path: '/',
7 | name: 'home',
8 | component: () => import('@/documentation/home.vue')
9 | },
10 | {
11 | path: '/getting-started',
12 | name: 'getting-started',
13 | component: () => import('@/documentation/getting-started.vue')
14 | },
15 | {
16 | path: '/api',
17 | name: 'api',
18 | component: () => import('@/documentation/api.vue')
19 | },
20 | {
21 | path: '/date-prototypes',
22 | name: 'date-prototypes',
23 | component: () => import('@/documentation/date-prototypes.vue')
24 | },
25 | {
26 | path: '/examples',
27 | name: 'examples',
28 | component: () => import('@/documentation/examples/index.vue'),
29 | redirect: '/examples/introduction',
30 | children: [
31 | {
32 | path: 'introduction',
33 | name: 'examples-intro',
34 | component: () => import('@/documentation/examples/introduction.vue')
35 | },
36 | {
37 | path: 'view',
38 | name: 'examples-view',
39 | component: () => import('@/documentation/examples/view.vue'),
40 | meta: { title: 'View' }
41 | },
42 | {
43 | path: 'date-and-time',
44 | name: 'examples-date-and-time',
45 | component: () => import('@/documentation/examples/date-and-time.vue'),
46 | meta: { title: 'Date and Time' }
47 | },
48 | {
49 | path: 'schedules',
50 | name: 'examples-schedules',
51 | component: () => import('@/documentation/examples/schedules.vue'),
52 | meta: { title: 'schedules' }
53 | },
54 | {
55 | path: 'calendar-events--display',
56 | name: 'examples-events-display',
57 | component: () => import('@/documentation/examples/calendar-events-display.vue'),
58 | meta: { title: 'Calendar Events Display' }
59 | },
60 | {
61 | path: 'calendar-events--interactions',
62 | name: 'examples-events-interactions',
63 | component: () => import('@/documentation/examples/calendar-events-interactions.vue'),
64 | meta: { title: 'Calendar Events Interactions' }
65 | },
66 | {
67 | path: 'dom-events',
68 | name: 'examples-dom-events',
69 | component: () => import('@/documentation/examples/dom-events.vue'),
70 | meta: { title: 'DOM Events' }
71 | },
72 | {
73 | path: 'customization',
74 | name: 'examples-customization',
75 | component: () => import('@/documentation/examples/customization.vue'),
76 | meta: { title: 'Customization' }
77 | },
78 | {
79 | path: 'playground',
80 | name: 'playground',
81 | component: () => import('@/documentation/examples/playground.vue')
82 | }
83 | ]
84 | },
85 | {
86 | path: '/migration-guide',
87 | name: 'migration-guide',
88 | component: () => import('@/documentation/migration-guide.vue')
89 | },
90 | {
91 | path: '/road-map',
92 | name: 'road-map',
93 | component: () => import('@/documentation/road-map.vue')
94 | },
95 | {
96 | path: '/release-notes',
97 | name: 'release-notes',
98 | component: () => import('@/documentation/release-notes.vue')
99 | },
100 | {
101 | path: '/test',
102 | name: 'test',
103 | component: () => import('@/documentation/isolated-test-view.vue')
104 | },
105 | {
106 | path: '/:pathMatch(.*)',
107 | name: 'not-found',
108 | component: () => import('@/documentation/404.vue'),
109 | meta: { public: true }
110 | }
111 | ]
112 |
113 | export default createRouter({
114 | history: createWebHistory(import.meta.env.BASE_URL),
115 | routes,
116 | scrollBehavior(to, from, savedPosition) {
117 | if (savedPosition) return savedPosition
118 | if (to.hash) return { el: to.hash, behavior: 'smooth' }
119 | return { top: 0 }
120 | }
121 | })
122 |
--------------------------------------------------------------------------------
/src/vue-cal/index.scss:
--------------------------------------------------------------------------------
1 | .vuecal {
2 | --vuecal-grid-columns: 7; // Default value, overridden dynamically on view change.
3 | --vuecal-grid-rows: 6; // Default value, overridden dynamically on view change.
4 | --vuecal-weekday-bar-height: 1.7rem;
5 | --vuecal-schedules-bar-height: 1.7rem;
6 | --vuecal-all-day-bar-height: 2rem;
7 | --vuecal-time-cell-height: 50px; // Default value, can be overridden from props.
8 | // When there are too many day cells to fit in the view, setting a min cell width will help
9 | // visualizing and a horizontal scrollbar will be added.
10 | --vuecal-min-cell-width: 0;
11 | --vuecal-transition-duration: 0.25s;
12 |
13 | display: flex;
14 | flex-direction: column;
15 | user-select: none;
16 | z-index: 0; // Default minimal z-index to at least have visible cells :before.
17 |
18 | &--date-picker {--vuecal-weekday-bar-height: 1.3rem;}
19 | &--timeless {--vuecal-all-day-bar-height: 0px;}
20 |
21 | &, *, :before, :after {box-sizing: border-box;}
22 | .grow {flex-grow: 1;}
23 |
24 | &__body-wrap {
25 | display: flex;
26 | flex-direction: column;
27 | flex-grow: 1;
28 | // Crucial for the content not to overflow when using --vuecal-min-cell-width or --vuecal-min-schedule-width.
29 | min-width: 0;
30 | }
31 |
32 | &__scrollable-wrap {
33 | position: relative;
34 | flex: 1;
35 | display: flex;
36 | min-height: 1px; // Fix the famous issue of the container overflowing the flex parent.
37 | }
38 |
39 | &__scrollable {
40 | position: relative; // For the time cells lines to fill up the whole calendar width.
41 | overflow: auto;
42 | flex: 1;
43 | display: flex;
44 | flex-direction: column;
45 |
46 | &--row {flex-direction: row;}
47 | &--has-week-numbers {flex-direction: row;}
48 | }
49 |
50 | &__week-numbers {
51 | display: flex;
52 | flex-direction: column;
53 | padding-top: calc(var(--vuecal-weekday-bar-height) + var(--vuecal-schedules-bar-height) + var(--vuecal-all-day-bar-height));
54 | }
55 | &__week-number {
56 | display: flex;
57 | flex-grow: 1;
58 | align-items: center;
59 | justify-content: center;
60 | width: 1.4em;
61 | }
62 |
63 | // Shared in headers and cells.
64 | &__schedule {
65 | position: relative;
66 | display: flex;
67 | flex-grow: 1;
68 | flex-basis: 0;
69 | justify-content: center;
70 | overflow: hidden;
71 | }
72 | &__scrollable--days-view &__schedule,
73 | &__scrollable--week-view &__schedule {min-width: var(--vuecal-min-schedule-width, 0);}
74 |
75 | &__schedules-headings {display: flex;}
76 | &__schedule--heading {
77 | font-size: 12px;
78 | align-items: center;
79 | }
80 | }
81 |
82 | // Transitions.
83 | // --------------------------------------------------------
84 | .vuecal-slide-fade--left-enter-active, .vuecal-slide-fade--left-leave-active,
85 | .vuecal-slide-fade--right-enter-active, .vuecal-slide-fade--right-leave-active {
86 | transition: var(--vuecal-transition-duration) ease-in-out;
87 | }
88 |
89 | .vuecal-slide-fade--left-enter-from,
90 | .vuecal-slide-fade--right-leave-to {
91 | transform: translateX(-12px);
92 | opacity: 0;
93 | }
94 |
95 | .vuecal-slide-fade--left-leave-to,
96 | .vuecal-slide-fade--right-enter-from {
97 | transform: translateX(12px);
98 | opacity: 0;
99 | }
100 |
101 | .vuecal-slide-fade--left-enter-active,
102 | .vuecal-slide-fade--right-enter-active,
103 | // When navigating hyper fast, make sure that any left-over 3rd animated container is positioned absolute
104 | // so it does not briefly appear below the calendar.
105 | .vuecal-slide-fade--left-leave-active ~ .vuecal-slide-fade--left-leave-active,
106 | .vuecal-slide-fade--right-leave-active ~ .vuecal-slide-fade--right-leave-active {
107 | position: absolute !important;
108 | inset: 0;
109 | }
110 |
111 | .vuecal-event-delete-leave-active {transition: 0.15s ease-out;}
112 | .vuecal-event-delete-leave-to {
113 | opacity: 0;
114 | transform: translateY(0.5rem);
115 | }
116 |
117 | .vuecal-shrink-enter-active,
118 | .vuecal-shrink-leave-active {
119 | transition: transform 0.2s, opacity 0.2s;
120 | }
121 |
122 | .vuecal-shrink-enter-from,
123 | .vuecal-shrink-leave-to {
124 | opacity: 0;
125 | }
126 |
--------------------------------------------------------------------------------
/src/vue-cal/components/time-column.vue:
--------------------------------------------------------------------------------
1 |
2 | .vuecal__time-column
3 | .vuecal__all-day-label(v-if="config.allDayEvents")
4 | slot(name="all-day-label") {{ vuecal.texts.allDay }}
5 |
6 | .vuecal__time-cell(v-for="(time, i) in timeCells" :key="i" :style="{ height: time.height || null }")
7 | slot(
8 | name="time-cell"
9 | :index="i"
10 | :minutes="time.minutes"
11 | :hours="time.hours"
12 | :minutes-sum="time.minutesSum"
13 | :format12="time.formatted12"
14 | :format24="time.formatted24")
15 | label {{ config.twelveHour ? time.formatted12 : time.formatted24 }}
16 |
17 |
18 |
54 |
55 |
121 |
--------------------------------------------------------------------------------
/src/documentation/components/example.vue:
--------------------------------------------------------------------------------
1 |
2 | .example(:class="`example--${anchor}`")
3 | title-link.mb2(h2 :anchor="`ex--${anchor}`")
4 | slot(name="title") {{ title }}
5 | .example__desc(v-if="$slots.desc")
6 | slot(name="desc")
7 |
8 | .source-wrap.w-flex.column.gap3.mt3(
9 | v-if="$slots['code-html'] || $slots['code-js'] || $slots['code-css']"
10 | :class="{ expanded, 'no-scroll': sourceInnerNoScroll, [codeClass]: !!codeClass }")
11 | .source-inner.w-flex.column.gap3(ref="sourceInnerEl")
12 | ssh-pre.example__source(
13 | v-if="$slots['code-html']"
14 | language="html-vue"
15 | data-label="HTML"
16 | :dark="store.darkMode")
17 | slot(name="code-html")
18 | ssh-pre.example__source(
19 | v-if="$slots['code-js']"
20 | language="js"
21 | data-label="JS"
22 | :dark="store.darkMode")
23 | slot(name="code-js")
24 | ssh-pre.example__source(
25 | v-if="$slots['code-css']"
26 | language="css"
27 | data-label="CSS"
28 | :dark="store.darkMode")
29 | slot(name="code-css")
30 | w-button.example__source-expand(v-if="!sourceInnerNoScroll" sm round @click="onExpandClick")
31 | w-icon.mr1 mdi mdi-unfold-{{ expanded ? 'less' : 'more' }}-horizontal
32 | | {{ expanded ? 'Collapse' : 'Expand' }} Source Code
33 |
34 | .example__render.mt2.mxa
35 | slot
36 | .example__desc
37 | slot(name="desc2")
38 | p.caption.mt1 For all the options details, refer to the #[router-link(to="/api") API] section.
39 |
40 |
41 |
75 |
76 |
133 |
--------------------------------------------------------------------------------
/docs/assets/road-map-ObK9ptzE.js:
--------------------------------------------------------------------------------
1 | import{k as a,v as i}from"./index-7bpj6pqg.js";function e(v,s){return s[0]||(s[0]=a('Road Map
- 🎯 To DoDrag & drop events on edgesDrag & drop events on disabled dayDrag events over years/year/month viewsDrag & drop timeless eventsFurther optimize events recomputationRecurring events
- 🚧 DoingResize Multiple day events on touch devicesHorizontal layout option
- ✅ DoneMultiple day eventsImprove touch device UX (events VS scroll)All-day eventsEvent count on year viewsEvents overlap size optionSupport SSRImprove the event range accuracy while resizing upwardsEvents accept dynamic colorsEvents snap-to-interval while resizingAccept/reject event resizing while resizingAccept/reject event resizing after resizing and revert changesExpose overlapping events on resize and drag & dropI/O drag & drop eventsAccept/reject event drag & drop and expose overlapping eventsEvents overlap with schedulesEvents overlapDrag & drop eventsv-model:eventscustom days viewview day offsetaccept/reject event creationview.deleteEvent(id)view.createEvent()default active viewEnable / disable viewsHide / show weekendsAdd timeline w/ time range & incrementAdd timelinetime format 12/24 hSupport for i18nDay schedulesSupport eventsBackground eventsOverlap eventsKeep only default style in CSSAllow custom arrowsDefault active dateDouble tap on touch devices1st NPM Release!Built-in themesResize eventsDelete eventsEmit DOM eventsEvent indicator on month viewCustom time formatHighlight current timeShow events on month viewsync 2 vue-cal instancesAdd CSS transitionsMultiple day eventsCustom events renderingCustom callback on event clickOption to start week on SundayAll day events in top barCustom cell renderingEvents count on year(s) viewsCreate new eventmin & max datesToday buttonExternalize localesSupport more simultaneous eventsHide particular weekdaysOptional week numberDate prototypesBusiness hoursDrag & drop eventsResize events Snap to intervalDrag events Snap to intervalDrag & drop ext. events into Vue CalDisable daysSupport Vue 3
- 🕔 LaterSupport timezonesDrag & drop multiple day eventsImprove multiple day events Month viewHorizontal timeline
',2))}const t={},l=i(t,[["render",e]]);export{l as default};
2 |
--------------------------------------------------------------------------------
/src/vue-cal/components/header.vue:
--------------------------------------------------------------------------------
1 |
2 | .vuecal__header
3 | slot(
4 | name="header"
5 | :view="view"
6 | :available-views="config.availableViews"
7 | :vuecal="vuecal")
8 |
9 | template(v-if="!$slots.header")
10 | .vuecal__views-bar(v-if="config.viewsBar")
11 | button.vuecal__view-button(
12 | v-for="(obj, id) in config.availableViews"
13 | @click="view.switch(id)"
14 | v-html="vuecal.texts[id]"
15 | :class="{ 'vuecal__view-button--active': view.id === id }"
16 | type="button")
17 |
18 | nav.vuecal__title-bar(v-if="config.titleBar")
19 | button.vuecal__nav.vuecal__nav--prev(
20 | @click="view.previous"
21 | :class="{ 'vuecal__nav--default': !$slots['previous-button'] }"
22 | type="button")
23 | slot(name="previous-button")
24 | .vuecal__transition-wrap
25 | transition(:name="`vuecal-slide-fade--${view.transitionDirection}`")
26 | div(:key="view.id + view.start.getTime()")
27 | component.vuecal__title(
28 | v-if="$slots.title || $slots[`title.${view.id}`]"
29 | :is="config.clickToNavigate && view.broaderView ? 'button' : 'div'"
30 | v-on="titleEventHandlers")
31 | slot(v-if="$slots[`title.${view.id}`]" :name="`title.${view.id}`" v-bind="view")
32 | slot(v-else name="title" v-bind="view")
33 | component.vuecal__title(
34 | v-else
35 | :is="config.clickToNavigate && view.broaderView ? 'button' : 'div'"
36 | v-on="titleEventHandlers"
37 | v-html="view.title")
38 | template(v-if="config.todayButton")
39 | slot(
40 | v-if="$slots['today-button']"
41 | name="today-button"
42 | :navigate="() => !view.containsToday && view.goToToday()"
43 | :active="view.containsToday")
44 | button.vuecal__nav.vuecal__nav--today.vuecal__nav--default(
45 | v-else
46 | @click="!view.containsToday && view.goToToday()"
47 | :disabled="!!view.containsToday"
48 | :class="{ 'vuecal__nav--active': view.containsToday }"
49 | type="button"
50 | v-html="vuecal.texts.today")
51 | button.vuecal__nav.vuecal__nav--next(
52 | @click="view.next"
53 | :class="{ 'vuecal__nav--default': !$slots['next-button'] }"
54 | type="button")
55 | slot(name="next-button")
56 |
57 |
58 |
70 |
71 |
158 |
--------------------------------------------------------------------------------