@0001-labs/design-system-1 0.3.0-alpha.28 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DS1/0-face/button-label.ts +28 -0
- package/DS1/0-face/calendar.ts +6 -4
- package/DS1/0-face/carousel.ts +70 -0
- package/DS1/0-face/constraint-panel.ts +443 -0
- package/DS1/0-face/constraint-site.ts +201 -0
- package/DS1/0-face/constraints.ts +308 -0
- package/DS1/0-face/copy-text.ts +1 -1
- package/DS1/0-face/cut-fade.ts +312 -0
- package/DS1/0-face/define.ts +30 -2
- package/DS1/0-face/face-correction.ts +96 -0
- package/DS1/0-face/failures.ts +32 -0
- package/DS1/0-face/fault-lines.ts +159 -0
- package/DS1/0-face/feedback-overlays.ts +5 -3
- package/DS1/0-face/feedback.ts +3 -0
- package/DS1/0-face/focus-vision.swift +31 -0
- package/DS1/0-face/focus.ts +181 -0
- package/DS1/0-face/google-face-metrics.json +102 -0
- package/DS1/0-face/grid.ts +61 -4
- package/DS1/0-face/i18n.ts +36 -18
- package/DS1/0-face/layout-inspector.ts +124 -98
- package/DS1/0-face/preference-options.ts +4 -0
- package/DS1/0-face/scaling.ts +21 -3
- package/DS1/0-face/shape.ts +43 -0
- package/DS1/0-face/text-contrast.ts +164 -0
- package/DS1/0-face/theme-bootstrap.js +43 -0
- package/DS1/0-face/toast-queue.ts +7 -1
- package/DS1/0-face/translations/da.json +10 -1
- package/DS1/0-face/translations/de.json +10 -1
- package/DS1/0-face/translations/en.json +10 -1
- package/DS1/0-face/translations/es.json +10 -1
- package/DS1/0-face/translations/fi.json +10 -1
- package/DS1/0-face/translations/fr.json +10 -1
- package/DS1/0-face/translations/is.json +10 -1
- package/DS1/0-face/translations/it.json +10 -1
- package/DS1/0-face/translations/ja.json +10 -1
- package/DS1/0-face/translations/ko.json +10 -1
- package/DS1/0-face/translations/nb.json +10 -1
- package/DS1/0-face/translations/pt-BR.json +10 -1
- package/DS1/0-face/translations/pt.json +10 -1
- package/DS1/0-face/translations/sv.json +10 -1
- package/DS1/0-face/translations/zh.json +10 -1
- package/DS1/0-face/word-wrap.ts +113 -0
- package/DS1/1-root/card.css +47 -20
- package/DS1/1-root/carousel.css +7 -0
- package/DS1/1-root/cjk.css +1 -1
- package/DS1/1-root/component-tokens.css +3 -2
- package/DS1/1-root/context.css +44 -6
- package/DS1/1-root/controls.css +99 -8
- package/DS1/1-root/document.css +9 -1
- package/DS1/1-root/empty.css +46 -5
- package/DS1/1-root/figures.css +50 -3
- package/DS1/1-root/fonts.css +1 -1
- package/DS1/1-root/footer.css +147 -16
- package/DS1/1-root/foundation.ts +60 -0
- package/DS1/1-root/guide.css +35 -0
- package/DS1/1-root/header.css +30 -9
- package/DS1/1-root/material.css +67 -0
- package/DS1/1-root/notice.css +10 -2
- package/DS1/1-root/one.css +15 -7
- package/DS1/1-root/progress.css +9 -1
- package/DS1/1-root/row.css +68 -2
- package/DS1/1-root/table.css +47 -4
- package/DS1/1-root/text.css +26 -0
- package/DS1/2-core/button-split.ts +10 -5
- package/DS1/2-core/chart/chart.css +71 -2
- package/DS1/2-core/chart/grid-chart.ts +2 -1
- package/DS1/2-core/ds-activity.ts +32 -0
- package/DS1/2-core/ds-app-chrome.ts +1 -0
- package/DS1/2-core/ds-avatar.ts +75 -7
- package/DS1/2-core/ds-badge.ts +8 -1
- package/DS1/2-core/ds-button.ts +7 -1
- package/DS1/2-core/ds-chart.ts +184 -49
- package/DS1/2-core/ds-cycle.ts +27 -186
- package/DS1/2-core/ds-device-bezel.ts +1 -0
- package/DS1/2-core/ds-empty.ts +6 -4
- package/DS1/2-core/ds-input.ts +23 -3
- package/DS1/2-core/ds-message.ts +22 -0
- package/DS1/2-core/ds-number.ts +349 -0
- package/DS1/2-core/ds-progress.ts +3 -0
- package/DS1/2-core/ds-radio.ts +3 -0
- package/DS1/2-core/ds-stat.ts +3 -0
- package/DS1/2-core/ds-switch.ts +5 -1
- package/DS1/2-core/ds-tag.ts +9 -2
- package/DS1/2-core/ds-text.ts +7 -0
- package/DS1/2-core/ds-token.ts +3 -0
- package/DS1/2-core/ds-tooltip.ts +17 -2
- package/DS1/2-core/ds-window.ts +1 -0
- package/DS1/2-core/generated/ds-icon-map.ts +47 -5
- package/DS1/2-core/generated/icons-extra.ts +24 -3
- package/DS1/2-core/generated/icons-immediate.ts +3 -3
- package/DS1/2-core/number-expression.ts +64 -0
- package/DS1/2-core/styles/ds-activity.css +16 -0
- package/DS1/2-core/styles/ds-badge.css +4 -1
- package/DS1/2-core/styles/ds-button-host.css +47 -13
- package/DS1/2-core/styles/ds-button-split.css +8 -0
- package/DS1/2-core/styles/ds-button.css +94 -20
- package/DS1/2-core/styles/ds-card.css +6 -0
- package/DS1/2-core/styles/ds-checkbox.css +20 -8
- package/DS1/2-core/styles/ds-image.css +2 -1
- package/DS1/2-core/styles/ds-input.css +37 -4
- package/DS1/2-core/styles/ds-number.css +127 -0
- package/DS1/2-core/styles/ds-radio.css +4 -1
- package/DS1/2-core/styles/ds-switch.css +34 -4
- package/DS1/2-core/styles/ds-tag.css +34 -6
- package/DS1/2-core/styles/ds-text.css +13 -4
- package/DS1/2-core/styles/ds-token.css +4 -1
- package/DS1/2-core/styles/ds-tooltip.css +13 -7
- package/DS1/3-unit/calendar-base.ts +5 -2
- package/DS1/3-unit/context-trigger.ts +29 -2
- package/DS1/3-unit/ds-calendar.ts +711 -37
- package/DS1/3-unit/ds-carousel.ts +13 -1
- package/DS1/3-unit/ds-context.ts +40 -2
- package/DS1/3-unit/ds-form.ts +47 -11
- package/DS1/3-unit/ds-list.ts +31 -3
- package/DS1/3-unit/ds-matrix.ts +3 -0
- package/DS1/3-unit/ds-menu.ts +9 -5
- package/DS1/3-unit/ds-row.ts +17 -2
- package/DS1/3-unit/ds-select.ts +1 -1
- package/DS1/3-unit/ds-sidebar.ts +18 -3
- package/DS1/3-unit/ds-table-of-contents.ts +166 -0
- package/DS1/3-unit/ds-table.ts +2 -1
- package/DS1/3-unit/ds-tabs.ts +40 -2
- package/DS1/3-unit/styles/ds-calendar.css +356 -42
- package/DS1/3-unit/styles/ds-context.css +38 -0
- package/DS1/3-unit/styles/ds-list.css +26 -1
- package/DS1/3-unit/styles/ds-matrix.css +4 -1
- package/DS1/3-unit/styles/ds-row.css +39 -0
- package/DS1/3-unit/styles/ds-select.css +22 -12
- package/DS1/3-unit/styles/ds-sidebar.css +16 -2
- package/DS1/3-unit/styles/ds-table-of-contents.css +40 -0
- package/DS1/3-unit/styles/ds-tabs.css +95 -0
- package/DS1/4-page/ds-container.ts +2 -1
- package/DS1/4-page/ds-document.ts +3 -0
- package/DS1/4-page/ds-footer.ts +93 -18
- package/DS1/4-page/ds-header.ts +5 -1
- package/DS1/4-page/ds-overlay.ts +8 -3
- package/DS1/4-page/ds-page.ts +22 -1
- package/DS1/4-page/email.ts +7 -4
- package/DS1/4-page/styles/ds-container.css +0 -1
- package/DS1/4-page/styles/ds-overlay.css +7 -0
- package/DS1/4-page/styles/ds-page.css +10 -0
- package/DS1/5-visual-kit/app-chrome.ts +129 -0
- package/DS1/5-visual-kit/bezels.ts +394 -0
- package/DS1/5-visual-kit/browser-icons.LICENSE +53 -0
- package/DS1/5-visual-kit/browser-icons.ts +47 -0
- package/DS1/5-visual-kit/browser-styles.ts +53 -0
- package/DS1/5-visual-kit/device-bezel.ts +55 -0
- package/DS1/5-visual-kit/instagram-frame.ts +15 -0
- package/DS1/5-visual-kit/instagram.ts +41 -0
- package/DS1/5-visual-kit/mobile-chrome.ts +25 -0
- package/DS1/5-visual-kit/native-icon-paths.ts +145 -0
- package/DS1/5-visual-kit/window.ts +22 -0
- package/DS1/components.ts +1 -0
- package/DS1/index.ts +14 -1
- package/DS1/react/choice.ts +4 -2
- package/DS1/react/context.ts +11 -6
- package/DS1/react/empty.ts +2 -2
- package/DS1/react/footer.ts +5 -5
- package/DS1/react/header.ts +1 -1
- package/DS1/react/message.ts +4 -2
- package/DS1/react/number.ts +53 -0
- package/DS1/react/sidebar.ts +4 -2
- package/DS1/react/table-of-contents.ts +28 -0
- package/DS1/x-flag/LICENSE.md +26 -0
- package/DS1/x-flag/ac.svg +1 -0
- package/DS1/x-flag/ad.svg +1 -0
- package/DS1/x-flag/ae.svg +1 -0
- package/DS1/x-flag/af.svg +1 -0
- package/DS1/x-flag/ag.svg +1 -0
- package/DS1/x-flag/ai.svg +1 -0
- package/DS1/x-flag/al.svg +1 -0
- package/DS1/x-flag/am.svg +1 -0
- package/DS1/x-flag/ao.svg +1 -0
- package/DS1/x-flag/aq.svg +1 -0
- package/DS1/x-flag/ar.svg +1 -0
- package/DS1/x-flag/as.svg +1 -0
- package/DS1/x-flag/at.svg +1 -0
- package/DS1/x-flag/au.svg +1 -0
- package/DS1/x-flag/aw.svg +1 -0
- package/DS1/x-flag/ax.svg +1 -0
- package/DS1/x-flag/az.svg +1 -0
- package/DS1/x-flag/ba.svg +1 -0
- package/DS1/x-flag/bb.svg +1 -0
- package/DS1/x-flag/bd.svg +1 -0
- package/DS1/x-flag/be.svg +1 -0
- package/DS1/x-flag/bf.svg +1 -0
- package/DS1/x-flag/bg.svg +1 -0
- package/DS1/x-flag/bh.svg +1 -0
- package/DS1/x-flag/bi.svg +1 -0
- package/DS1/x-flag/bj.svg +1 -0
- package/DS1/x-flag/bl.svg +1 -0
- package/DS1/x-flag/bm.svg +1 -0
- package/DS1/x-flag/bn.svg +1 -0
- package/DS1/x-flag/bo.svg +1 -0
- package/DS1/x-flag/bq-bo.svg +1 -0
- package/DS1/x-flag/bq-sa.svg +1 -0
- package/DS1/x-flag/bq-se.svg +1 -0
- package/DS1/x-flag/bq.svg +1 -0
- package/DS1/x-flag/br.svg +1 -0
- package/DS1/x-flag/bs.svg +1 -0
- package/DS1/x-flag/bt.svg +1 -0
- package/DS1/x-flag/bv.svg +1 -0
- package/DS1/x-flag/bw.svg +1 -0
- package/DS1/x-flag/by.svg +1 -0
- package/DS1/x-flag/bz.svg +1 -0
- package/DS1/x-flag/ca.svg +1 -0
- package/DS1/x-flag/cc.svg +1 -0
- package/DS1/x-flag/cd.svg +1 -0
- package/DS1/x-flag/cf.svg +1 -0
- package/DS1/x-flag/cg.svg +1 -0
- package/DS1/x-flag/ch.svg +1 -0
- package/DS1/x-flag/ci.svg +1 -0
- package/DS1/x-flag/ck.svg +1 -0
- package/DS1/x-flag/cl.svg +1 -0
- package/DS1/x-flag/cm.svg +1 -0
- package/DS1/x-flag/cn.svg +1 -0
- package/DS1/x-flag/co.svg +1 -0
- package/DS1/x-flag/cr.svg +1 -0
- package/DS1/x-flag/cu.svg +1 -0
- package/DS1/x-flag/cv.svg +1 -0
- package/DS1/x-flag/cw.svg +1 -0
- package/DS1/x-flag/cx.svg +1 -0
- package/DS1/x-flag/cy.svg +1 -0
- package/DS1/x-flag/cz.svg +1 -0
- package/DS1/x-flag/de.svg +1 -0
- package/DS1/x-flag/dj.svg +1 -0
- package/DS1/x-flag/dk.svg +1 -0
- package/DS1/x-flag/dm.svg +1 -0
- package/DS1/x-flag/do.svg +1 -0
- package/DS1/x-flag/dz.svg +1 -0
- package/DS1/x-flag/ec.svg +1 -0
- package/DS1/x-flag/ee.svg +1 -0
- package/DS1/x-flag/eg.svg +1 -0
- package/DS1/x-flag/eh.svg +1 -0
- package/DS1/x-flag/er.svg +1 -0
- package/DS1/x-flag/es-ct.svg +1 -0
- package/DS1/x-flag/es.svg +1 -0
- package/DS1/x-flag/et.svg +1 -0
- package/DS1/x-flag/eu.svg +1 -0
- package/DS1/x-flag/fi.svg +1 -0
- package/DS1/x-flag/fj.svg +1 -0
- package/DS1/x-flag/fk.svg +1 -0
- package/DS1/x-flag/fm.svg +1 -0
- package/DS1/x-flag/fo.svg +1 -0
- package/DS1/x-flag/fr.svg +1 -0
- package/DS1/x-flag/ga.svg +1 -0
- package/DS1/x-flag/gb-eng.svg +1 -0
- package/DS1/x-flag/gb-nir.svg +1 -0
- package/DS1/x-flag/gb-sct.svg +1 -0
- package/DS1/x-flag/gb-wls.svg +1 -0
- package/DS1/x-flag/gb.svg +1 -0
- package/DS1/x-flag/gd.svg +1 -0
- package/DS1/x-flag/ge.svg +1 -0
- package/DS1/x-flag/gf.svg +1 -0
- package/DS1/x-flag/gg.svg +1 -0
- package/DS1/x-flag/gh.svg +1 -0
- package/DS1/x-flag/gi.svg +1 -0
- package/DS1/x-flag/gl.svg +1 -0
- package/DS1/x-flag/gm.svg +1 -0
- package/DS1/x-flag/gn.svg +1 -0
- package/DS1/x-flag/gp.svg +1 -0
- package/DS1/x-flag/gq.svg +1 -0
- package/DS1/x-flag/gr.svg +1 -0
- package/DS1/x-flag/gs.svg +1 -0
- package/DS1/x-flag/gt.svg +1 -0
- package/DS1/x-flag/gu.svg +1 -0
- package/DS1/x-flag/gw.svg +1 -0
- package/DS1/x-flag/gy.svg +1 -0
- package/DS1/x-flag/hk.svg +1 -0
- package/DS1/x-flag/hm.svg +1 -0
- package/DS1/x-flag/hn.svg +1 -0
- package/DS1/x-flag/hr.svg +1 -0
- package/DS1/x-flag/ht.svg +1 -0
- package/DS1/x-flag/hu.svg +1 -0
- package/DS1/x-flag/ic.svg +1 -0
- package/DS1/x-flag/id.svg +1 -0
- package/DS1/x-flag/ie.svg +1 -0
- package/DS1/x-flag/il.svg +1 -0
- package/DS1/x-flag/im.svg +1 -0
- package/DS1/x-flag/in.svg +1 -0
- package/DS1/x-flag/io.svg +1 -0
- package/DS1/x-flag/iq.svg +1 -0
- package/DS1/x-flag/ir.svg +1 -0
- package/DS1/x-flag/is.svg +1 -0
- package/DS1/x-flag/it.svg +1 -0
- package/DS1/x-flag/je.svg +1 -0
- package/DS1/x-flag/jm.svg +1 -0
- package/DS1/x-flag/jo.svg +1 -0
- package/DS1/x-flag/jp.svg +1 -0
- package/DS1/x-flag/ke.svg +1 -0
- package/DS1/x-flag/kg.svg +1 -0
- package/DS1/x-flag/kh.svg +1 -0
- package/DS1/x-flag/ki.svg +1 -0
- package/DS1/x-flag/km.svg +1 -0
- package/DS1/x-flag/kn.svg +1 -0
- package/DS1/x-flag/kp.svg +1 -0
- package/DS1/x-flag/kr.svg +1 -0
- package/DS1/x-flag/kw.svg +1 -0
- package/DS1/x-flag/ky.svg +1 -0
- package/DS1/x-flag/kz.svg +1 -0
- package/DS1/x-flag/la.svg +1 -0
- package/DS1/x-flag/lb.svg +1 -0
- package/DS1/x-flag/lc.svg +1 -0
- package/DS1/x-flag/li.svg +1 -0
- package/DS1/x-flag/lk.svg +1 -0
- package/DS1/x-flag/lr.svg +1 -0
- package/DS1/x-flag/ls.svg +1 -0
- package/DS1/x-flag/lt.svg +1 -0
- package/DS1/x-flag/lu.svg +1 -0
- package/DS1/x-flag/lv.svg +1 -0
- package/DS1/x-flag/ly.svg +1 -0
- package/DS1/x-flag/ma.svg +1 -0
- package/DS1/x-flag/mc.svg +1 -0
- package/DS1/x-flag/md.svg +1 -0
- package/DS1/x-flag/me.svg +1 -0
- package/DS1/x-flag/mf.svg +1 -0
- package/DS1/x-flag/mg.svg +1 -0
- package/DS1/x-flag/mh.svg +1 -0
- package/DS1/x-flag/mk.svg +1 -0
- package/DS1/x-flag/ml.svg +1 -0
- package/DS1/x-flag/mm.svg +1 -0
- package/DS1/x-flag/mn.svg +1 -0
- package/DS1/x-flag/mo.svg +1 -0
- package/DS1/x-flag/mp.svg +1 -0
- package/DS1/x-flag/mq.svg +1 -0
- package/DS1/x-flag/mr.svg +1 -0
- package/DS1/x-flag/ms.svg +1 -0
- package/DS1/x-flag/mt.svg +1 -0
- package/DS1/x-flag/mu.svg +1 -0
- package/DS1/x-flag/mv.svg +1 -0
- package/DS1/x-flag/mw.svg +1 -0
- package/DS1/x-flag/mx.svg +1 -0
- package/DS1/x-flag/my.svg +1 -0
- package/DS1/x-flag/mz.svg +1 -0
- package/DS1/x-flag/na.svg +1 -0
- package/DS1/x-flag/nc.svg +1 -0
- package/DS1/x-flag/ne.svg +1 -0
- package/DS1/x-flag/nf.svg +1 -0
- package/DS1/x-flag/ng.svg +1 -0
- package/DS1/x-flag/ni.svg +1 -0
- package/DS1/x-flag/nl.svg +1 -0
- package/DS1/x-flag/no.svg +1 -0
- package/DS1/x-flag/np.svg +1 -0
- package/DS1/x-flag/nr.svg +1 -0
- package/DS1/x-flag/nu.svg +1 -0
- package/DS1/x-flag/nz.svg +1 -0
- package/DS1/x-flag/om.svg +1 -0
- package/DS1/x-flag/pa.svg +1 -0
- package/DS1/x-flag/pe.svg +1 -0
- package/DS1/x-flag/pf.svg +1 -0
- package/DS1/x-flag/pg.svg +1 -0
- package/DS1/x-flag/ph.svg +1 -0
- package/DS1/x-flag/pk.svg +1 -0
- package/DS1/x-flag/pl.svg +1 -0
- package/DS1/x-flag/pm.svg +1 -0
- package/DS1/x-flag/pn.svg +1 -0
- package/DS1/x-flag/pr.svg +1 -0
- package/DS1/x-flag/ps.svg +1 -0
- package/DS1/x-flag/pt.svg +1 -0
- package/DS1/x-flag/pw.svg +1 -0
- package/DS1/x-flag/py.svg +1 -0
- package/DS1/x-flag/qa.svg +1 -0
- package/DS1/x-flag/re.svg +1 -0
- package/DS1/x-flag/ro.svg +1 -0
- package/DS1/x-flag/rs.svg +1 -0
- package/DS1/x-flag/ru.svg +1 -0
- package/DS1/x-flag/rw.svg +1 -0
- package/DS1/x-flag/sa.svg +1 -0
- package/DS1/x-flag/sb.svg +1 -0
- package/DS1/x-flag/sc.svg +1 -0
- package/DS1/x-flag/sd.svg +1 -0
- package/DS1/x-flag/se.svg +1 -0
- package/DS1/x-flag/sg.svg +1 -0
- package/DS1/x-flag/sh.svg +1 -0
- package/DS1/x-flag/si.svg +1 -0
- package/DS1/x-flag/sj.svg +1 -0
- package/DS1/x-flag/sk.svg +1 -0
- package/DS1/x-flag/sl.svg +1 -0
- package/DS1/x-flag/sm.svg +1 -0
- package/DS1/x-flag/sn.svg +1 -0
- package/DS1/x-flag/so.svg +1 -0
- package/DS1/x-flag/sr.svg +1 -0
- package/DS1/x-flag/ss.svg +1 -0
- package/DS1/x-flag/st.svg +1 -0
- package/DS1/x-flag/sv.svg +1 -0
- package/DS1/x-flag/sx.svg +1 -0
- package/DS1/x-flag/sy.svg +1 -0
- package/DS1/x-flag/sz.svg +1 -0
- package/DS1/x-flag/ta.svg +1 -0
- package/DS1/x-flag/tc.svg +1 -0
- package/DS1/x-flag/td.svg +1 -0
- package/DS1/x-flag/tf.svg +1 -0
- package/DS1/x-flag/tg.svg +1 -0
- package/DS1/x-flag/th.svg +1 -0
- package/DS1/x-flag/tj.svg +1 -0
- package/DS1/x-flag/tk.svg +1 -0
- package/DS1/x-flag/tl.svg +1 -0
- package/DS1/x-flag/tm.svg +1 -0
- package/DS1/x-flag/tn.svg +1 -0
- package/DS1/x-flag/to.svg +1 -0
- package/DS1/x-flag/tr.svg +1 -0
- package/DS1/x-flag/tt.svg +1 -0
- package/DS1/x-flag/tv.svg +1 -0
- package/DS1/x-flag/tw.svg +1 -0
- package/DS1/x-flag/tz.svg +1 -0
- package/DS1/x-flag/ua.svg +1 -0
- package/DS1/x-flag/ug.svg +1 -0
- package/DS1/x-flag/um.svg +1 -0
- package/DS1/x-flag/us.svg +1 -0
- package/DS1/x-flag/uy.svg +1 -0
- package/DS1/x-flag/uz.svg +1 -0
- package/DS1/x-flag/va.svg +1 -0
- package/DS1/x-flag/vc.svg +1 -0
- package/DS1/x-flag/ve.svg +1 -0
- package/DS1/x-flag/vg.svg +1 -0
- package/DS1/x-flag/vi.svg +1 -0
- package/DS1/x-flag/vn.svg +1 -0
- package/DS1/x-flag/vu.svg +1 -0
- package/DS1/x-flag/wf.svg +1 -0
- package/DS1/x-flag/ws.svg +1 -0
- package/DS1/x-flag/xk.svg +1 -0
- package/DS1/x-flag/ye.svg +1 -0
- package/DS1/x-flag/yt.svg +1 -0
- package/DS1/x-flag/za.svg +1 -0
- package/DS1/x-flag/zm.svg +1 -0
- package/DS1/x-flag/zw.svg +1 -0
- package/DS1/x-icon/android.svg +9 -0
- package/DS1/x-icon/arrange-check.svg +1 -1
- package/DS1/x-icon/arrange-page.svg +1 -1
- package/DS1/x-icon/chrome.svg +7 -0
- package/DS1/x-icon/edge-brand.svg +6 -0
- package/DS1/x-icon/firefox.svg +4 -0
- package/DS1/x-icon/health.svg +3 -0
- package/DS1/x-icon/left.svg +2 -3
- package/DS1/x-icon/linux.svg +1 -0
- package/DS1/x-icon/material.svg +1 -0
- package/DS1/x-icon/milestone-planned.svg +3 -0
- package/DS1/x-icon/milestone.svg +3 -0
- package/DS1/x-icon/more.svg +1 -5
- package/DS1/x-icon/network.svg +3 -0
- package/DS1/x-icon/opera.svg +4 -0
- package/DS1/x-icon/prediction.svg +3 -0
- package/DS1/x-icon/priority-high.svg +3 -0
- package/DS1/x-icon/priority-low.svg +3 -0
- package/DS1/x-icon/priority-medium.svg +3 -0
- package/DS1/x-icon/project.svg +3 -0
- package/DS1/x-icon/redo.svg +2 -2
- package/DS1/x-icon/repeat-small.svg +3 -0
- package/DS1/x-icon/safari.svg +4 -0
- package/DS1/x-icon/scaling.svg +3 -0
- package/DS1/x-icon/tabs.svg +4 -0
- package/DS1/x-icon/windows.svg +5 -0
- package/README.md +3 -3
- package/dist/0-face/button-label.d.ts +5 -0
- package/dist/0-face/button-label.d.ts.map +1 -0
- package/dist/0-face/button-label.js +31 -0
- package/dist/0-face/calendar.d.ts +1 -1
- package/dist/0-face/calendar.d.ts.map +1 -1
- package/dist/0-face/calendar.js +6 -3
- package/dist/0-face/carousel.d.ts +9 -0
- package/dist/0-face/carousel.d.ts.map +1 -1
- package/dist/0-face/carousel.js +80 -0
- package/dist/0-face/constraint-panel.d.ts +43 -0
- package/dist/0-face/constraint-panel.d.ts.map +1 -0
- package/dist/0-face/constraint-panel.js +451 -0
- package/dist/0-face/constraint-site.d.ts +73 -0
- package/dist/0-face/constraint-site.d.ts.map +1 -0
- package/dist/0-face/constraint-site.js +186 -0
- package/dist/0-face/constraints.d.ts +47 -0
- package/dist/0-face/constraints.d.ts.map +1 -0
- package/dist/0-face/constraints.js +274 -0
- package/dist/0-face/copy-text.js +1 -1
- package/dist/0-face/cut-fade.d.ts +112 -0
- package/dist/0-face/cut-fade.d.ts.map +1 -0
- package/dist/0-face/cut-fade.js +274 -0
- package/dist/0-face/define.d.ts.map +1 -1
- package/dist/0-face/define.js +24 -3
- package/dist/0-face/face-correction.d.ts +51 -0
- package/dist/0-face/face-correction.d.ts.map +1 -0
- package/dist/0-face/face-correction.js +80 -0
- package/dist/0-face/failures.d.ts +16 -0
- package/dist/0-face/failures.d.ts.map +1 -0
- package/dist/0-face/failures.js +21 -0
- package/dist/0-face/fault-lines.d.ts +20 -0
- package/dist/0-face/fault-lines.d.ts.map +1 -0
- package/dist/0-face/fault-lines.js +172 -0
- package/dist/0-face/feedback-overlays.d.ts.map +1 -1
- package/dist/0-face/feedback-overlays.js +5 -3
- package/dist/0-face/feedback.d.ts +2 -0
- package/dist/0-face/feedback.d.ts.map +1 -1
- package/dist/0-face/feedback.js +1 -0
- package/dist/0-face/focus.d.ts +71 -0
- package/dist/0-face/focus.d.ts.map +1 -0
- package/dist/0-face/focus.js +196 -0
- package/dist/0-face/grid.d.ts +11 -1
- package/dist/0-face/grid.d.ts.map +1 -1
- package/dist/0-face/grid.js +90 -4
- package/dist/0-face/i18n.d.ts.map +1 -1
- package/dist/0-face/i18n.js +38 -16
- package/dist/0-face/layout-inspector.d.ts +5 -1
- package/dist/0-face/layout-inspector.d.ts.map +1 -1
- package/dist/0-face/layout-inspector.js +130 -104
- package/dist/0-face/preference-options.d.ts +4 -0
- package/dist/0-face/preference-options.d.ts.map +1 -1
- package/dist/0-face/scaling.d.ts +8 -1
- package/dist/0-face/scaling.d.ts.map +1 -1
- package/dist/0-face/scaling.js +19 -2
- package/dist/0-face/shape.d.ts +35 -0
- package/dist/0-face/shape.d.ts.map +1 -0
- package/dist/0-face/shape.js +23 -0
- package/dist/0-face/text-contrast.d.ts +22 -0
- package/dist/0-face/text-contrast.d.ts.map +1 -0
- package/dist/0-face/text-contrast.js +178 -0
- package/dist/0-face/theme-bootstrap.js +56 -0
- package/dist/0-face/toast-queue.d.ts +4 -0
- package/dist/0-face/toast-queue.d.ts.map +1 -1
- package/dist/0-face/toast-queue.js +4 -1
- package/dist/0-face/translations/da.json +10 -1
- package/dist/0-face/translations/de.json +10 -1
- package/dist/0-face/translations/en.json +10 -1
- package/dist/0-face/translations/es.json +10 -1
- package/dist/0-face/translations/fi.json +10 -1
- package/dist/0-face/translations/fr.json +10 -1
- package/dist/0-face/translations/is.json +10 -1
- package/dist/0-face/translations/it.json +10 -1
- package/dist/0-face/translations/ja.json +10 -1
- package/dist/0-face/translations/ko.json +10 -1
- package/dist/0-face/translations/nb.json +10 -1
- package/dist/0-face/translations/pt-BR.json +10 -1
- package/dist/0-face/translations/pt.json +10 -1
- package/dist/0-face/translations/sv.json +10 -1
- package/dist/0-face/translations/zh.json +10 -1
- package/dist/0-face/word-wrap.d.ts +6 -0
- package/dist/0-face/word-wrap.d.ts.map +1 -0
- package/dist/0-face/word-wrap.js +119 -0
- package/dist/1-root/foundation.d.ts +12 -0
- package/dist/1-root/foundation.d.ts.map +1 -0
- package/dist/1-root/foundation.js +59 -0
- package/dist/2-core/button-split.d.ts +1 -0
- package/dist/2-core/button-split.d.ts.map +1 -1
- package/dist/2-core/button-split.js +9 -4
- package/dist/2-core/chart/grid-chart.d.ts +2 -0
- package/dist/2-core/chart/grid-chart.d.ts.map +1 -1
- package/dist/2-core/chart/grid-chart.js +2 -1
- package/dist/2-core/ds-activity.d.ts +25 -0
- package/dist/2-core/ds-activity.d.ts.map +1 -0
- package/dist/2-core/ds-activity.js +28 -0
- package/dist/2-core/ds-app-chrome.d.ts +2 -0
- package/dist/2-core/ds-app-chrome.d.ts.map +1 -0
- package/dist/2-core/ds-app-chrome.js +1 -0
- package/dist/2-core/ds-avatar.d.ts +12 -0
- package/dist/2-core/ds-avatar.d.ts.map +1 -1
- package/dist/2-core/ds-avatar.js +86 -7
- package/dist/2-core/ds-badge.d.ts +7 -1
- package/dist/2-core/ds-badge.d.ts.map +1 -1
- package/dist/2-core/ds-badge.js +8 -1
- package/dist/2-core/ds-button.d.ts +12 -0
- package/dist/2-core/ds-button.d.ts.map +1 -1
- package/dist/2-core/ds-button.js +4 -0
- package/dist/2-core/ds-chart.d.ts +28 -3
- package/dist/2-core/ds-chart.d.ts.map +1 -1
- package/dist/2-core/ds-chart.js +110 -130
- package/dist/2-core/ds-cycle.d.ts +2 -2
- package/dist/2-core/ds-cycle.d.ts.map +1 -1
- package/dist/2-core/ds-cycle.js +36 -171
- package/dist/2-core/ds-device-bezel.d.ts +2 -0
- package/dist/2-core/ds-device-bezel.d.ts.map +1 -0
- package/dist/2-core/ds-device-bezel.js +1 -0
- package/dist/2-core/ds-empty.d.ts +2 -2
- package/dist/2-core/ds-empty.d.ts.map +1 -1
- package/dist/2-core/ds-empty.js +6 -4
- package/dist/2-core/ds-input.d.ts +7 -0
- package/dist/2-core/ds-input.d.ts.map +1 -1
- package/dist/2-core/ds-input.js +23 -3
- package/dist/2-core/ds-message.d.ts +11 -0
- package/dist/2-core/ds-message.d.ts.map +1 -1
- package/dist/2-core/ds-message.js +18 -0
- package/dist/2-core/ds-number.d.ts +86 -0
- package/dist/2-core/ds-number.d.ts.map +1 -0
- package/dist/2-core/ds-number.js +326 -0
- package/dist/2-core/ds-progress.d.ts +3 -0
- package/dist/2-core/ds-progress.d.ts.map +1 -1
- package/dist/2-core/ds-progress.js +3 -0
- package/dist/2-core/ds-radio.d.ts +3 -0
- package/dist/2-core/ds-radio.d.ts.map +1 -1
- package/dist/2-core/ds-radio.js +10 -1
- package/dist/2-core/ds-stat.d.ts +3 -0
- package/dist/2-core/ds-stat.d.ts.map +1 -1
- package/dist/2-core/ds-stat.js +3 -0
- package/dist/2-core/ds-switch.d.ts +14 -1
- package/dist/2-core/ds-switch.d.ts.map +1 -1
- package/dist/2-core/ds-switch.js +3 -1
- package/dist/2-core/ds-tag.d.ts +8 -1
- package/dist/2-core/ds-tag.d.ts.map +1 -1
- package/dist/2-core/ds-tag.js +12 -2
- package/dist/2-core/ds-text.d.ts +7 -0
- package/dist/2-core/ds-text.d.ts.map +1 -1
- package/dist/2-core/ds-text.js +7 -0
- package/dist/2-core/ds-token.d.ts +3 -0
- package/dist/2-core/ds-token.d.ts.map +1 -1
- package/dist/2-core/ds-token.js +3 -0
- package/dist/2-core/ds-tooltip.d.ts.map +1 -1
- package/dist/2-core/ds-tooltip.js +23 -2
- package/dist/2-core/ds-window.d.ts +2 -0
- package/dist/2-core/ds-window.d.ts.map +1 -0
- package/dist/2-core/ds-window.js +1 -0
- package/dist/2-core/generated/ds-icon-map.d.ts +27 -6
- package/dist/2-core/generated/ds-icon-map.d.ts.map +1 -1
- package/dist/2-core/generated/ds-icon-map.js +47 -5
- package/dist/2-core/generated/icons-extra.d.ts.map +1 -1
- package/dist/2-core/generated/icons-extra.js +24 -3
- package/dist/2-core/generated/icons-immediate.d.ts +1 -1
- package/dist/2-core/generated/icons-immediate.d.ts.map +1 -1
- package/dist/2-core/generated/icons-immediate.js +3 -3
- package/dist/2-core/number-expression.d.ts +12 -0
- package/dist/2-core/number-expression.d.ts.map +1 -0
- package/dist/2-core/number-expression.js +82 -0
- package/dist/3-unit/calendar-base.d.ts +5 -0
- package/dist/3-unit/calendar-base.d.ts.map +1 -1
- package/dist/3-unit/calendar-base.js +3 -2
- package/dist/3-unit/context-trigger.d.ts.map +1 -1
- package/dist/3-unit/context-trigger.js +32 -2
- package/dist/3-unit/ds-calendar.d.ts +197 -4
- package/dist/3-unit/ds-calendar.d.ts.map +1 -1
- package/dist/3-unit/ds-calendar.js +687 -45
- package/dist/3-unit/ds-carousel.d.ts +3 -0
- package/dist/3-unit/ds-carousel.d.ts.map +1 -1
- package/dist/3-unit/ds-carousel.js +13 -1
- package/dist/3-unit/ds-context.d.ts +18 -1
- package/dist/3-unit/ds-context.d.ts.map +1 -1
- package/dist/3-unit/ds-context.js +37 -1
- package/dist/3-unit/ds-form.d.ts +9 -0
- package/dist/3-unit/ds-form.d.ts.map +1 -1
- package/dist/3-unit/ds-form.js +52 -11
- package/dist/3-unit/ds-list.d.ts +20 -1
- package/dist/3-unit/ds-list.d.ts.map +1 -1
- package/dist/3-unit/ds-list.js +31 -3
- package/dist/3-unit/ds-matrix.d.ts +3 -0
- package/dist/3-unit/ds-matrix.d.ts.map +1 -1
- package/dist/3-unit/ds-matrix.js +3 -0
- package/dist/3-unit/ds-menu.d.ts.map +1 -1
- package/dist/3-unit/ds-menu.js +17 -1
- package/dist/3-unit/ds-row.d.ts +13 -0
- package/dist/3-unit/ds-row.d.ts.map +1 -1
- package/dist/3-unit/ds-row.js +15 -2
- package/dist/3-unit/ds-select.js +1 -1
- package/dist/3-unit/ds-sidebar.d.ts +17 -0
- package/dist/3-unit/ds-sidebar.d.ts.map +1 -1
- package/dist/3-unit/ds-sidebar.js +14 -3
- package/dist/3-unit/ds-table-of-contents.d.ts +68 -0
- package/dist/3-unit/ds-table-of-contents.d.ts.map +1 -0
- package/dist/3-unit/ds-table-of-contents.js +190 -0
- package/dist/3-unit/ds-table.d.ts.map +1 -1
- package/dist/3-unit/ds-table.js +2 -1
- package/dist/3-unit/ds-tabs.d.ts +32 -1
- package/dist/3-unit/ds-tabs.d.ts.map +1 -1
- package/dist/3-unit/ds-tabs.js +39 -1
- package/dist/4-page/ds-container.d.ts.map +1 -1
- package/dist/4-page/ds-container.js +2 -1
- package/dist/4-page/ds-document.d.ts +3 -0
- package/dist/4-page/ds-document.d.ts.map +1 -1
- package/dist/4-page/ds-document.js +3 -0
- package/dist/4-page/ds-footer.d.ts +47 -7
- package/dist/4-page/ds-footer.d.ts.map +1 -1
- package/dist/4-page/ds-footer.js +58 -14
- package/dist/4-page/ds-header.d.ts +6 -0
- package/dist/4-page/ds-header.d.ts.map +1 -1
- package/dist/4-page/ds-header.js +4 -1
- package/dist/4-page/ds-overlay.d.ts +2 -2
- package/dist/4-page/ds-overlay.d.ts.map +1 -1
- package/dist/4-page/ds-overlay.js +8 -3
- package/dist/4-page/ds-page.d.ts +12 -0
- package/dist/4-page/ds-page.d.ts.map +1 -1
- package/dist/4-page/ds-page.js +19 -1
- package/dist/4-page/email.d.ts +2 -0
- package/dist/4-page/email.d.ts.map +1 -1
- package/dist/4-page/email.js +5 -4
- package/dist/5-visual-kit/app-chrome.d.ts +98 -0
- package/dist/5-visual-kit/app-chrome.d.ts.map +1 -0
- package/dist/5-visual-kit/app-chrome.js +119 -0
- package/dist/5-visual-kit/bezels.d.ts +30 -0
- package/dist/5-visual-kit/bezels.d.ts.map +1 -0
- package/dist/5-visual-kit/bezels.js +388 -0
- package/dist/5-visual-kit/browser-icons.d.ts +5 -0
- package/dist/5-visual-kit/browser-icons.d.ts.map +1 -0
- package/dist/5-visual-kit/browser-icons.js +51 -0
- package/dist/5-visual-kit/browser-styles.d.ts +3 -0
- package/dist/5-visual-kit/browser-styles.d.ts.map +1 -0
- package/dist/5-visual-kit/browser-styles.js +52 -0
- package/dist/5-visual-kit/device-bezel.d.ts +84 -0
- package/dist/5-visual-kit/device-bezel.d.ts.map +1 -0
- package/dist/5-visual-kit/device-bezel.js +56 -0
- package/dist/5-visual-kit/instagram-frame.d.ts +8 -0
- package/dist/5-visual-kit/instagram-frame.d.ts.map +1 -0
- package/dist/5-visual-kit/instagram-frame.js +13 -0
- package/dist/5-visual-kit/instagram.d.ts +18 -0
- package/dist/5-visual-kit/instagram.d.ts.map +1 -0
- package/dist/5-visual-kit/instagram.js +39 -0
- package/dist/5-visual-kit/mobile-chrome.d.ts +4 -0
- package/dist/5-visual-kit/mobile-chrome.d.ts.map +1 -0
- package/dist/5-visual-kit/mobile-chrome.js +24 -0
- package/dist/5-visual-kit/native-icon-paths.d.ts +146 -0
- package/dist/5-visual-kit/native-icon-paths.d.ts.map +1 -0
- package/dist/5-visual-kit/native-icon-paths.js +145 -0
- package/dist/5-visual-kit/window.d.ts +51 -0
- package/dist/5-visual-kit/window.d.ts.map +1 -0
- package/dist/5-visual-kit/window.js +20 -0
- package/dist/browser-icons-BFbS1tIx.js +56 -0
- package/dist/browser-icons-BFbS1tIx.js.map +1 -0
- package/dist/browser-icons-CT8qU77Z.js +48 -0
- package/dist/browser-icons-CT8qU77Z.js.map +1 -0
- package/dist/components.d.ts +1 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +1 -0
- package/dist/design-system-1.bundle.js +5874 -2097
- package/dist/design-system-1.bundle.js.map +1 -1
- package/dist/design-system-1.bundle.min.js +7332 -4682
- package/dist/design-system-1.bundle.min.js.map +1 -1
- package/dist/design-system-1.css +1 -1
- package/dist/feedback.js +197 -167
- package/dist/feedback.js.map +1 -1
- package/dist/{icons-extra-CRxxWFjX.js → icons-extra-BMPvmQPI.js} +25 -4
- package/dist/icons-extra-BMPvmQPI.js.map +1 -0
- package/dist/{icons-extra-H5pqCM_R.js → icons-extra-BfYOw-cR.js} +83 -5
- package/dist/icons-extra-BfYOw-cR.js.map +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/instagram-CKofs55A.js +50 -0
- package/dist/instagram-CKofs55A.js.map +1 -0
- package/dist/instagram-CcYf5oiM.js +48 -0
- package/dist/instagram-CcYf5oiM.js.map +1 -0
- package/dist/lit-Ce_uWYM4.js +612 -0
- package/dist/lit-Ce_uWYM4.js.map +1 -0
- package/dist/lit-DGUVm1Ce.js +557 -0
- package/dist/lit-DGUVm1Ce.js.map +1 -0
- package/dist/native-icon-paths-CB9evXBM.js +93 -0
- package/dist/native-icon-paths-CB9evXBM.js.map +1 -0
- package/dist/native-icon-paths-zay-3g_n.js +150 -0
- package/dist/native-icon-paths-zay-3g_n.js.map +1 -0
- package/dist/react/avatar.d.ts +1 -1
- package/dist/react/choice.d.ts +4 -0
- package/dist/react/choice.d.ts.map +1 -1
- package/dist/react/choice.js +2 -2
- package/dist/react/context.d.ts +19 -4
- package/dist/react/context.d.ts.map +1 -1
- package/dist/react/context.js +4 -4
- package/dist/react/empty.d.ts +2 -2
- package/dist/react/empty.d.ts.map +1 -1
- package/dist/react/empty.js +1 -1
- package/dist/react/footer.d.ts +5 -5
- package/dist/react/footer.d.ts.map +1 -1
- package/dist/react/footer.js +2 -2
- package/dist/react/header.d.ts +2 -2
- package/dist/react/header.d.ts.map +1 -1
- package/dist/react/message.d.ts +4 -0
- package/dist/react/message.d.ts.map +1 -1
- package/dist/react/message.js +2 -2
- package/dist/react/number.d.ts +53 -0
- package/dist/react/number.d.ts.map +1 -0
- package/dist/react/number.js +25 -0
- package/dist/react/page.d.ts +1 -1
- package/dist/react/sidebar.d.ts +4 -0
- package/dist/react/sidebar.d.ts.map +1 -1
- package/dist/react/sidebar.js +2 -2
- package/dist/react/table-of-contents.d.ts +19 -0
- package/dist/react/table-of-contents.d.ts.map +1 -0
- package/dist/react/table-of-contents.js +28 -0
- package/dist/{shared-words-CdMDVtXx.js → shared-words-C5cjxMjZ.js} +151 -16
- package/dist/shared-words-C5cjxMjZ.js.map +1 -0
- package/dist/{shared-words-BtRee8xR.js → shared-words-Cdm7xd-_.js} +163 -28
- package/dist/shared-words-Cdm7xd-_.js.map +1 -0
- package/package.json +25 -12
- package/DS1/3-unit/ds-calendar-large.ts +0 -353
- package/DS1/3-unit/styles/ds-calendar-large.css +0 -178
- package/dist/3-unit/ds-calendar-large.d.ts +0 -142
- package/dist/3-unit/ds-calendar-large.d.ts.map +0 -1
- package/dist/3-unit/ds-calendar-large.js +0 -308
- package/dist/icons-extra-CRxxWFjX.js.map +0 -1
- package/dist/icons-extra-H5pqCM_R.js.map +0 -1
- package/dist/shared-words-BtRee8xR.js.map +0 -1
- package/dist/shared-words-CdMDVtXx.js.map +0 -1
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
// ds-calendar.ts
|
|
2
|
-
// A month on whole squares: seven days across, a week to a row.
|
|
2
|
+
// A month on whole squares: seven days across, a week to a row. `view` shows
|
|
3
|
+
// one week instead (every event of each day, nothing folded behind "+N") or one
|
|
4
|
+
// day (its events without a time across the top, then its hours, one square
|
|
5
|
+
// each, with the timed events at their hour) or a swimlane (weeks across, a
|
|
6
|
+
// row to an event, each event one bar from its first week to its last);
|
|
7
|
+
// `views` puts Day, Week, Month and Swimlane in the header to change between
|
|
8
|
+
// them. An event over several days is one bar
|
|
9
|
+
// with its name written once, and words that do not fit fade out at the end.
|
|
10
|
+
// By default a
|
|
3
11
|
// day is four squares wide and four high (its date on the first square, two
|
|
4
|
-
// lines of events, one square to spare), drawn in hairline boxes
|
|
12
|
+
// lines of events, one square to spare), drawn in hairline boxes with a frame
|
|
13
|
+
// rounded more than a card's; the first week names its days inside its boxes,
|
|
14
|
+
// and a step back, Today and a step on are one plain split. `compact`
|
|
5
15
|
// makes a day one square: the small month a year is drawn from. `picker`
|
|
6
16
|
// makes the calendar a field that opens that small month below it, with the
|
|
7
17
|
// first and last day written out where they can be typed, a switch for a
|
|
@@ -14,11 +24,15 @@
|
|
|
14
24
|
|
|
15
25
|
import { html, nothing, unsafeCSS, type PropertyValues } from "lit";
|
|
16
26
|
import { define } from "../0-face/define";
|
|
27
|
+
import { CutFades } from "../0-face/cut-fade";
|
|
17
28
|
import { getText } from "../0-face/i18n";
|
|
18
29
|
import { dateFormatter, type DateLength } from "../0-face/format";
|
|
19
30
|
import {
|
|
31
|
+
addDays,
|
|
20
32
|
addMonths,
|
|
33
|
+
compareDays,
|
|
21
34
|
dateNumber,
|
|
35
|
+
dayLabel,
|
|
22
36
|
dayOf,
|
|
23
37
|
eventsByDay,
|
|
24
38
|
heatLevels,
|
|
@@ -30,21 +44,148 @@ import {
|
|
|
30
44
|
sameMonth,
|
|
31
45
|
startOfMonth,
|
|
32
46
|
weekdayNames,
|
|
47
|
+
weekdayOf,
|
|
33
48
|
type CalendarEvent,
|
|
34
49
|
type Day,
|
|
35
50
|
type HeatLevel,
|
|
36
51
|
} from "../0-face/calendar";
|
|
37
52
|
import { CalendarBase, readMonth, readRange, type CalendarChangeDetail } from "./calendar-base";
|
|
38
53
|
import "../2-core/ds-icon";
|
|
54
|
+
import "../2-core/ds-button";
|
|
39
55
|
import metrics from "../1-root/component-metrics.css?inline";
|
|
40
56
|
import controls from "../1-root/controls.css?inline";
|
|
41
57
|
import styles from "./styles/ds-calendar.css?inline";
|
|
42
58
|
|
|
43
59
|
export * from "./calendar-base";
|
|
44
60
|
|
|
61
|
+
/** An activity day's fill: one colour, or bands side by side when several share it. */
|
|
62
|
+
function activityFill(events: readonly CalendarEvent[]): string | undefined {
|
|
63
|
+
if (!events.length) return undefined;
|
|
64
|
+
const colours = [...new Set(events.map(event => event.color || "var(--ds-calendar-event-fill, var(--ds-apple-green))"))];
|
|
65
|
+
if (colours.length === 1) return colours[0];
|
|
66
|
+
const share = 100 / colours.length;
|
|
67
|
+
return `linear-gradient(90deg, ${colours.map((colour, index) => `${colour} ${index * share}% ${(index + 1) * share}%`).join(", ")})`;
|
|
68
|
+
}
|
|
69
|
+
|
|
45
70
|
const whole = (value: number | undefined, least: number, most: number, fallback: number) =>
|
|
46
71
|
typeof value === "number" && Number.isFinite(value) ? Math.max(least, Math.min(most, Math.floor(value))) : fallback;
|
|
47
72
|
|
|
73
|
+
/**
|
|
74
|
+
* A day's lines of events in a full month: the event that starts a bar on a
|
|
75
|
+
* line (and how many days the bar runs), `"on"` where a bar from an earlier
|
|
76
|
+
* day passes over the line, or null where the line is empty.
|
|
77
|
+
*/
|
|
78
|
+
/** What a full calendar shows: a month, a week, a day, or the events as a swimlane over weeks. */
|
|
79
|
+
export type CalendarView = "month" | "week" | "day" | "swimlane";
|
|
80
|
+
|
|
81
|
+
/** The views in the header, the shortest first, with their shared words. */
|
|
82
|
+
const VIEWS: readonly (readonly [CalendarView, string])[] = [["day", "Day"], ["week", "Week"], ["month", "Month"], ["swimlane", "Swimlane"]];
|
|
83
|
+
|
|
84
|
+
const isView = (value: unknown): value is CalendarView => VIEWS.some(([view]) => view === value);
|
|
85
|
+
|
|
86
|
+
/** `views` as an attribute: present and empty for every view the calendar draws, or the views to offer, in order. */
|
|
87
|
+
const viewsConverter = {
|
|
88
|
+
fromAttribute: (value: string | null): boolean | string[] => {
|
|
89
|
+
if (value === null || value === "false") return false;
|
|
90
|
+
const list = value.split(/[\s,]+/).filter(Boolean);
|
|
91
|
+
return list.length === 0 || value === "true" ? true : list;
|
|
92
|
+
},
|
|
93
|
+
toAttribute: (value: boolean | string[]) => (Array.isArray(value) ? value.join(" ") : value ? "" : null),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/** One event's bar in a swimlane: its row, and the first and last week (counted from 0) it covers in view. */
|
|
97
|
+
export interface SwimlaneBar {
|
|
98
|
+
event: CalendarEvent;
|
|
99
|
+
row: number;
|
|
100
|
+
from: number;
|
|
101
|
+
to: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Lays events out as a swimlane over `weeks` (each the first day of a week
|
|
106
|
+
* of seven): one row each, in the order `eventsByDay` lists them (by first
|
|
107
|
+
* day, the longer first), each a bar from the week holding its first day to
|
|
108
|
+
* the week holding its last, held inside the weeks drawn. Events wholly
|
|
109
|
+
* outside them are left out.
|
|
110
|
+
*/
|
|
111
|
+
export function swimlaneBars(events: readonly CalendarEvent[], weeks: readonly Day[]): SwimlaneBar[] {
|
|
112
|
+
if (!weeks.length) return [];
|
|
113
|
+
const first = weeks[0], last = addDays(weeks.at(-1)!, 6);
|
|
114
|
+
const seen = new Set<CalendarEvent>();
|
|
115
|
+
const ordered: CalendarEvent[] = [];
|
|
116
|
+
for (const list of eventsByDay(events, first, last).values()) for (const event of list) if (!seen.has(event)) { seen.add(event); ordered.push(event); }
|
|
117
|
+
ordered.sort((a, b) => compareDays(a.start, b.start) || compareDays(endOf(b), endOf(a)));
|
|
118
|
+
const weekOf = (day: Day) => Math.max(0, Math.min(weeks.length - 1, Math.floor(daysBetween(first, day) / 7)));
|
|
119
|
+
return ordered.map((event, row) => ({ event, row, from: weekOf(event.start < first ? first : event.start), to: weekOf(endOf(event) > last ? last : endOf(event)) }));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** An event's last day: its end, or its start for one day. */
|
|
123
|
+
const endOf = (event: CalendarEvent): Day => (event.end && isDay(event.end) && event.end > event.start ? event.end : event.start);
|
|
124
|
+
|
|
125
|
+
/** Whole days from `from` to `to`. */
|
|
126
|
+
const daysBetween = (from: Day, to: Day) => Math.round((Date.UTC(+to.slice(0, 4), +to.slice(5, 7) - 1, +to.slice(8, 10)) - Date.UTC(+from.slice(0, 4), +from.slice(5, 7) - 1, +from.slice(8, 10))) / 864e5);
|
|
127
|
+
|
|
128
|
+
/** Detail of `view-change`. */
|
|
129
|
+
export interface CalendarViewDetail { view: CalendarView }
|
|
130
|
+
|
|
131
|
+
/** What a full month's day writes before its date: its weekday's name (the first week), and its week's number. */
|
|
132
|
+
type DayLabel = { weekday?: string; today?: boolean; week?: number };
|
|
133
|
+
|
|
134
|
+
export type CalendarLine = { event: CalendarEvent; span: number } | "on" | null;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Lays one week's events on lines, so an event over several days keeps its
|
|
138
|
+
* line and is one bar with its name written once: each event takes the
|
|
139
|
+
* lowest line free on all its days in the week, longest first as `byDay`
|
|
140
|
+
* lists them. A day whose events need more lines than `lines` keeps the last
|
|
141
|
+
* one for "+N", and a bar breaks there and goes on after it; `shown` says
|
|
142
|
+
* which days are drawn at all (a bar stops at a day that is not).
|
|
143
|
+
*/
|
|
144
|
+
export function weekLines(
|
|
145
|
+
week: readonly Day[],
|
|
146
|
+
byDay: ReadonlyMap<Day, readonly CalendarEvent[]>,
|
|
147
|
+
lines: number,
|
|
148
|
+
shown: (day: Day) => boolean = () => true,
|
|
149
|
+
): { lines: CalendarLine[]; hidden: number }[] {
|
|
150
|
+
const lineOf = new Map<CalendarEvent, number>();
|
|
151
|
+
const taken = week.map(() => new Set<number>());
|
|
152
|
+
week.forEach((day, column) => {
|
|
153
|
+
for (const event of byDay.get(day) ?? []) {
|
|
154
|
+
if (lineOf.has(event)) continue;
|
|
155
|
+
const columns: number[] = [];
|
|
156
|
+
for (let next = column; next < week.length && (byDay.get(week[next]) ?? []).includes(event); next++) columns.push(next);
|
|
157
|
+
let line = 0;
|
|
158
|
+
while (columns.some(next => taken[next].has(line))) line++;
|
|
159
|
+
lineOf.set(event, line);
|
|
160
|
+
for (const next of columns) taken[next].add(line);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
// How many lines each day draws: all of them, or one fewer for "+N".
|
|
164
|
+
const room = week.map(day => {
|
|
165
|
+
if (!shown(day)) return 0;
|
|
166
|
+
const events = byDay.get(day) ?? [];
|
|
167
|
+
const deepest = Math.max(-1, ...events.map(event => lineOf.get(event)!));
|
|
168
|
+
return deepest < lines ? lines : Math.max(0, lines - 1);
|
|
169
|
+
});
|
|
170
|
+
return week.map((day, column) => {
|
|
171
|
+
const events = byDay.get(day) ?? [];
|
|
172
|
+
const drawn: CalendarLine[] = [];
|
|
173
|
+
for (let line = 0; line < room[column]; line++) {
|
|
174
|
+
const event = events.find(candidate => lineOf.get(candidate) === line);
|
|
175
|
+
if (!event) { drawn.push(null); continue; }
|
|
176
|
+
const before = column > 0 && line < room[column - 1] && (byDay.get(week[column - 1]) ?? []).includes(event);
|
|
177
|
+
if (before) { drawn.push("on"); continue; }
|
|
178
|
+
let span = 1;
|
|
179
|
+
while (column + span < week.length && line < room[column + span] && (byDay.get(week[column + span]) ?? []).includes(event)) span++;
|
|
180
|
+
drawn.push({ event, span });
|
|
181
|
+
}
|
|
182
|
+
// Drop the empty lines at the foot, so a quiet day draws nothing under its date.
|
|
183
|
+
while (drawn.length && drawn.at(-1) === null) drawn.pop();
|
|
184
|
+
const hidden = room[column] < lines ? events.filter(event => lineOf.get(event)! >= room[column]).length : 0;
|
|
185
|
+
return { lines: drawn, hidden };
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
48
189
|
/** One end of a picker's value: its day and, when it has one, its time (`14:30`). */
|
|
49
190
|
export interface CalendarEnd {
|
|
50
191
|
day: Day;
|
|
@@ -102,12 +243,38 @@ const openPickers = new Set<Calendar>();
|
|
|
102
243
|
* small month.
|
|
103
244
|
* @prop {number} dayWidth - Squares per day (`day-width`): 4 by default, 1
|
|
104
245
|
* when compact.
|
|
246
|
+
* @prop {string} view - `month` (the default), `week`, `day` or `swimlane`:
|
|
247
|
+
* the weeks from the month shown across (a week half a day wide, so it is
|
|
248
|
+
* as wide as the month), each event one row with one bar from its first
|
|
249
|
+
* week to its last. A compact calendar and a picker are always a month.
|
|
250
|
+
* @prop {boolean | string[]} views - Day, Week, Month and Swimlane in the
|
|
251
|
+
* header, to change `view`; or the views to offer, in order
|
|
252
|
+
* (`views="month swimlane"`). A name the calendar does not draw is the
|
|
253
|
+
* page's own view: chosen, the calendar keeps its header (the month's title,
|
|
254
|
+
* the arrows moving a month) and shows what the page puts in its slot of
|
|
255
|
+
* that name (`<div slot="earnings">`) in place of the days.
|
|
256
|
+
* @prop {Record<string, string>} viewNames - The words for each view in the
|
|
257
|
+
* switch (property only), over the shared Day, Week, Month and Swimlane;
|
|
258
|
+
* a page's own view is named by its name when left out.
|
|
259
|
+
* @prop {string} date - The day the week or day view shows, `YYYY-MM-DD`;
|
|
260
|
+
* today, or the first of the month shown, by default.
|
|
105
261
|
* @prop {number} lines - Lines of events under a day's date: 2 by default.
|
|
106
262
|
* @prop {boolean} weekNumbers - Write the week's number (`week-numbers`) on
|
|
107
263
|
* its first day.
|
|
108
|
-
* @prop {boolean} fixed - Always draw six weeks, so the month keeps its height
|
|
264
|
+
* @prop {boolean} fixed - Always draw six weeks, so the month keeps its height,
|
|
265
|
+
* and every other view stands in that same height (the weekdays and six
|
|
266
|
+
* weeks): the week's days grow to it, the day's hours fill it, the
|
|
267
|
+
* swimlane's rows fill it and scroll beyond it, and a page's own view gets
|
|
268
|
+
* it, so nothing under the calendar moves when the view changes.
|
|
109
269
|
* @prop {boolean} outsideDays - Draw the days of the months before and after
|
|
110
270
|
* in the title grey (`outside-days`); pressing one moves there.
|
|
271
|
+
* @prop {boolean} activity - The days as quiet squares, a contribution grid:
|
|
272
|
+
* weeks across a square each, as the swimlane runs, and the days down them,
|
|
273
|
+
* every other weekday named before them and the month over its weeks; a
|
|
274
|
+
* day on which something happens is filled with its events' colours, split
|
|
275
|
+
* into bands when several share it. Dates stay in each day's name and
|
|
276
|
+
* tooltip. Takes the place of the large calendar's months in squares.
|
|
277
|
+
* @prop {number} months - How many months activity draws from `month`: 1 by default.
|
|
111
278
|
* @prop {boolean} heat - Light each day by how much happens on it, as a
|
|
112
279
|
* contribution graph does: the events' `count`s added up (one each by
|
|
113
280
|
* default), in four steps of the busiest day shown.
|
|
@@ -116,7 +283,12 @@ const openPickers = new Set<Calendar>();
|
|
|
116
283
|
* by default.
|
|
117
284
|
* @prop {boolean} noHeader - Leave out the title and arrows (`no-header`).
|
|
118
285
|
* With a header, pressing the title travels by months and years: the
|
|
119
|
-
* twelve months of the year, then twelve years, and back down.
|
|
286
|
+
* twelve months of the year, then twelve years, and back down. With a
|
|
287
|
+
* header, a swipe sideways across the days (a finger or a pen), or a
|
|
288
|
+
* trackpad's sideways scroll, does what the arrows do, the next month
|
|
289
|
+
* towards the start and the one before towards the end, and stops where
|
|
290
|
+
* they stop; the days take sideways touches for it, so a carousel or a
|
|
291
|
+
* page around the calendar does not move with them.
|
|
120
292
|
* @prop {boolean} noToday - Leave out the Today button (`no-today`).
|
|
121
293
|
* @prop {string} min - The first day a person may choose.
|
|
122
294
|
* @prop {string} max - The last day a person may choose.
|
|
@@ -135,6 +307,11 @@ const openPickers = new Set<Calendar>();
|
|
|
135
307
|
* @prop {boolean} disabled - A picker cannot be opened.
|
|
136
308
|
* @prop {boolean} open - A picker's month is open.
|
|
137
309
|
* @prop {CalendarEvent[]} events - What happens on the days (property only).
|
|
310
|
+
* @prop {(month: Date, view: string) => string} monthHeading - Names the month view's title, and a page's own view's,
|
|
311
|
+
* (property only): given the first of the month shown, it returns the words,
|
|
312
|
+
* so a product can write "Events in October" instead of "October 2026". The
|
|
313
|
+
* week and day views, the twelve months and the twelve years keep their own
|
|
314
|
+
* titles, and the title button's label still says what it opens.
|
|
138
315
|
*
|
|
139
316
|
* @fires day-press - A day was pressed or chosen with Enter or Space;
|
|
140
317
|
* `{ day, events }`. Cancelable: cancelled, a selectable calendar keeps its value.
|
|
@@ -142,7 +319,8 @@ const openPickers = new Set<Calendar>();
|
|
|
142
319
|
* `{ value, previous, start, end }`, `end` empty while it is still to come.
|
|
143
320
|
* @fires input - A picker's value changed, the same moment as `change`.
|
|
144
321
|
* @fires event-press - An event was pressed; `{ event, day }`.
|
|
145
|
-
* @fires navigate - A person moved to another month; `{ month }`.
|
|
322
|
+
* @fires navigate - A person moved to another month, week or day; `{ month }`.
|
|
323
|
+
* @fires view-change - A person chose another view; `{ view }`.
|
|
146
324
|
* @fires ds-calendar-toggle - A picker's month opened or closed; `{ open }`.
|
|
147
325
|
*
|
|
148
326
|
* A selectable calendar shows Clear right under its dates while something is chosen, and `clear()`
|
|
@@ -163,11 +341,17 @@ export class Calendar extends CalendarBase {
|
|
|
163
341
|
dayWidth: { type: Number, attribute: "day-width" },
|
|
164
342
|
lines: { type: Number },
|
|
165
343
|
weekNumbers: { type: Boolean, attribute: "week-numbers", reflect: true },
|
|
344
|
+
view: { type: String, reflect: true },
|
|
345
|
+
views: { reflect: true, converter: viewsConverter },
|
|
346
|
+
viewNames: { attribute: false },
|
|
347
|
+
date: { type: String, reflect: true },
|
|
166
348
|
fixed: { type: Boolean, reflect: true },
|
|
167
349
|
outsideDays: { type: Boolean, attribute: "outside-days", reflect: true },
|
|
168
350
|
noHeader: { type: Boolean, attribute: "no-header", reflect: true },
|
|
169
351
|
noToday: { type: Boolean, attribute: "no-today", reflect: true },
|
|
170
352
|
heat: { type: Boolean, reflect: true },
|
|
353
|
+
activity: { type: Boolean, reflect: true },
|
|
354
|
+
months: { type: Number },
|
|
171
355
|
heatMax: { type: Number, attribute: "heat-max" },
|
|
172
356
|
picker: { type: Boolean, reflect: true },
|
|
173
357
|
name: { type: String, reflect: true },
|
|
@@ -179,23 +363,34 @@ export class Calendar extends CalendarBase {
|
|
|
179
363
|
open: { type: Boolean, reflect: true },
|
|
180
364
|
unfolded: { state: true },
|
|
181
365
|
embedded: { attribute: false },
|
|
366
|
+
monthHeading: { attribute: false },
|
|
182
367
|
level: { state: true },
|
|
183
368
|
browseYear: { state: true },
|
|
184
369
|
pickFocus: { state: true },
|
|
185
370
|
};
|
|
186
371
|
|
|
187
372
|
static styles = [unsafeCSS(metrics), unsafeCSS(controls), unsafeCSS(styles)];
|
|
373
|
+
/** Words cut short (the title, a weekday, the picker's value, an event's name on its bar) fade over their last square, never "…": marks them `data-cut="end"` while they do not fit. */
|
|
374
|
+
protected readonly cuts = new CutFades(this, { selector: ".title:not(.title-button), .title-text, .weekday, .trigger .text, .event .name, .swimlane-month" });
|
|
188
375
|
|
|
189
376
|
declare month: string | undefined;
|
|
377
|
+
/** Names the month view's title from the first of the month shown; the month and year when unset. */
|
|
378
|
+
declare monthHeading: ((month: Date, view: string) => string) | undefined;
|
|
190
379
|
declare compact: boolean;
|
|
191
380
|
declare dayWidth: number | undefined;
|
|
192
381
|
declare lines: number | undefined;
|
|
193
382
|
declare weekNumbers: boolean;
|
|
383
|
+
declare view: CalendarView | (string & {});
|
|
384
|
+
declare views: boolean | string[];
|
|
385
|
+
declare viewNames: Record<string, string> | undefined;
|
|
386
|
+
declare date: string | undefined;
|
|
194
387
|
declare fixed: boolean;
|
|
195
388
|
declare outsideDays: boolean;
|
|
196
389
|
declare noHeader: boolean;
|
|
197
390
|
declare noToday: boolean;
|
|
198
391
|
declare heat: boolean;
|
|
392
|
+
declare activity: boolean;
|
|
393
|
+
declare months: number | undefined;
|
|
199
394
|
declare heatMax: number | undefined;
|
|
200
395
|
declare picker: boolean;
|
|
201
396
|
declare name: string | undefined;
|
|
@@ -231,11 +426,14 @@ export class Calendar extends CalendarBase {
|
|
|
231
426
|
super();
|
|
232
427
|
this.compact = false;
|
|
233
428
|
this.weekNumbers = false;
|
|
429
|
+
this.view = "month";
|
|
430
|
+
this.views = false;
|
|
234
431
|
this.fixed = false;
|
|
235
432
|
this.outsideDays = false;
|
|
236
433
|
this.noHeader = false;
|
|
237
434
|
this.noToday = false;
|
|
238
435
|
this.heat = false;
|
|
436
|
+
this.activity = false;
|
|
239
437
|
this.picker = false;
|
|
240
438
|
this.time = false;
|
|
241
439
|
this.required = false;
|
|
@@ -281,6 +479,88 @@ export class Calendar extends CalendarBase {
|
|
|
281
479
|
return this.byDay.get(day) ?? [];
|
|
282
480
|
}
|
|
283
481
|
|
|
482
|
+
/** The view drawn: a compact calendar and a picker are always a month. */
|
|
483
|
+
get shownView(): CalendarView {
|
|
484
|
+
if (this.picker || this.compact) return "month";
|
|
485
|
+
if (isView(this.view)) return this.view;
|
|
486
|
+
return this.viewChoices.includes(this.view) ? (this.view as CalendarView) : "month";
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** The views offered in the header, in order: every one the calendar draws, or the page's list. */
|
|
490
|
+
get viewChoices(): string[] {
|
|
491
|
+
const views = this.views;
|
|
492
|
+
if (Array.isArray(views)) return [...new Set(views.map(view => String(view).trim()).filter(Boolean))];
|
|
493
|
+
return views ? VIEWS.map(([view]) => view) : [];
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** Whether the view shown is the page's own, drawn in its slot. */
|
|
497
|
+
private get ownView() {
|
|
498
|
+
return !isView(this.shownView);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/** A view's words in the switch: the page's, else the shared ones, else its name. */
|
|
502
|
+
private viewName(view: string): string {
|
|
503
|
+
const shared = VIEWS.find(([choice]) => choice === view)?.[1];
|
|
504
|
+
const word = this.viewNames?.[view] ?? (shared ? this.words(shared) : view);
|
|
505
|
+
// The switch's options are written `value:words,value:words`.
|
|
506
|
+
return word.replace(/,/g, " ");
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/** Whether the view moves by days (a week or a day) rather than by months (a month or a swimlane). */
|
|
510
|
+
private get byDays() {
|
|
511
|
+
const view = this.shownView;
|
|
512
|
+
return view === "week" || view === "day";
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** The day the week or day view shows: `date`, else today in the month shown, else its first. */
|
|
516
|
+
get shownDay(): Day {
|
|
517
|
+
if (isDay(this.date)) return this.date;
|
|
518
|
+
const month = this.shownMonth;
|
|
519
|
+
return sameMonth(this.todayDay, month) ? this.todayDay : month;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** The seven days of the week `day` falls in. */
|
|
523
|
+
private weekOf(day: Day): Day[] {
|
|
524
|
+
const start = addDays(day, -((weekdayOf(day) - this.firstDay + 7) % 7));
|
|
525
|
+
return Array.from({ length: 7 }, (_, index) => addDays(start, index));
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** Shows another week or day around `day`, and says so with `navigate`. */
|
|
529
|
+
showDay(day: Day, announce = false) {
|
|
530
|
+
if (!isDay(day) || day === this.date) return;
|
|
531
|
+
this.date = day;
|
|
532
|
+
this.month = day.slice(0, 7);
|
|
533
|
+
this.unfolded = undefined;
|
|
534
|
+
if (announce) this.announceMonth(day);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/** Changes the view, as Day, Week and Month do, and says so with `view-change`. */
|
|
538
|
+
showView(view: CalendarView) {
|
|
539
|
+
if (view === this.shownView) return;
|
|
540
|
+
if (!isDay(this.date)) this.date = this.shownDay;
|
|
541
|
+
this.view = view;
|
|
542
|
+
this.unfolded = undefined;
|
|
543
|
+
this.dispatchEvent(new CustomEvent<CalendarViewDetail>("view-change", { bubbles: true, composed: true, detail: { view } }));
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** The split button's choice; its own change event stays inside, since a calendar's change is a chosen day. */
|
|
547
|
+
private onViewChange = (event: Event) => {
|
|
548
|
+
event.stopPropagation();
|
|
549
|
+
const view = (event as CustomEvent<{ value: string }>).detail?.value;
|
|
550
|
+
if (view && (isView(view) || this.viewChoices.includes(view))) this.showView(view as CalendarView);
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
/** Brings a day into view: its month, its week or the day itself. */
|
|
554
|
+
private bringIntoView(day: Day) {
|
|
555
|
+
const view = this.shownView;
|
|
556
|
+
if (!this.byDays) {
|
|
557
|
+
if (!sameMonth(day, this.shownMonth)) this.showMonth(day, true);
|
|
558
|
+
} else if (view === "day" || !this.weekOf(this.shownDay).includes(day)) this.showDay(day, true);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/** Each shown day's lines of events, laid out a week at a time in render(). */
|
|
562
|
+
private laidOut = new Map<Day, { lines: CalendarLine[]; hidden: number }>();
|
|
563
|
+
|
|
284
564
|
protected willUpdate(changed: PropertyValues<this>) {
|
|
285
565
|
super.willUpdate(changed);
|
|
286
566
|
this.style.setProperty("--ds-calendar-day-squares", String(this.squaresPerDay));
|
|
@@ -299,6 +579,13 @@ export class Calendar extends CalendarBase {
|
|
|
299
579
|
|
|
300
580
|
protected updated(changed: PropertyValues<this>) {
|
|
301
581
|
super.updated(changed);
|
|
582
|
+
// A day opens its hours an hour before its first timed event, or at seven.
|
|
583
|
+
if (this.shownView === "day" && (changed.has("view") || changed.has("date") || changed.has("events"))) {
|
|
584
|
+
const hours = this.renderRoot.querySelector<HTMLElement>(".hours");
|
|
585
|
+
const first = hours?.querySelector<HTMLElement>(".hour[data-busy]")?.dataset.hour;
|
|
586
|
+
const open = hours?.querySelector<HTMLElement>(`.hour[data-hour="${Math.max(0, (first ? Number(first) : 8) - 1)}"]`);
|
|
587
|
+
if (hours && open) hours.scrollTop = open.offsetTop;
|
|
588
|
+
}
|
|
302
589
|
if (changed.has("value") || changed.has("required")) {
|
|
303
590
|
this.internals?.setFormValue(this.value || null);
|
|
304
591
|
if (this.required && !this.value) this.internals?.setValidity({ valueMissing: true }, this.pickerWords(undefined, "Choose a date"), this.trigger ?? undefined);
|
|
@@ -333,6 +620,7 @@ export class Calendar extends CalendarBase {
|
|
|
333
620
|
showToday() {
|
|
334
621
|
this.level = "days";
|
|
335
622
|
this.showMonth(this.todayDay, true);
|
|
623
|
+
if (this.shownView !== "month") this.showDay(this.todayDay, true);
|
|
336
624
|
this.focusDay = this.todayDay;
|
|
337
625
|
}
|
|
338
626
|
|
|
@@ -394,7 +682,11 @@ export class Calendar extends CalendarBase {
|
|
|
394
682
|
|
|
395
683
|
/** The arrows: a month among the days, a year among the months, twelve years among the years. */
|
|
396
684
|
private step(direction: -1 | 1) {
|
|
397
|
-
if (this.level === "days")
|
|
685
|
+
if (this.level === "days") {
|
|
686
|
+
const view = this.shownView;
|
|
687
|
+
if (this.byDays) return this.showDay(addDays(this.shownDay, direction * (view === "week" ? 7 : 1)), true);
|
|
688
|
+
return direction < 0 ? this.previousMonth() : this.nextMonth();
|
|
689
|
+
}
|
|
398
690
|
this.browseYear += direction * (this.level === "months" ? 1 : 12);
|
|
399
691
|
if (this.level === "years") this.pickFocus = (this.pickFocus ?? this.browseYear) + direction * 12;
|
|
400
692
|
}
|
|
@@ -412,6 +704,7 @@ export class Calendar extends CalendarBase {
|
|
|
412
704
|
const month = `${String(this.browseYear).padStart(4, "0")}-${String(value + 1).padStart(2, "0")}-01`;
|
|
413
705
|
this.level = "days";
|
|
414
706
|
this.showMonth(month, true);
|
|
707
|
+
if (this.shownView !== "month") this.showDay(month as Day, false);
|
|
415
708
|
const day = [this.focusDay, this.chosen.start, this.todayDay].find(day => day && sameMonth(day, month)) ?? month;
|
|
416
709
|
void this.focusCell(day);
|
|
417
710
|
}
|
|
@@ -533,10 +826,62 @@ export class Calendar extends CalendarBase {
|
|
|
533
826
|
const target = this.keyTarget(event, day);
|
|
534
827
|
if (!target) return;
|
|
535
828
|
event.preventDefault();
|
|
536
|
-
|
|
829
|
+
this.bringIntoView(target);
|
|
537
830
|
void this.focusCell(target);
|
|
538
831
|
};
|
|
539
832
|
|
|
833
|
+
// A swipe sideways across the days or the months, as the arrows: by finger or pen, or a trackpad's
|
|
834
|
+
// sideways scroll. Read from the arrows themselves, so a swipe goes nowhere an arrow cannot.
|
|
835
|
+
private swipe?: { id: number; x: number; y: number; at: number };
|
|
836
|
+
private swipedAt = 0;
|
|
837
|
+
private wheel = { dx: 0, at: 0, locked: 0 };
|
|
838
|
+
|
|
839
|
+
private swipeTo(direction: -1 | 1) {
|
|
840
|
+
const arrow = this.renderRoot.querySelector<HTMLButtonElement>(`.header [part~="${direction < 0 ? "previous" : "next"}"]`);
|
|
841
|
+
if (!arrow || arrow.disabled) return false;
|
|
842
|
+
this.step(direction);
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
private get rtl() {
|
|
847
|
+
return getComputedStyle(this).direction === "rtl";
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
private onSwipeStart = (event: PointerEvent) => {
|
|
851
|
+
this.swipe = event.pointerType === "mouse" || !event.isPrimary ? undefined : { id: event.pointerId, x: event.clientX, y: event.clientY, at: event.timeStamp };
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
private onSwipeEnd = (event: PointerEvent) => {
|
|
855
|
+
const swipe = this.swipe;
|
|
856
|
+
this.swipe = undefined;
|
|
857
|
+
if (!swipe || event.pointerId !== swipe.id) return;
|
|
858
|
+
const dx = (event.clientX - swipe.x) * (this.rtl ? -1 : 1);
|
|
859
|
+
const dy = event.clientY - swipe.y;
|
|
860
|
+
if (Math.abs(dx) < 40 || Math.abs(dx) < Math.abs(dy) * 2 || event.timeStamp - swipe.at > 800) return;
|
|
861
|
+
// A day the swipe started on is not pressed by it.
|
|
862
|
+
if (this.swipeTo(dx < 0 ? 1 : -1)) this.swipedAt = Date.now();
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
private onSwipeWheel = (event: WheelEvent) => {
|
|
866
|
+
if (Math.abs(event.deltaX) <= Math.abs(event.deltaY)) return;
|
|
867
|
+
event.preventDefault();
|
|
868
|
+
const now = Date.now();
|
|
869
|
+
if (now < this.wheel.locked) return;
|
|
870
|
+
if (now - this.wheel.at > 200) this.wheel.dx = 0;
|
|
871
|
+
this.wheel.at = now;
|
|
872
|
+
this.wheel.dx += event.deltaX * (this.rtl ? -1 : 1);
|
|
873
|
+
if (Math.abs(this.wheel.dx) < 60) return;
|
|
874
|
+
const direction = this.wheel.dx > 0 ? 1 : -1;
|
|
875
|
+
this.wheel = { dx: 0, at: 0, locked: now + 500 };
|
|
876
|
+
this.swipeTo(direction);
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
private onSwipeClick = (event: MouseEvent) => {
|
|
880
|
+
if (Date.now() - this.swipedAt > 400) return;
|
|
881
|
+
event.preventDefault();
|
|
882
|
+
event.stopImmediatePropagation();
|
|
883
|
+
};
|
|
884
|
+
|
|
540
885
|
private onClick = (event: MouseEvent) => {
|
|
541
886
|
const path = event.composedPath() as HTMLElement[];
|
|
542
887
|
const find = (selector: string) => path.find(node => node instanceof HTMLElement && node.matches(selector));
|
|
@@ -557,7 +902,7 @@ export class Calendar extends CalendarBase {
|
|
|
557
902
|
this.focusDay = day;
|
|
558
903
|
this.press(day);
|
|
559
904
|
// A day of the month before or after takes the calendar there.
|
|
560
|
-
|
|
905
|
+
this.bringIntoView(day);
|
|
561
906
|
};
|
|
562
907
|
|
|
563
908
|
/** While a range waits for its end, the day under the pointer previews it. */
|
|
@@ -581,26 +926,41 @@ export class Calendar extends CalendarBase {
|
|
|
581
926
|
const level = this.level;
|
|
582
927
|
const year = this.browseYear;
|
|
583
928
|
const span = this.yearSpan;
|
|
584
|
-
const
|
|
585
|
-
const
|
|
929
|
+
const view = this.shownView;
|
|
930
|
+
const labels = level === "days" && this.byDays
|
|
931
|
+
? view === "week" ? ["Previous week", "Next week"] : ["Previous day", "Next day"]
|
|
932
|
+
: { days: ["Previous month", "Next month"], months: ["Previous year", "Next year"], years: ["Earlier years", "Later years"] }[level];
|
|
933
|
+
const shownDays = view === "week" ? this.weekOf(this.shownDay) : [this.shownDay];
|
|
934
|
+
const blocked = level === "days" && this.byDays
|
|
935
|
+
? [Boolean(min && addDays(shownDays[0], -1) < min), Boolean(max && addDays(shownDays.at(-1)!, 1) > max)]
|
|
936
|
+
: level === "days"
|
|
586
937
|
? [before, after]
|
|
587
938
|
: level === "months"
|
|
588
939
|
? [Boolean(min && year - 1 < Number(min.slice(0, 4))), Boolean(max && year + 1 > Number(max.slice(0, 4)))]
|
|
589
940
|
: [Boolean(min && span[0] - 1 < Number(min.slice(0, 4))), Boolean(max && span[0] + 12 > Number(max.slice(0, 4)))];
|
|
590
941
|
const arrow = (which: "previous" | "next") => html`<button
|
|
591
|
-
class="
|
|
942
|
+
class="nav"
|
|
592
943
|
part=${which}
|
|
593
944
|
type="button"
|
|
594
945
|
aria-label=${this.words(labels[which === "previous" ? 0 : 1])}
|
|
595
946
|
?disabled=${blocked[which === "previous" ? 0 : 1]}
|
|
596
947
|
@click=${() => this.step(which === "previous" ? -1 : 1)}
|
|
597
|
-
><ds-icon type=${which === "previous" ? "
|
|
948
|
+
><ds-icon type=${(this.compact ? "chevron-" : "") + (which === "previous" ? "left" : "right")} size=${this.compact ? "large" : nothing} aria-hidden="true"></ds-icon></button>`;
|
|
598
949
|
const title = level === "days"
|
|
599
|
-
?
|
|
950
|
+
? view === "week"
|
|
951
|
+
? this.rangeTitle(shownDays[0], shownDays[6])
|
|
952
|
+
: view === "day"
|
|
953
|
+
? dayLabel(this.shownDay)
|
|
954
|
+
: view === "swimlane"
|
|
955
|
+
? this.monthRangeTitle(this.swimlaneWeeks)
|
|
956
|
+
: this.monthHeading
|
|
957
|
+
? this.monthHeading(new Date(Number(month.slice(0, 4)), Number(month.slice(5, 7)) - 1, 1), view)
|
|
958
|
+
: monthTitle(month, this.compact ? "short" : "long")
|
|
600
959
|
: level === "months"
|
|
601
960
|
? this.yearName(year)
|
|
602
961
|
: `${this.yearName(span[0])} – ${this.yearName(span[0] + 11)}`;
|
|
603
|
-
|
|
962
|
+
const switchable = this.viewChoices.length > 1 && !this.compact && !this.picker;
|
|
963
|
+
return html`<div class="header" part="header" ?data-views=${switchable}>
|
|
604
964
|
${level === "years"
|
|
605
965
|
? html`<span class="title" id="title" aria-live="polite">${title}</span>`
|
|
606
966
|
: html`<button
|
|
@@ -610,26 +970,38 @@ export class Calendar extends CalendarBase {
|
|
|
610
970
|
aria-label=${`${title}, ${this.words("Choose a month and year")}`}
|
|
611
971
|
@click=${this.levelUp}
|
|
612
972
|
><span class="title-text" id="title" aria-live="polite">${title}</span></button>`}
|
|
613
|
-
|
|
614
|
-
|
|
973
|
+
${switchable ? html`<ds-button
|
|
974
|
+
class="views"
|
|
975
|
+
part="views"
|
|
976
|
+
split
|
|
977
|
+
literal
|
|
978
|
+
aria-label=${this.words("View")}
|
|
979
|
+
.options=${this.viewChoices.map(choice => `${choice}:${this.viewName(choice)}`).join(",")}
|
|
980
|
+
.value=${view}
|
|
981
|
+
@change=${this.onViewChange}
|
|
982
|
+
></ds-button>` : nothing}
|
|
983
|
+
<span class="controls" role="group" aria-label=${this.words("Date")}>
|
|
615
984
|
${arrow("previous")}
|
|
985
|
+
${this.noToday || this.squaresPerDay < 2 ? nothing : html`<button class="today-button" part="today" type="button" @click=${() => this.showToday()}>${this.words("Today")}</button>`}
|
|
616
986
|
${arrow("next")}
|
|
617
987
|
</span>
|
|
618
988
|
</div>`;
|
|
619
989
|
}
|
|
620
990
|
|
|
621
|
-
private renderDay(day: Day,
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
|
|
991
|
+
private renderDay(day: Day, inView = sameMonth(day, this.shownMonth), tabDay = this.tabDay, label?: DayLabel) {
|
|
992
|
+
const outside = !inView;
|
|
993
|
+
if (outside && !this.outsideDays) {
|
|
994
|
+
return html`<div role="gridcell" class="day outside" part="day outside">${label?.weekday || label?.week
|
|
995
|
+
? html`<span class="date-line" aria-hidden="true">${this.renderLabel(label)}</span>`
|
|
996
|
+
: nothing}</div>`;
|
|
997
|
+
}
|
|
625
998
|
const events = this.eventsOn(day);
|
|
626
999
|
const lines = this.eventLines;
|
|
627
1000
|
const isToday = day === this.todayDay;
|
|
628
1001
|
const { selected, inside, first, last } = this.selection(day);
|
|
629
1002
|
const allowed = this.allowed(day);
|
|
630
1003
|
const unfolded = this.unfolded === day;
|
|
631
|
-
const
|
|
632
|
-
const hidden = events.length - shown.length;
|
|
1004
|
+
const laid = this.laidOut.get(day) ?? { lines: [], hidden: 0 };
|
|
633
1005
|
const heat = this.heatByDay.get(day);
|
|
634
1006
|
const parts = ["day", isToday ? "today" : "", selected ? "selected" : "", heat ? "heat" : ""].filter(Boolean).join(" ");
|
|
635
1007
|
return html`<div
|
|
@@ -637,7 +1009,7 @@ export class Calendar extends CalendarBase {
|
|
|
637
1009
|
class="day"
|
|
638
1010
|
part=${parts}
|
|
639
1011
|
data-day=${day}
|
|
640
|
-
tabindex=${!outside && day ===
|
|
1012
|
+
tabindex=${!outside && day === tabDay ? 0 : -1}
|
|
641
1013
|
aria-label=${this.cellLabel(day, events)}
|
|
642
1014
|
aria-selected=${this.selectable ? String(selected || inside) : nothing}
|
|
643
1015
|
aria-current=${isToday ? "date" : nothing}
|
|
@@ -653,29 +1025,306 @@ export class Calendar extends CalendarBase {
|
|
|
653
1025
|
?data-open=${unfolded}
|
|
654
1026
|
>
|
|
655
1027
|
<span class="date-line" aria-hidden="true">
|
|
656
|
-
${
|
|
1028
|
+
${label ? this.renderLabel(label) : nothing}
|
|
657
1029
|
<span class="date">${dateNumber(day)}</span>
|
|
658
1030
|
</span>
|
|
659
1031
|
${lines > 0 && events.length
|
|
660
1032
|
? html`<span class="events" aria-hidden="true">
|
|
661
|
-
${
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
${hidden > 0 && !unfolded ? html`<span class="more" part="more" data-more>+${hidden}</span>` : nothing}
|
|
1033
|
+
${unfolded
|
|
1034
|
+
? events.map(event => this.renderEvent(event, events, 1))
|
|
1035
|
+
: laid.lines.map(line => line === null || line === "on"
|
|
1036
|
+
? html`<span class="line" data-line=${line ?? "empty"}></span>`
|
|
1037
|
+
: this.renderEvent(line.event, events, line.span))}
|
|
1038
|
+
${laid.hidden > 0 && !unfolded ? html`<span class="more" part="more" data-more>+${laid.hidden}</span>` : nothing}
|
|
668
1039
|
</span>`
|
|
669
1040
|
: nothing}
|
|
670
1041
|
</div>`;
|
|
671
1042
|
}
|
|
672
1043
|
|
|
1044
|
+
/** What a day writes before its date: its weekday's name in the first week, and the week's number on its first day. */
|
|
1045
|
+
private renderLabel(label: DayLabel) {
|
|
1046
|
+
return html`${label.weekday ? html`<span class="weekday-name" ?data-today=${label.today}>${label.weekday}</span>` : nothing}${label.week
|
|
1047
|
+
? html`<span class="week-number">${this.words("W{week}", { week: label.week })}</span>`
|
|
1048
|
+
: nothing}`;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/** A week's days as one title: "Oct 18 – 24, 2026", in the language's own way. */
|
|
1052
|
+
private rangeTitle(from: Day, to: Day): string {
|
|
1053
|
+
const format = dateFormatter({ month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
|
|
1054
|
+
const at = (day: Day) => Date.UTC(Number(day.slice(0, 4)), Number(day.slice(5, 7)) - 1, Number(day.slice(8, 10)), 12);
|
|
1055
|
+
return typeof format.formatRange === "function" ? format.formatRange(at(from), at(to)) : `${format.format(at(from))} – ${format.format(at(to))}`;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/** The first day of each week a swimlane draws: from the week holding the month's first, a week to each square the month has across. */
|
|
1059
|
+
private get swimlaneWeeks(): Day[] {
|
|
1060
|
+
const first = this.shownMonth;
|
|
1061
|
+
const start = addDays(first, -((weekdayOf(first) - this.firstDay + 7) % 7));
|
|
1062
|
+
const count = Math.max(4, 7 * this.squaresPerDay);
|
|
1063
|
+
return Array.from({ length: count }, (_, index) => addDays(start, index * 7));
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/** The month owning most of a week: its Thursday's, counted from the week's first day. */
|
|
1067
|
+
private ownerOf(weekStart: Day): Day {
|
|
1068
|
+
return startOfMonth(addDays(weekStart, 3));
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/** The months a swimlane covers as one title: "Oct 2026 – Jan 2027". */
|
|
1072
|
+
private monthRangeTitle(weeks: readonly Day[]): string {
|
|
1073
|
+
const from = this.ownerOf(weeks[0]), to = this.ownerOf(weeks.at(-1)!);
|
|
1074
|
+
const format = dateFormatter({ month: "short", year: "numeric", timeZone: "UTC" });
|
|
1075
|
+
const at = (day: Day) => Date.UTC(Number(day.slice(0, 4)), Number(day.slice(5, 7)) - 1, 15);
|
|
1076
|
+
if (from === to) return monthTitle(from);
|
|
1077
|
+
return typeof format.formatRange === "function" ? format.formatRange(at(from), at(to)) : `${format.format(at(from))} – ${format.format(at(to))}`;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* The events as a swimlane: weeks across, a square each; over them the
|
|
1082
|
+
* months, named where the month owning the week changes, and each week's
|
|
1083
|
+
* first date (today's date in its disc on today's week); then a row to an
|
|
1084
|
+
* event, a square high, each one bar from its first week to its last.
|
|
1085
|
+
*/
|
|
1086
|
+
private renderSwimlane() {
|
|
1087
|
+
const weeks = this.swimlaneWeeks;
|
|
1088
|
+
const bars = swimlaneBars(this.events ?? [], weeks);
|
|
1089
|
+
// Fixed, the rows (a square each) fill the month's height under the months and dates, and scroll past it.
|
|
1090
|
+
const fit = this.fixed ? Math.max(1, 6 * (this.eventLines + 2) - 1) : 6;
|
|
1091
|
+
const rows = Math.max(fit, bars.length + 1);
|
|
1092
|
+
const today = this.todayDay;
|
|
1093
|
+
const todayWeek = weeks.findIndex(start => today >= start && today <= addDays(start, 6));
|
|
1094
|
+
const months: { column: number; span: number; month: Day }[] = [];
|
|
1095
|
+
weeks.forEach((start, column) => {
|
|
1096
|
+
const month = this.ownerOf(start);
|
|
1097
|
+
const last = months.at(-1);
|
|
1098
|
+
if (last && last.month === month) last.span++;
|
|
1099
|
+
else months.push({ column, span: 1, month });
|
|
1100
|
+
});
|
|
1101
|
+
const range = (event: CalendarEvent) => {
|
|
1102
|
+
const end = event.end && isDay(event.end) && event.end > event.start ? event.end : event.start;
|
|
1103
|
+
return end === event.start ? dayLabel(event.start) : this.rangeTitle(event.start, end);
|
|
1104
|
+
};
|
|
1105
|
+
return this.swipeFrame(html`<div
|
|
1106
|
+
class="swimlane"
|
|
1107
|
+
part="swimlane"
|
|
1108
|
+
role="group"
|
|
1109
|
+
?data-travel=${this.travelled}
|
|
1110
|
+
aria-label=${this.nameOr(this.monthRangeTitle(weeks))}
|
|
1111
|
+
style=${`--ds-calendar-weeks: ${weeks.length}; --ds-calendar-rows: ${rows}; --ds-calendar-fit: ${fit}`}
|
|
1112
|
+
>
|
|
1113
|
+
<div class="swimlane-months" part="months" aria-hidden="true">
|
|
1114
|
+
${months.map(({ column, span, month }, index) => html`<span class="swimlane-month" style=${`grid-column: ${column + 1} / span ${span}`}>${
|
|
1115
|
+
index === 0 || month.slice(5, 7) === "01" ? monthTitle(month, "short") : monthName(month, "short")
|
|
1116
|
+
}</span>`)}
|
|
1117
|
+
</div>
|
|
1118
|
+
<div class="swimlane-dates" part="dates" aria-hidden="true">
|
|
1119
|
+
${weeks.map((start, column) => column === todayWeek
|
|
1120
|
+
? html`<span class="swimlane-date" data-today><span class="date">${dateNumber(today)}</span></span>`
|
|
1121
|
+
: html`<span class="swimlane-date"><span class="date">${dateNumber(start)}</span></span>`)}
|
|
1122
|
+
</div>
|
|
1123
|
+
<div class="swimlane-scroll" part="scroll"><div class="swimlane-grid" part="grid">
|
|
1124
|
+
${Array.from({ length: rows }, (_, row) => weeks.map((_, column) => html`<span
|
|
1125
|
+
class="cell"
|
|
1126
|
+
aria-hidden="true"
|
|
1127
|
+
style=${`grid-row: ${row + 1}; grid-column: ${column + 1}`}
|
|
1128
|
+
?data-first-row=${row === 0}
|
|
1129
|
+
?data-last-row=${row === rows - 1}
|
|
1130
|
+
?data-first-column=${column === 0}
|
|
1131
|
+
?data-last-column=${column === weeks.length - 1}
|
|
1132
|
+
></span>`))}
|
|
1133
|
+
${bars.map(({ event, row, from, to }, index) => {
|
|
1134
|
+
const fill = event.color ? `--ds-calendar-event-fill: ${event.color}; --ds-calendar-event-ink: var(--ds-slate); ` : "";
|
|
1135
|
+
return html`<button
|
|
1136
|
+
type="button"
|
|
1137
|
+
class="event"
|
|
1138
|
+
part="event"
|
|
1139
|
+
data-event=${index}
|
|
1140
|
+
aria-label=${`${event.title}, ${range(event)}`}
|
|
1141
|
+
style=${`${fill}grid-row: ${row + 1}; grid-column: ${from + 1} / ${to + 2}`}
|
|
1142
|
+
@click=${() => this.pressEvent(event, event.start)}
|
|
1143
|
+
><span class="name">${event.title}</span></button>`;
|
|
1144
|
+
})}
|
|
1145
|
+
</div></div>
|
|
1146
|
+
</div>`);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/** The first day of each week activity draws: from the week holding the month's first to the one holding its last month's last day. */
|
|
1150
|
+
private get activityWeeks(): Day[] {
|
|
1151
|
+
const first = this.shownMonth;
|
|
1152
|
+
const end = addDays(addMonths(first, whole(this.months, 1, 36, 1)), -1);
|
|
1153
|
+
const weeks: Day[] = [];
|
|
1154
|
+
for (let day = addDays(first, -((weekdayOf(first) - this.firstDay + 7) % 7)); day <= end; day = addDays(day, 7)) weeks.push(day);
|
|
1155
|
+
return weeks;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* The days as quiet squares: every other weekday named down the start, the
|
|
1160
|
+
* months over their weeks, and a week to a column a square wide, its days
|
|
1161
|
+
* down it, each a square drawn two pixels in with a picture's corners,
|
|
1162
|
+
* filled with its events' colours. Days outside the months stay empty.
|
|
1163
|
+
*/
|
|
1164
|
+
private renderActivity() {
|
|
1165
|
+
const weeks = this.activityWeeks;
|
|
1166
|
+
const first = this.shownMonth;
|
|
1167
|
+
const end = addMonths(first, whole(this.months, 1, 36, 1));
|
|
1168
|
+
this.byDay = eventsByDay(this.events ?? [], weeks[0], addDays(weeks.at(-1)!, 6));
|
|
1169
|
+
const short = weekdayNames(this.firstDay, "short");
|
|
1170
|
+
const narrow = weekdayNames(this.firstDay, "narrow");
|
|
1171
|
+
const long = weekdayNames(this.firstDay, "long");
|
|
1172
|
+
const today = this.todayDay;
|
|
1173
|
+
const months: { column: number; span: number; month: Day }[] = [];
|
|
1174
|
+
// A week is named by the month owning most of it, held to the months drawn.
|
|
1175
|
+
const lastMonth = addMonths(end, -1);
|
|
1176
|
+
weeks.forEach((start, column) => {
|
|
1177
|
+
const owner = this.ownerOf(start);
|
|
1178
|
+
const month = owner < first ? first : owner > lastMonth ? lastMonth : owner;
|
|
1179
|
+
const last = months.at(-1);
|
|
1180
|
+
if (last && last.month === month) last.span++;
|
|
1181
|
+
else months.push({ column, span: 1, month });
|
|
1182
|
+
});
|
|
1183
|
+
return html`<div class="calendar" part="calendar">
|
|
1184
|
+
${this.renderHeader()}
|
|
1185
|
+
<div class="activity" part="activity" style=${`--ds-calendar-weeks: ${weeks.length}`}>
|
|
1186
|
+
<div class="activity-rail" part="weekdays" aria-hidden="true">
|
|
1187
|
+
${short.map((name, row) => html`<span class="activity-weekday" data-narrow=${narrow[row]}>${row % 2 === 1 ? name : ""}</span>`)}
|
|
1188
|
+
</div>
|
|
1189
|
+
<div class="activity-weeks">
|
|
1190
|
+
<div class="activity-months" part="months" aria-hidden="true">
|
|
1191
|
+
${months.map(({ column, span, month }, index) => html`<span class="activity-month" style=${`grid-column: ${column + 1} / span ${span}`}>${
|
|
1192
|
+
index === 0 || month.slice(5, 7) === "01" ? monthTitle(month, "short") : monthName(month, "short")
|
|
1193
|
+
}</span>`)}
|
|
1194
|
+
</div>
|
|
1195
|
+
<div
|
|
1196
|
+
class="activity-grid"
|
|
1197
|
+
part="grid"
|
|
1198
|
+
role="grid"
|
|
1199
|
+
aria-label=${this.nameOr(months.length > 1 ? this.monthRangeTitle(weeks) : monthTitle(first))}
|
|
1200
|
+
@click=${this.onClick}
|
|
1201
|
+
>
|
|
1202
|
+
${short.map((_, row) => html`<div role="row" class="activity-row">
|
|
1203
|
+
<span role="rowheader" class="activity-hidden">${long[row]}</span>
|
|
1204
|
+
${weeks.map((start, column) => {
|
|
1205
|
+
const day = addDays(start, row);
|
|
1206
|
+
const style = `grid-row: ${row + 1}; grid-column: ${column + 1}`;
|
|
1207
|
+
if (day < first || day >= end) return html`<span role="gridcell" class="activity-day" aria-hidden="true" style=${style}></span>`;
|
|
1208
|
+
const events = this.eventsOn(day);
|
|
1209
|
+
const fill = activityFill(events);
|
|
1210
|
+
const label = this.cellLabel(day, events);
|
|
1211
|
+
const { selected } = this.selection(day);
|
|
1212
|
+
return html`<span
|
|
1213
|
+
role="gridcell"
|
|
1214
|
+
class="activity-day"
|
|
1215
|
+
part=${["day", day === today ? "today" : "", events.length ? "filled" : ""].filter(Boolean).join(" ")}
|
|
1216
|
+
data-day=${day}
|
|
1217
|
+
aria-label=${label}
|
|
1218
|
+
title=${label}
|
|
1219
|
+
aria-current=${day === today ? "date" : nothing}
|
|
1220
|
+
aria-selected=${this.selectable ? String(selected) : nothing}
|
|
1221
|
+
?data-filled=${events.length > 0}
|
|
1222
|
+
?data-selected=${selected}
|
|
1223
|
+
style=${`${style};${fill ? ` --ds-calendar-cell-fill: ${fill};` : ""}`}
|
|
1224
|
+
></span>`;
|
|
1225
|
+
})}
|
|
1226
|
+
</div>`)}
|
|
1227
|
+
</div>
|
|
1228
|
+
</div>
|
|
1229
|
+
</div>
|
|
1230
|
+
</div>`;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
/** A view drawn under the header that swipes as the arrows move: the swimlane and a page's own view. */
|
|
1234
|
+
private swipeFrame(content: unknown) {
|
|
1235
|
+
const swipes = !this.noHeader;
|
|
1236
|
+
return html`<div
|
|
1237
|
+
class="calendar"
|
|
1238
|
+
part="calendar"
|
|
1239
|
+
?data-swipe=${swipes}
|
|
1240
|
+
@pointerdown=${swipes ? this.onSwipeStart : nothing}
|
|
1241
|
+
@pointerup=${swipes ? this.onSwipeEnd : nothing}
|
|
1242
|
+
@pointercancel=${() => (this.swipe = undefined)}
|
|
1243
|
+
@wheel=${swipes ? this.onSwipeWheel : nothing}
|
|
1244
|
+
@click=${{ handleEvent: this.onSwipeClick, capture: true }}
|
|
1245
|
+
>
|
|
1246
|
+
${this.renderHeader()}
|
|
1247
|
+
${content}
|
|
1248
|
+
</div>`;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/** A page's own view: what it puts in the slot named after the view, as wide as the month. */
|
|
1252
|
+
private renderOwnView() {
|
|
1253
|
+
const view = this.shownView;
|
|
1254
|
+
return this.swipeFrame(html`<div class="own-view" part="own-view" data-view=${view} ?data-travel=${this.travelled}><slot name=${view}></slot></div>`);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* One day: its events without a time across the top, a day's width each,
|
|
1259
|
+
* then its hours, a square each, in an area that scrolls, with each timed
|
|
1260
|
+
* event at its hour beside the others of that hour.
|
|
1261
|
+
*/
|
|
1262
|
+
private renderDayView() {
|
|
1263
|
+
const day = this.shownDay;
|
|
1264
|
+
this.byDay = eventsByDay(this.events ?? [], day, day);
|
|
1265
|
+
const events = this.eventsOn(day);
|
|
1266
|
+
const allDay = events.filter(event => !event.time);
|
|
1267
|
+
const hourName = dateFormatter({ hour: "numeric", timeZone: "UTC" });
|
|
1268
|
+
const hours = Array.from({ length: 24 }, (_, hour) => ({
|
|
1269
|
+
hour,
|
|
1270
|
+
name: hourName.format(Date.UTC(2000, 0, 1, hour)),
|
|
1271
|
+
events: events.filter(event => event.time && Number(event.time.slice(0, 2)) === hour),
|
|
1272
|
+
}));
|
|
1273
|
+
return html`<div class="calendar" part="calendar">
|
|
1274
|
+
${this.renderHeader()}
|
|
1275
|
+
${this.level !== "days" ? this.renderPicks(6) : html`<div
|
|
1276
|
+
class="day-view"
|
|
1277
|
+
part="day-view"
|
|
1278
|
+
data-day=${day}
|
|
1279
|
+
aria-label=${this.cellLabel(day, events)}
|
|
1280
|
+
@click=${this.onClick}
|
|
1281
|
+
>
|
|
1282
|
+
${allDay.length ? html`<div class="all-day" part="all-day">${allDay.map(event => this.renderEvent(event, events, 1))}</div>` : nothing}
|
|
1283
|
+
<div class="hours" part="hours">
|
|
1284
|
+
${hours.map(({ hour, name, events: atHour }) => html`<div class="hour" part="hour" data-hour=${hour} ?data-busy=${atHour.length > 0}>
|
|
1285
|
+
<span class="hour-name">${name}</span>
|
|
1286
|
+
<span class="hour-events">${atHour.map(event => this.renderEvent(event, events, 1))}</span>
|
|
1287
|
+
</div>`)}
|
|
1288
|
+
</div>
|
|
1289
|
+
</div>`}
|
|
1290
|
+
</div>`;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/** An event on its line: one bar over `span` days, its name written once. */
|
|
1294
|
+
private renderEvent(event: CalendarEvent, events: readonly CalendarEvent[], span: number) {
|
|
1295
|
+
const fill = event.color ? `--ds-calendar-event-fill: ${event.color}; --ds-calendar-event-ink: var(--ds-slate);` : "";
|
|
1296
|
+
return html`<span
|
|
1297
|
+
class="event"
|
|
1298
|
+
part="event"
|
|
1299
|
+
data-event=${events.indexOf(event)}
|
|
1300
|
+
data-span=${span > 1 ? span : nothing}
|
|
1301
|
+
style=${fill || span > 1 ? `${fill}${span > 1 ? ` --ds-calendar-event-span: ${span}` : ""}` : nothing}
|
|
1302
|
+
>${event.time ? html`<span class="time">${event.time}</span>` : nothing}<span class="name">${event.title}</span></span>`;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
673
1305
|
render() {
|
|
674
1306
|
if (this.picker) return this.renderPicker();
|
|
1307
|
+
if (this.activity && this.level === "days") return this.renderActivity();
|
|
675
1308
|
const month = this.shownMonth;
|
|
676
1309
|
const first = this.firstDay;
|
|
677
|
-
const
|
|
1310
|
+
const view = this.shownView;
|
|
1311
|
+
if (view === "day" && this.level === "days") return this.renderDayView();
|
|
1312
|
+
if (view === "swimlane" && this.level === "days") return this.renderSwimlane();
|
|
1313
|
+
if (this.ownView && this.level === "days") return this.renderOwnView();
|
|
1314
|
+
const weeks = view === "week" ? [this.weekOf(this.shownDay)] : monthWeeks(month, first, this.fixed);
|
|
678
1315
|
this.byDay = eventsByDay(this.events ?? [], weeks[0][0], weeks.at(-1)![6]);
|
|
1316
|
+
this.laidOut = new Map();
|
|
1317
|
+
// In view: the week's days, or the month's (the days around it are drawn only with outside-days).
|
|
1318
|
+
const inView = (day: Day) => view === "week" || sameMonth(day, month);
|
|
1319
|
+
const drawn = (day: Day) => inView(day) || this.outsideDays;
|
|
1320
|
+
// A week shows every event of each day: as many lines as its fullest day needs.
|
|
1321
|
+
const depth = view === "week"
|
|
1322
|
+
? Math.max(this.eventLines, ...weekLines(weeks[0], this.byDay, 99).map(day => day.lines.length))
|
|
1323
|
+
: this.eventLines;
|
|
1324
|
+
for (const week of weeks) weekLines(week, this.byDay, depth, drawn).forEach((laid, column) => this.laidOut.set(week[column], laid));
|
|
1325
|
+
const tabDay = view === "week"
|
|
1326
|
+
? [this.focusDay, this.chosen.start, this.todayDay].find(day => day && weeks[0].includes(day)) ?? weeks[0][0]
|
|
1327
|
+
: this.tabDay;
|
|
679
1328
|
// Heat is measured over the days of the month shown, so the neighbours' days never set its scale.
|
|
680
1329
|
this.heatByDay = this.heat ? heatLevels(new Map([...this.byDay].filter(([day]) => sameMonth(day, month))), this.heatMax) : new Map();
|
|
681
1330
|
// A compact month names its days in a letter (one square) or two (wider); a full month in short.
|
|
@@ -685,14 +1334,33 @@ export class Calendar extends CalendarBase {
|
|
|
685
1334
|
: weekdayNames(first, "short").map(name => (/^\p{Script=Latin}/u.test(name) ? name.replace(/\.$/, "").slice(0, 2) : name))
|
|
686
1335
|
: weekdayNames(first, "short");
|
|
687
1336
|
const long = weekdayNames(first, "long");
|
|
688
|
-
|
|
1337
|
+
// Inside a day's box the name is short where the day is three squares or more, and a letter where it is narrower.
|
|
1338
|
+
const inside = this.squaresPerDay >= 3 ? short : weekdayNames(first, "narrow");
|
|
1339
|
+
const today = this.todayDay;
|
|
1340
|
+
const todayColumn = drawn(today) && weeks.some(week => week.includes(today)) ? (weekdayOf(today) - first + 7) % 7 : -1;
|
|
1341
|
+
const swipes = !this.noHeader;
|
|
1342
|
+
return html`<div
|
|
1343
|
+
class="calendar"
|
|
1344
|
+
part="calendar"
|
|
1345
|
+
?data-swipe=${swipes}
|
|
1346
|
+
@focusout=${this.onFocusOut}
|
|
1347
|
+
@pointerover=${this.onPointerOver}
|
|
1348
|
+
@pointerleave=${() => (this.hoverDay = undefined)}
|
|
1349
|
+
@pointerdown=${swipes ? this.onSwipeStart : nothing}
|
|
1350
|
+
@pointerup=${swipes ? this.onSwipeEnd : nothing}
|
|
1351
|
+
@pointercancel=${() => (this.swipe = undefined)}
|
|
1352
|
+
@wheel=${swipes ? this.onSwipeWheel : nothing}
|
|
1353
|
+
@click=${{ handleEvent: this.onSwipeClick, capture: true }}
|
|
1354
|
+
>
|
|
689
1355
|
${this.renderHeader()}
|
|
690
1356
|
${this.level !== "days" && !this.noHeader ? this.renderPicks(weeks.length) : html`<div
|
|
691
1357
|
class="grid"
|
|
692
1358
|
part="grid"
|
|
693
1359
|
role="grid"
|
|
1360
|
+
data-view=${view}
|
|
694
1361
|
?data-travel=${this.travelled}
|
|
695
|
-
aria-label=${this.nameOr(monthTitle(month))}
|
|
1362
|
+
aria-label=${this.nameOr(view === "week" ? this.rangeTitle(weeks[0][0], weeks[0][6]) : monthTitle(month))}
|
|
1363
|
+
style=${view === "week" ? `--ds-calendar-lines: ${depth}` : nothing}
|
|
696
1364
|
aria-multiselectable=${this.selectable ? "true" : nothing}
|
|
697
1365
|
@keydown=${this.onKeyDown}
|
|
698
1366
|
@click=${this.onClick}
|
|
@@ -700,7 +1368,13 @@ export class Calendar extends CalendarBase {
|
|
|
700
1368
|
<div role="row" class="weekdays" part="weekdays">
|
|
701
1369
|
${short.map((name, index) => html`<span role="columnheader" class="weekday" aria-label=${long[index]}>${name}</span>`)}
|
|
702
1370
|
</div>
|
|
703
|
-
${weeks.map(week => html`<div role="row" class="week" part="week">${week.map((day, column) => this.renderDay(day,
|
|
1371
|
+
${weeks.map((week, row) => html`<div role="row" class="week" part="week">${week.map((day, column) => this.renderDay(day, inView(day), tabDay, this.compact ? undefined : {
|
|
1372
|
+
// A full month names its days inside the first week's boxes, at their start, today's weekday in today's red.
|
|
1373
|
+
weekday: row === 0 ? inside[column] : undefined,
|
|
1374
|
+
today: row === 0 && todayColumn === column,
|
|
1375
|
+
// The week's number on its first day, counted from the week's middle so a week from Sunday takes its Monday's.
|
|
1376
|
+
week: this.weekNumbers && column === 0 && this.squaresPerDay >= 3 && week.some(drawn) ? isoWeek(addDays(day, 3)) : undefined,
|
|
1377
|
+
}))}</div>`)}
|
|
704
1378
|
</div>`}
|
|
705
1379
|
${this.renderClear()}
|
|
706
1380
|
</div>`;
|
|
@@ -779,7 +1453,7 @@ export class Calendar extends CalendarBase {
|
|
|
779
1453
|
month.renderRoot.querySelector<HTMLElement>('[data-day][tabindex="0"]')?.focus({ preventScroll: true });
|
|
780
1454
|
}
|
|
781
1455
|
|
|
782
|
-
/**
|
|
1456
|
+
/** Directly against the field, or above when there is more room there; held inside the screen. */
|
|
783
1457
|
private place = () => {
|
|
784
1458
|
const panel = this.panel, trigger = this.trigger;
|
|
785
1459
|
if (!this.shown || !panel || !trigger) return;
|
|
@@ -789,7 +1463,7 @@ export class Calendar extends CalendarBase {
|
|
|
789
1463
|
const left = viewport?.offsetLeft ?? 0, top = viewport?.offsetTop ?? 0;
|
|
790
1464
|
const width = viewport?.width ?? view.innerWidth, height = viewport?.height ?? view.innerHeight;
|
|
791
1465
|
const box = panel.getBoundingClientRect();
|
|
792
|
-
const gap =
|
|
1466
|
+
const gap = 0;
|
|
793
1467
|
// The face is one control high at the foot of its square: measure from that square.
|
|
794
1468
|
const square = Math.max(rect.height, gap * 2);
|
|
795
1469
|
const foot = rect.bottom, head = rect.bottom - square;
|