diff --git a/package.json b/package.json index 931f7b40..2c1d834a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "boot-cell", - "version": "2.0.0-beta.10", + "version": "2.0.0-beta.11", "license": "LGPL-3.0", "author": "shiy2008@gmail.com", "description": "Web Components UI library based on WebCell v3, BootStrap v5, BootStrap Icon v1 & FontAwesome v6", @@ -24,7 +24,7 @@ "main": "dist/index.js", "module": "dist/index.esm.js", "dependencies": { - "@swc/helpers": "^0.5.3", + "@swc/helpers": "^0.5.6", "classnames": "^2.5.1", "dom-renderer": "^2.0.6", "mobx": "^6.12.0", @@ -52,25 +52,25 @@ "@peculiar/webcrypto": "^1.4.5", "@tech_query/snabbdom-looks-like": "^2.0.1", "@types/classnames": "^2.3.1", - "@types/jest": "^29.5.11", + "@types/jest": "^29.5.12", "@types/resize-observer-browser": "^0.1.11", "@types/turndown": "^5.0.4", "cross-env": "^7.0.3", "element-internals-polyfill": "^1.3.10", - "husky": "^9.0.6", + "husky": "^9.0.10", "identity-obj-proxy": "^3.0.0", "iterable-observer": "^1.0.1", "jest": "^29.7.0", "less": "^4.2.0", - "lint-staged": "^15.2.0", + "lint-staged": "^15.2.2", "markdown-area-element": "^0.2.3", "open-cli": "^8.0.0", "parcel": "~2.11.0", - "prettier": "^3.2.4", + "prettier": "^3.2.5", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "typedoc": "^0.25.7", - "typedoc-plugin-mdn-links": "^3.1.14", + "typedoc-plugin-mdn-links": "^3.1.15", "typescript": "~5.3.3" }, "scripts": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd487807..3186d24b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@swc/helpers': - specifier: ^0.5.3 - version: 0.5.3 + specifier: ^0.5.6 + version: 0.5.6 classnames: specifier: ^2.5.1 version: 2.5.1 @@ -36,7 +36,7 @@ devDependencies: version: 3.4.0 '@parcel/config-default': specifier: ^2.11.0 - version: 2.11.0(@parcel/core@2.11.0)(@swc/helpers@0.5.3)(typescript@5.3.3) + version: 2.11.0(@parcel/core@2.11.0)(@swc/helpers@0.5.6)(typescript@5.3.3) '@parcel/packager-ts': specifier: ~2.11.0 version: 2.11.0 @@ -59,8 +59,8 @@ devDependencies: specifier: ^2.3.1 version: 2.3.1 '@types/jest': - specifier: ^29.5.11 - version: 29.5.11 + specifier: ^29.5.12 + version: 29.5.12 '@types/resize-observer-browser': specifier: ^0.1.11 version: 0.1.11 @@ -74,8 +74,8 @@ devDependencies: specifier: ^1.3.10 version: 1.3.10 husky: - specifier: ^9.0.6 - version: 9.0.6 + specifier: ^9.0.10 + version: 9.0.10 identity-obj-proxy: specifier: ^3.0.0 version: 3.0.0 @@ -89,8 +89,8 @@ devDependencies: specifier: ^4.2.0 version: 4.2.0 lint-staged: - specifier: ^15.2.0 - version: 15.2.0 + specifier: ^15.2.2 + version: 15.2.2 markdown-area-element: specifier: ^0.2.3 version: 0.2.3(typescript@5.3.3) @@ -99,10 +99,10 @@ devDependencies: version: 8.0.0 parcel: specifier: ~2.11.0 - version: 2.11.0(@swc/helpers@0.5.3)(typescript@5.3.3) + version: 2.11.0(@swc/helpers@0.5.6)(typescript@5.3.3) prettier: - specifier: ^3.2.4 - version: 3.2.4 + specifier: ^3.2.5 + version: 3.2.5 ts-jest: specifier: ^29.1.2 version: 29.1.2(@jest/types@29.6.3)(jest@29.7.0)(typescript@5.3.3) @@ -113,8 +113,8 @@ devDependencies: specifier: ^0.25.7 version: 0.25.7(typescript@5.3.3) typedoc-plugin-mdn-links: - specifier: ^3.1.14 - version: 3.1.14(typedoc@0.25.7) + specifier: ^3.1.15 + version: 3.1.15(typedoc@0.25.7) typescript: specifier: ~5.3.3 version: 5.3.3 @@ -896,7 +896,7 @@ packages: - '@parcel/core' dev: true - /@parcel/config-default@2.11.0(@parcel/core@2.11.0)(@swc/helpers@0.5.3)(typescript@5.3.3): + /@parcel/config-default@2.11.0(@parcel/core@2.11.0)(@swc/helpers@0.5.6)(typescript@5.3.3): resolution: {integrity: sha512-1e2+qcZkm5/0f4eI20p/DemcYiSxq9d/eyjpTXA7PulJaHbL1wonwUAuy3mvnAvDnLOJmAk/obDVgX1ZfxMGtg==} peerDependencies: '@parcel/core': ^2.11.0 @@ -909,7 +909,7 @@ packages: '@parcel/optimizer-htmlnano': 2.11.0(typescript@5.3.3) '@parcel/optimizer-image': 2.11.0(@parcel/core@2.11.0) '@parcel/optimizer-svgo': 2.11.0 - '@parcel/optimizer-swc': 2.11.0(@swc/helpers@0.5.3) + '@parcel/optimizer-swc': 2.11.0(@swc/helpers@0.5.6) '@parcel/packager-css': 2.11.0 '@parcel/packager-html': 2.11.0 '@parcel/packager-js': 2.11.0 @@ -1113,7 +1113,7 @@ packages: - '@parcel/core' dev: true - /@parcel/optimizer-swc@2.11.0(@swc/helpers@0.5.3): + /@parcel/optimizer-swc@2.11.0(@swc/helpers@0.5.6): resolution: {integrity: sha512-ftf42F3JyZxJb6nnLlgNGyNQ273YOla4dFGH/tWC8iTwObHUpWe7cMbCGcrSJBvAlsLkZfLpFNAXFxUgxdKyHQ==} engines: {node: '>= 12.0.0', parcel: ^2.11.0} dependencies: @@ -1121,7 +1121,7 @@ packages: '@parcel/plugin': 2.11.0 '@parcel/source-map': 2.1.1 '@parcel/utils': 2.11.0 - '@swc/core': 1.3.102(@swc/helpers@0.5.3) + '@swc/core': 1.3.102(@swc/helpers@0.5.6) nullthrows: 1.1.1 transitivePeerDependencies: - '@parcel/core' @@ -1417,7 +1417,7 @@ packages: '@parcel/source-map': 2.1.1 '@parcel/utils': 2.11.0 '@parcel/workers': 2.11.0(@parcel/core@2.11.0) - '@swc/helpers': 0.5.3 + '@swc/helpers': 0.5.6 browserslist: 4.22.2 nullthrows: 1.1.1 regenerator-runtime: 0.13.11 @@ -1859,7 +1859,7 @@ packages: dev: true optional: true - /@swc/core@1.3.102(@swc/helpers@0.5.3): + /@swc/core@1.3.102(@swc/helpers@0.5.6): resolution: {integrity: sha512-OAjNLY/f6QWKSDzaM3bk31A+OYHu6cPa9P/rFIx8X5d24tHXUpRiiq6/PYI6SQRjUPlB72GjsjoEU8F+ALadHg==} engines: {node: '>=10'} requiresBuild: true @@ -1870,7 +1870,7 @@ packages: optional: true dependencies: '@swc/counter': 0.1.2 - '@swc/helpers': 0.5.3 + '@swc/helpers': 0.5.6 '@swc/types': 0.1.5 optionalDependencies: '@swc/core-darwin-arm64': 1.3.102 @@ -1908,8 +1908,8 @@ packages: tslib: 2.6.2 dev: true - /@swc/helpers@0.5.3: - resolution: {integrity: sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==} + /@swc/helpers@0.5.6: + resolution: {integrity: sha512-aYX01Ke9hunpoCexYAgQucEpARGQ5w/cqHFrIR+e9gdKb1QWTsVJuTJ2ozQzIAxLyRQe/m+2RqzkyOOGiMKRQA==} dependencies: tslib: 2.6.2 @@ -2007,8 +2007,8 @@ packages: '@types/istanbul-lib-report': 3.0.3 dev: true - /@types/jest@29.5.11: - resolution: {integrity: sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==} + /@types/jest@29.5.12: + resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} dependencies: expect: 29.7.0 pretty-format: 29.7.0 @@ -2976,8 +2976,8 @@ packages: engines: {node: '>=16.17.0'} dev: true - /husky@9.0.6: - resolution: {integrity: sha512-EEuw/rfTiMjOfuL7pGO/i9otg1u36TXxqjIA6D9qxVjd/UXoDOsLor/BSFf5hTK50shwzCU3aVVwdXDp/lp7RA==} + /husky@9.0.10: + resolution: {integrity: sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==} engines: {node: '>=18'} hasBin: true dev: true @@ -3197,7 +3197,7 @@ packages: /iterable-observer@1.0.1: resolution: {integrity: sha512-qy2Kuf1drKVmWgWaRsqdM8EHanAW4xS37j1nFdVP07qiN0Mj4D5sl9dYA8mZJdX/d3De4bhZoFuVWXvcOM1usg==} dependencies: - '@swc/helpers': 0.5.3 + '@swc/helpers': 0.5.6 dev: true /jest-changed-files@29.7.0: @@ -3785,8 +3785,8 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true - /lint-staged@15.2.0: - resolution: {integrity: sha512-TFZzUEV00f+2YLaVPWBWGAMq7So6yQx+GG8YRMDeOEIf95Zn5RyiLMsEiX4KTNl9vq/w+NqRJkLA1kPIo15ufQ==} + /lint-staged@15.2.2: + resolution: {integrity: sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==} engines: {node: '>=18.12.0'} hasBin: true dependencies: @@ -3795,7 +3795,7 @@ packages: debug: 4.3.4 execa: 8.0.1 lilconfig: 3.0.0 - listr2: 8.0.0 + listr2: 8.0.1 micromatch: 4.0.5 pidtree: 0.6.0 string-argv: 0.3.2 @@ -3804,8 +3804,8 @@ packages: - supports-color dev: true - /listr2@8.0.0: - resolution: {integrity: sha512-u8cusxAcyqAiQ2RhYvV7kRKNLgUvtObIbhOX2NCXqvp1UU32xIg5CT22ykS2TPKJXZWJwtK3IKLiqAGlGNE+Zg==} + /listr2@8.0.1: + resolution: {integrity: sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==} engines: {node: '>=18.0.0'} dependencies: cli-truncate: 4.0.0 @@ -4158,7 +4158,7 @@ packages: engines: {node: '>=6'} dev: true - /parcel@2.11.0(@swc/helpers@0.5.3)(typescript@5.3.3): + /parcel@2.11.0(@swc/helpers@0.5.6)(typescript@5.3.3): resolution: {integrity: sha512-H/RI1/DmuOkL8RuG/EpNPvtzrbF+7jA/R56ydEEm+lqFbYktKB4COR7JXdHkZXRgbSJyimrFB8d0r9+SaRnj0Q==} engines: {node: '>= 12.0.0'} hasBin: true @@ -4166,7 +4166,7 @@ packages: '@parcel/core': optional: true dependencies: - '@parcel/config-default': 2.11.0(@parcel/core@2.11.0)(@swc/helpers@0.5.3)(typescript@5.3.3) + '@parcel/config-default': 2.11.0(@parcel/core@2.11.0)(@swc/helpers@0.5.6)(typescript@5.3.3) '@parcel/core': 2.11.0 '@parcel/diagnostic': 2.11.0 '@parcel/events': 2.11.0 @@ -4315,8 +4315,8 @@ packages: posthtml-render: 3.0.0 dev: true - /prettier@3.2.4: - resolution: {integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==} + /prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} hasBin: true dev: true @@ -4865,8 +4865,8 @@ packages: engines: {node: '>=14.16'} dev: true - /typedoc-plugin-mdn-links@3.1.14(typedoc@0.25.7): - resolution: {integrity: sha512-zPnzIHvMChiuS4BvI4mbs89HwS6HSPE+q7rPRliJCeIkQYqW7YaTXuBwbUQFPg++02KNUY0IMp0MFvouoi1Qgw==} + /typedoc-plugin-mdn-links@3.1.15(typedoc@0.25.7): + resolution: {integrity: sha512-PniQgrCAmhbJf8fPiZULxPV9aYVszNoEk1R7XbTBIMluqplaUkG3qfckthb2l++5yDR1lHEycTMu6hQyKyz+QA==} peerDependencies: typedoc: '>= 0.23.14 || 0.24.x || 0.25.x' dependencies: @@ -4976,7 +4976,7 @@ packages: element-internals-polyfill: ^1 jsdom: '>=21' dependencies: - '@swc/helpers': 0.5.3 + '@swc/helpers': 0.5.6 dom-renderer: 2.0.6(typescript@5.3.3) element-internals-polyfill: 1.3.10 mobx: 6.12.0 @@ -5003,7 +5003,7 @@ packages: peerDependencies: typescript: '>=4.1' dependencies: - '@swc/helpers': 0.5.3 + '@swc/helpers': 0.5.6 element-internals-polyfill: 1.3.10 regenerator-runtime: 0.14.1 typescript: 5.3.3 diff --git a/source/Carousel.tsx b/source/Carousel.tsx new file mode 100644 index 00000000..8fec78ed --- /dev/null +++ b/source/Carousel.tsx @@ -0,0 +1,188 @@ +import { observable } from 'mobx'; +import { + FC, + WebCell, + WebCellProps, + attribute, + component, + observer, + on, + reaction +} from 'web-cell'; + +export interface CarouselItemProps extends WebCellProps { + interval?: number; +} + +export const CarouselItem: FC = ({ + className = '', + interval, + children, + ...props +}) => ( +
+ {children} +
+); + +export const CarouselCaption: FC> = ({ + className = '', + children, + ...props +}) => ( +
+ {children} +
+); + +interface ItemMeta { + caption?: string; +} + +export interface CarouselProps { + interval?: number; +} + +export interface Carousel extends WebCell {} + +@component({ + tagName: 'carousel-box', + mode: 'open' +}) +@observer +export class Carousel extends HTMLElement implements WebCell { + @attribute + @observable + accessor interval: number | undefined; + + @observable + accessor itemMeta: ItemMeta[] = []; + + @attribute + @observable + accessor currentIndex = 0; + + private timer: number; + + connectedCallback() { + if (this.interval) + this.timer ||= window.setInterval( + () => this.turnByOffset(1), + this.interval + ); + else this.handleActiveItem(this.currentIndex); + } + + disconnectedCallback() { + clearInterval(this.timer); + } + + @on('slotchange', 'slot') + handleSlotChange(_: Event, slot: HTMLSlotElement) { + const items = slot.assignedElements(); + + if (this.itemMeta.length !== items.length) + this.itemMeta = items.map(item => ({ + caption: ( + item.querySelector('.carousel-caption')?.textContent || + item.textContent + ).trim() + })); + } + + turnByOffset(delta: number) { + this.currentIndex = (this.currentIndex + delta) % this.itemMeta.length; + } + + @on('click', '.carousel > button') + handleButtonClick(_: MouseEvent, { dataset }: HTMLButtonElement) { + this.turnByOffset(dataset.bsSlide === 'next' ? 1 : -1); + } + + @on('keyup', '.carousel > button') + handleButtonPress({ key }: KeyboardEvent, { dataset }: HTMLButtonElement) { + if (key === 'Enter') + this.turnByOffset(dataset.bsSlide === 'next' ? 1 : -1); + } + + @on('click', '.carousel-indicators button') + handleIndicatorsButtonClick(_: MouseEvent, { dataset }: HTMLButtonElement) { + this.currentIndex = +dataset.bsSlideTo; + } + + @on('keyup', '.carousel-indicators button') + handleIndicatorsButtonPress( + { key }: KeyboardEvent, + { dataset }: HTMLButtonElement + ) { + if (key === 'Enter') this.currentIndex = +dataset.bsSlideTo; + } + + @reaction(({ currentIndex }) => currentIndex) + handleActiveItem(currentIndex: number) { + this.querySelector('.carousel-item.active')?.classList.remove('active'); + + this.children[currentIndex].classList.add('active'); + } + + renderContent() { + const { itemMeta, currentIndex } = this; + + return ( +
+
+ {itemMeta.map(({ caption }, index) => ( +
+
+ +
+ + +
+ ); + } + + render() { + return ( + <> + + {this.renderContent()} + + ); + } +} diff --git a/source/index.ts b/source/index.ts index bbbf3fc9..2ac23f46 100644 --- a/source/index.ts +++ b/source/index.ts @@ -15,6 +15,7 @@ export * from './Tooltip'; export * from './Dropdown'; export * from './Collapse'; export * from './Accordion'; +export * from './Carousel'; export * from './Nav'; export * from './Navbar'; export * from './Offcanvas';