diff --git a/.gitignore b/.gitignore index 7f211952..1cd62e35 100644 --- a/.gitignore +++ b/.gitignore @@ -69,4 +69,3 @@ out-tsc # Vitepress docs/.vitepress/dist docs/.vitepress/cache -docs/dev/components/custom-elements.json diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 9c5b6e0a..7b8f58a1 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,14 +1,12 @@ import { defineConfig } from "vitepress"; -import nav from "./nav"; -import sidebar from "./sidebar"; -import socialLinks from "./socialLinks"; +import sidebar from "../sidebar"; +import socialLinks from "../social-links"; // https://vitepress.dev/reference/site-config export default defineConfig({ title: "Tapsi components", description: "A set of components based on Tapsi design system.", base: "/web-components/", - vite: {}, head: [ ["link", { rel: "preconnect", href: "https://fonts.googleapis.com" }], [ @@ -30,6 +28,5 @@ export default defineConfig({ themeConfig: { sidebar, socialLinks, - nav, }, }); diff --git a/docs/.vitepress/nav.ts b/docs/.vitepress/nav.ts deleted file mode 100644 index 179fac26..00000000 --- a/docs/.vitepress/nav.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { DefaultTheme } from "vitepress"; - -export default [ - { - text: "Getting Started", - link: "/dev/getting-started", - }, - { - text: "Components", - link: "/dev/components/tap-avatar", - }, - { - text: "Design Guidelines", - link: "/design/guidelines", - }, - { - text: "Related Links", - items: [ - { - text: "Icon Library", - link: "https://tap30.github.io/icons", - target: "_blank", - }, - ], - }, -] as DefaultTheme.NavItem[]; diff --git a/docs/.vitepress/sidebar.ts b/docs/.vitepress/sidebar.ts deleted file mode 100644 index b41ed4bb..00000000 --- a/docs/.vitepress/sidebar.ts +++ /dev/null @@ -1,90 +0,0 @@ -import type { Package } from "custom-elements-manifest"; -import fs from "fs"; -import { DefaultTheme } from "vitepress"; - -const getComponentsDevItems = () => { - const file = fs.readFileSync("dist/custom-elements.json"); - const manifest = JSON.parse(file.toString()) as Package; - return manifest.modules - .filter(module => !!module.declarations?.length) - .map(module => { - if (!module.exports) - throw new Error(`Module has no export: ${module.path}`); - - const components = module.exports.filter( - exp => exp.kind === "custom-element-definition", - ); - - // For now, we assume we have only one custom element per module - const component = components[0]; - - return { - link: "/components/" + component.name, - text: component.name, - }; - }); -}; - -const getSidebarDevContents = (): DefaultTheme.Sidebar => { - return [ - { text: "Getting Started", link: "/getting-started" }, - { - text: "Design System Guidelines", - base: "/design/", - link: "/guidelines", - }, - { - text: "Components", - collapsed: false, - items: getComponentsDevItems(), - }, - { - text: "API References", - collapsed: false, - items: [ - { text: "CSS Parts", link: "/references/css-parts" }, - { - text: "Design Tokens", - items: [ - { text: "Colors", link: "/references/color-tokens" }, - { text: "Radius", link: "/references/radius-tokens" }, - { text: "Spacing", link: "/references/spacing-tokens" }, - { text: "Stroke", link: "/references/stroke-tokens" }, - { text: "Typography", link: "/references/typography-tokens" }, - { text: "Components", link: "/references/components-tokens" }, - ], - }, - ], - }, - ]; -}; - -const getComponentsDesignItems = () => { - const componentGuidelinesBasePath = "docs/design/components"; - const files = fs - .readdirSync(componentGuidelinesBasePath) - .filter(file => file.endsWith(".md")) - .map(file => file.replace(".md", "")); - return files.map(file => { - return { - link: "components/" + file, - text: file, - }; - }); -}; - -const getSidebarDesignContents = (): DefaultTheme.Sidebar => { - return [ - { text: "Introduction", link: "/guidelines" }, - { - text: "Component Guidelines", - base: "/design/", - items: getComponentsDesignItems(), - }, - ]; -}; - -export default { - "/dev/": { base: "/dev/", items: getSidebarDevContents() }, - "/design/": { base: "/design/", items: getSidebarDesignContents() }, -} as DefaultTheme.Sidebar; diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css index cfa9e5a8..357d3619 100644 --- a/docs/.vitepress/theme/custom.css +++ b/docs/.vitepress/theme/custom.css @@ -12,77 +12,3 @@ gap: 12px; flex-wrap: wrap; } - - -custom-element-manifest-viewer::part(container) { - display: flex; - flex-wrap: wrap; -} - -custom-element-manifest-viewer::part(label) { - border-radius: 4px; - padding: 3px 6px; - transition: color 0.25s, background-color 0.5s; - font-size: var(--vp-code-font-size); -} - -custom-element-manifest-viewer::part(input) { - transition: all 0.3s; - border: 1px solid var(--vp-c-divider); - padding: 8px; - background: transparent; - border-radius: 8px; -} - -custom-element-manifest-viewer::part(input):hover, custom-element-manifest-viewer::part(input):focus { - border: 1px solid var(--vp-c-brand-1); -} - -custom-element-manifest-viewer::part(component-preview) { - background-color: #fff; - /*background-image: linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%,#eee),linear-gradient(45deg,#eee 25%,transparent 25%,transparent 75%,#eee 75%,#eee);*/ - background-size: 20px 20px; - background-position: 0 0, 10px 10px; - color: black; - padding: 16px; - min-height: 100px; - border-radius: 8px 8px 0 0; - /*border-radius: 8px 0 0 0;*/ - display: flex; - align-items: center; - justify-content: center; - width: 100%; - /*width: 60%;*/ - direction: rtl; - position: relative; -} - -.vp-doc table { - display: table; - width: 100%; -} - -custom-element-manifest-viewer::part(knobs) { - display: flex; - flex-direction: column; - gap: 12px; - padding: 24px; - border-radius: 0; - /*border-radius: 0 8px 0 0;*/ - position: relative; - width: 100%; - /*width: 40%;*/ - background: var(--vp-c-bg-elv); -} - -custom-element-manifest-viewer::part(code) { - position: relative; - z-index: 1; - margin: 0; - padding: 20px 24px; - background: #0d1117; - overflow-x: auto; - font-size: var(--vp-code-font-size); - width: 100%; - border-radius: 0 0 8px 8px; -} diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 637ea1a4..d541d744 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,6 +1,5 @@ import DefaultTheme from "vitepress/theme"; import "../../../packages/theme/src/index.css"; -import "../../components"; import "./custom.css"; export default { diff --git a/docs/components/[component].md b/docs/components/[component].md new file mode 100644 index 00000000..9d01ef08 --- /dev/null +++ b/docs/components/[component].md @@ -0,0 +1,36 @@ +--- +prev: false +next: false +outline: "deep" +--- + + + + diff --git a/docs/components/[component].paths.ts b/docs/components/[component].paths.ts new file mode 100644 index 00000000..3f11316b --- /dev/null +++ b/docs/components/[component].paths.ts @@ -0,0 +1,194 @@ +import fs from "node:fs"; + +import path from "node:path"; +import { + type Component, + type Metadata, +} from "../../internals/doc-helpers/types"; +import { getFileMeta } from "../../scripts/utils"; +import { codify, tabulateData } from "../internals/utils/tabulateData"; + +export default { + paths() { + const { dirname } = getFileMeta(import.meta.url); + + const docsDir = path.resolve(dirname, ".."); + + const monorepoDir = path.join(docsDir, ".."); + const distDir = path.join(monorepoDir, "dist"); + const metadataFile = path.join(distDir, "components-metadata.json"); + + const metadata = JSON.parse( + fs.readFileSync(metadataFile).toString(), + ) as Metadata; + + return metadata.components.map(c => { + let content = ""; + + content += getComponentMarkdown(c); + + return { + params: { + component: c.kebabCaseName, + }, + content, + }; + }); + }, +}; + +const getComponentMarkdown = (component: Component) => { + let res = "\n"; + + if (component) { + res += `# ${component.name?.split("Tapsi")[1]}\n`; + + res += `${component.summary}\n`; + + res += getUsageMarkdown(component); + + res += getMembersMarkdown(component); + + res += getSlotsMarkdown(component); + + res += getEventsMarkdown(component); + } + + return res; +}; + +const getSlotsMarkdown = (component: Component) => { + const slots = component?.slots || []; + let res = ""; + + if ((slots?.length ?? 0) > 0) { + res += "\n## Slots\n"; + + res += tabulateData( + ["Name", "Description"], + slots?.map(({ description, name }) => [ + name ? codify(name) : "-", + description || "-", + ]), + ); + + res += `\n +::: tip + +You can use slot names as variables: + +\`\`\`ts +import { ${component.slotsEnumName} } from "${component.importPaths.webComponents}"; + +${slots + ?.map(slot => { + const slotEnum = + slot.name === "" ? "DEFAULT" : slot.name.toUpperCase().replace(/-/g, "_"); + + return `console.log(${component.slotsEnumName}.${slotEnum}); // "${slot.name}"`; + }) + .join("\n")} + +\`\`\` + +:::`; + } + + return res; +}; + +const getUsageMarkdown = (component: Component) => { + let res = ""; + + res += "\n\n"; + + return res; +}; + +const getMembersMarkdown = (component: Component) => { + const members = component.members || []; + let res = ""; + + if ((members.length ?? 0) > 0) { + res += "\n## Properties\n"; + + res += tabulateData( + ["Name", "Description", "Type", "Default Value"], + members.map(member => { + if (member.kind !== "field") return []; + const { type, name, description, default: defaultValue } = member; + + return [ + name ? codify(name) : "-", + description?.replace(/\\/g, "
") || "", + type?.text + ? type.text + ?.split("|") + .map(t => codify(t.trim().replace(/'/g, '"'))) + .join(" \\| ") + : "-", + defaultValue ? codify(defaultValue.replace(/'/g, '"')) : "-", + ]; + }), + ); + } + + return res; +}; + +const getEventsMarkdown = (component: Component) => { + const events = component?.events || []; + let res = ""; + + if ((events?.length ?? 0) > 0) { + res += "\n## Events\n"; + + res += tabulateData( + ["Name", "Description", "Type"], + events?.map(({ description, name, type }) => [ + name ? codify(name) : "-", + description + ?.replace(/cancelable/g, 'Cancelable') + .replace(/bubbles/g, 'Bubbles') || "-", + type?.text ? codify(type.text) : "-", + ]), + ); + + const exportedEvents = events.filter(e => { + return ( + !!e.type && e.type.text !== "Event" && e.type.text !== "InputEvent" + ); + }); + + if (exportedEvents.length > 0) { + res += `\n +::: tip + +You can import custom event names: + +\`\`\`ts +import { \n ${exportedEvents + .map(e => e.type.text) + .join(",\n ")}\n} from "${component.importPaths.webComponents}"; + +${exportedEvents + .map(event => { + const eventClass = event.type.text; + + return `element.addEventListener(${eventClass}.type, handle${component.name}${event.type.text.replace("Event", "")});`; + }) + .join("\n")} + +\`\`\` + +:::`; + } + } + + return res; +}; diff --git a/docs/components/doc-guideline-card/doc-guideline-card.style.ts b/docs/components/doc-guideline-card/doc-guideline-card.style.ts deleted file mode 100644 index 053f601b..00000000 --- a/docs/components/doc-guideline-card/doc-guideline-card.style.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { css } from "lit"; - -export default css` - :host { - box-sizing: border-box; - flex: 1; - align-self: stretch; - display: flex; - } - - :host *, - :host *::before, - :host *::after { - box-sizing: inherit; - } - - .guideline-card { - height: 100%; - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 1rem; - border-radius: 8px; - padding: 16px 16px 8px; - line-height: 24px; - font-size: var(--vp-custom-block-font-size); - border-color: var(--vp-custom-block-info-border); - color: var(--vp-custom-block-info-text); - background-color: var(--vp-custom-block-info-bg); - } - - .image { - margin: auto; - } - .image img { - width: 100%; - } - - .badge { - display: inline-block; - margin-left: 2px; - border: 1px solid transparent; - height: 26px; - border-radius: 12px; - padding: 0 10px; - font-size: 12px; - font-weight: 500; - transform: translateY(10px); - } - - .do { - background-color: var(--tap-palette-green-400); - color: #ffffff; - } - .dont { - background-color: var(--tap-palette-red-400); - color: #ffffff; - } - .caution { - background-color: var(--tap-palette-yellow-400); - color: #000000; - } - - @media (max-width: 1100px) { - :host { - width: 100%; - align-items: stretch; - } - .guideline-card { - width: 100%; - } - } -`; diff --git a/docs/components/doc-guideline-card/doc-guideline-card.ts b/docs/components/doc-guideline-card/doc-guideline-card.ts deleted file mode 100644 index 99946284..00000000 --- a/docs/components/doc-guideline-card/doc-guideline-card.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { html, LitElement, nothing } from "lit"; -import { property } from "lit/decorators.js"; - -export class GuidelineCard extends LitElement { - @property({ attribute: "image-url" }) - public imageUrl? = ""; - - @property() - public variant?: "do" | "dont" | "caution"; - - private _renderBadge() { - const badgeConfig = { - do: { - title: "Do", - class: "do", - }, - dont: { - title: "Don't", - class: "dont", - }, - caution: { - title: "Caution", - class: "caution", - }, - }; - - if (this.variant && Object.keys(badgeConfig).includes(this.variant)) { - const selectedBadge = badgeConfig[this.variant]; - - return html`
- ${selectedBadge.title} -
`; - } - - return nothing; - } - - protected override render() { - return html` -
-
- -
- ${this._renderBadge()} -
- -
-
- `; - } -} diff --git a/docs/components/doc-guideline-card/index.ts b/docs/components/doc-guideline-card/index.ts deleted file mode 100644 index b26ce9b9..00000000 --- a/docs/components/doc-guideline-card/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { customElement } from "lit/decorators.js"; -import { GuidelineCard } from "./doc-guideline-card"; -import styles from "./doc-guideline-card.style"; - -@customElement("doc-guideline-card") -export class DocGuidelineCard extends GuidelineCard { - public static override readonly styles = [styles]; -} - -declare global { - interface HTMLElementTagNameMap { - "doc-guideline-card": DocGuidelineCard; - } -} diff --git a/docs/components/doc-guideline-section/doc-guideline-section.style.ts b/docs/components/doc-guideline-section/doc-guideline-section.style.ts deleted file mode 100644 index 3aa7bf74..00000000 --- a/docs/components/doc-guideline-section/doc-guideline-section.style.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { css } from "lit"; - -export default css` - :host { - box-sizing: border-box; - } - - :host *, - :host *::before, - :host *::after { - box-sizing: inherit; - } - - .guideline-section { - padding: 20px 0; - min-height: 40vh; - display: flex; - align-items: center; - justify-content: space-between; - gap: 2rem; - } - - .guideline-section.reverse { - flex-direction: row-reverse; - } - - .image { - max-width: 30vw; - min-width: 30vw; - } - - .image img { - width: 100%; - } - - @media (max-width: 1100px) { - .guideline-section { - flex-direction: column; - align-items: flex-start; - } - - .guideline-section.reverse { - flex-direction: column-reverse; - } - - .image { - max-width: unset; - margin: auto; - } - } -`; diff --git a/docs/components/doc-guideline-section/doc-guideline-section.ts b/docs/components/doc-guideline-section/doc-guideline-section.ts deleted file mode 100644 index b86d3466..00000000 --- a/docs/components/doc-guideline-section/doc-guideline-section.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { html, LitElement } from "lit"; -import { property } from "lit/decorators.js"; - -export class GuidelineSection extends LitElement { - @property({ type: Boolean }) - public reverse = false; - - protected override render() { - return html` -
-
- -
-
- -
-
- `; - } -} diff --git a/docs/components/doc-guideline-section/index.ts b/docs/components/doc-guideline-section/index.ts deleted file mode 100644 index 18d817c6..00000000 --- a/docs/components/doc-guideline-section/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { customElement } from "lit/decorators.js"; -import { GuidelineSection } from "./doc-guideline-section.js"; -import styles from "./doc-guideline-section.style.js"; - -@customElement("doc-guideline-section") -export class DocGuidelineSection extends GuidelineSection { - public static override readonly styles = [styles]; -} - -declare global { - interface HTMLElementTagNameMap { - "doc-guideline-section": DocGuidelineSection; - } -} diff --git a/docs/components/doc-row/doc-row.style.ts b/docs/components/doc-row/doc-row.style.ts deleted file mode 100644 index 0008d0be..00000000 --- a/docs/components/doc-row/doc-row.style.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { css } from "lit"; - -export default css` - :host { - box-sizing: border-box; - } - - :host *, - :host *::before, - :host *::after { - box-sizing: inherit; - } - - .row { - display: flex; - align-items: stretch; - justify-content: space-between; - gap: 2rem; - margin-bottom: 40px; - } - - @media (max-width: 1100px) { - .row { - flex-direction: column; - align-items: flex-start; - } - } -`; diff --git a/docs/components/doc-row/doc-row.ts b/docs/components/doc-row/doc-row.ts deleted file mode 100644 index 852f37fd..00000000 --- a/docs/components/doc-row/doc-row.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { html, LitElement } from "lit"; - -export class Row extends LitElement { - protected override render() { - return html` -
- -
- `; - } -} diff --git a/docs/components/doc-row/index.ts b/docs/components/doc-row/index.ts deleted file mode 100644 index 6bf0dd23..00000000 --- a/docs/components/doc-row/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { customElement } from "lit/decorators.js"; -import { Row } from "./doc-row"; -import styles from "./doc-row.style"; - -@customElement("doc-row") -export class DocRow extends Row { - public static override readonly styles = [styles]; -} - -declare global { - interface HTMLElementTagNameMap { - "doc-row": DocRow; - } -} diff --git a/docs/components/index.ts b/docs/components/index.ts deleted file mode 100644 index 8e2893b0..00000000 --- a/docs/components/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import "./doc-guideline-card"; -import "./doc-guideline-section/index.js"; -import "./doc-row"; diff --git a/docs/design/components/bottom-sheet.md b/docs/design/components/bottom-sheet.md deleted file mode 100644 index 0c2a8dfe..00000000 --- a/docs/design/components/bottom-sheet.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -aside: false ---- - -# Bottom Sheet - - - - - -### Anatomy - -Every Bottomsheet consist of 3 sections - -- Title -- Content -- Action Bar - - -Each part has a specific role and should contain content that serves that role. -The bottom sheet should be draggable to expand and reveal more details or actions when needed, with an option to collapse it again. - - - - - - - - - -#### Title - -The title section defines the purpose of the bottom sheet. It may include elements such as an artwork, title, and description. Additionally, it can feature leading and trailing icons, often used for navigation actions like "back" or "close." - -- Use Handler when you expect users to interact more deeply with the content, such as scrolling, expanding, or manipulating the sheet. -- For more static interactions where you want users to focus on reading information or completing an action without needing to manipulate the interface it’s ok to not using handler. - - - - - - - - - -#### Content - -The content section mostly consist of description texts, inputs, and selections - - - - - - - - - -#### Action Bar - -This section is consist of button elements that can be vary from single button to multiple vertical and horizontal buttons - - - - - - - - - - - -### Types of BottomSheet -All the bottomsheets are in this 2 category - -- **Selective**: Allows users to select one or multiple options to proceed. -- **Data-Based**: Requires users to input data. - - - - - - - - - - - -### Spec - -- Width: Full Width of the screen -- Background Color: `color-surface-primary` -- Top left radius: `radius-20px` -- Top right radius: `radius-20px` -- Direction: Vertical -- Item spacing: `spacing-16px` -- Padding Top: `spacing-16px` -- Padding Bottom: `spacing-32px` - - - - - - - - - -bottomsheet guideline - - -Avoid using both handler and close icon at the same time - - - - - - -bottomsheet guideline - -On the map, the bottom sheet should only cover a portion of the screen, such as the lower third, allowing the map to remain visible. - - - - - - - - - - - -bottomsheet guideline - -Bottom sheets that require confirmation from users should include an action bar. - - - - - -bottomsheet guideline - -Selective bottom sheets with a single option do not require an action bar. - - - - - - - - - - - -Avoid stacking two layers of bottom sheets or modals on top of each other. If necessary, ensure a close option is provided for the previous layer. - - - -Avoid stacking two layers of bottom sheets or modals on top of each other. - -Make sure that both bottom sheets have easily accessible and visible close buttons. The user should be able to quickly exit the top sheet first, and then the bottom sheet. - -If it's crucial that the user interacts with one sheet before the other, make the second sheet modal. make sure the second option has a close option - - - diff --git a/docs/design/guidelines.md b/docs/design/guidelines.md deleted file mode 100644 index 2d890091..00000000 --- a/docs/design/guidelines.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -prev: false -next: false -outline: 'deep' ---- - -# Design System Guidelines - -... diff --git a/docs/dev/components/[component].md b/docs/dev/components/[component].md deleted file mode 100644 index 71fd53c0..00000000 --- a/docs/dev/components/[component].md +++ /dev/null @@ -1,48 +0,0 @@ ---- -prev: false -next: false -outline: 'deep' ---- - - - - diff --git a/docs/dev/components/[component].paths.ts b/docs/dev/components/[component].paths.ts deleted file mode 100644 index 2ce6bfe4..00000000 --- a/docs/dev/components/[component].paths.ts +++ /dev/null @@ -1,149 +0,0 @@ -import type { - CustomElement, - Package, - PropertyLike, -} from "custom-elements-manifest"; -import fs from "node:fs"; - -export default { - paths() { - const file = fs.readFileSync("dist/custom-elements.json"); - const manifest = JSON.parse(file.toString()) as Package; - - return manifest.modules - .filter(module => !!module.declarations?.length) - .map(module => { - if (!module.exports) - throw new Error(`Module has no export: ${module.path}`); - - const component = module.declarations?.[0] as CustomElement; - - if ( - !component || - !component.tagName || - !component.tagName.startsWith("tap") - ) { - return; - } - - let content = ""; - - content += `# ${getPageTitle(component.tagName)}\n`; - - if (component.summary) { - content += `${component.summary}\n`; - } - - // content += getInstallSection(component.tagName) - - if (component.description) { - content += `\n${component.description}\n`; - } - - // TODO: remove after fixing cemnama - if (!["tap-modal", "tap-pinwheel-group"].includes(component.tagName)) - content += `\n`; - - if (!!component.members?.length) { - content += "### Properties\n"; - content += "| Name | Type | Default | Description |\n"; - content += "| ---- | ---- | ---- | ---- |\n"; - - (component.members as PropertyLike[]).forEach(member => { - content += `| \`${member.name}\` | ${member.type?.text - ?.split("|") - .map(t => `\`${t.trim()}\``) - .join( - " \\| ", - )} | \`${member.default}\` | ${member.description} |\n`; - }); - } - - if (!!component.slots?.length) { - content += "### Slots\n"; - content += "| Name | Description |\n"; - content += "| ---- | ---- |\n"; - - component.slots.forEach(slot => { - const name = !!slot.name ? slot.name : "default"; - content += `| \`${name}\` | ${slot.description} |\n`; - }); - } - - if (!!component.cssParts?.length) { - content += "### CSS Parts\n"; - content += "| Name | Description |\n"; - content += "| ---- | ---- |\n"; - - component.cssParts.forEach(cssPart => { - content += `| \`${cssPart.name}\` | ${cssPart.description} |\n`; - }); - content += `\n > Check [this link](/dev/references/css-parts.html#${component.tagName}) to learn how to use CSS parts for ${getPageTitle(component.tagName)}.\n`; - } - - if (!!component.cssProperties?.length) { - content += "### CSS Properties\n"; - content += "| Token | Default | Description\n"; - content += "| ---- | ---- | ---- |\n"; - - component.cssProperties.forEach(cssProperty => { - content += `| \`${cssProperty.name}\` | \`${cssProperty.default || "-"}\` | ${cssProperty.description || "-"} |\n`; - }); - } - - if (!!component.events?.length) { - content += "### Events\n"; - content += "| Name | Description\n"; - content += "| ----- | ------- |\n"; - component.events.forEach(event => { - content += `| \`${event.name}\` | ${event.description || "-"} |\n`; - }); - } - - return { - params: { - component: component.tagName, - }, - content, - }; - }); - }, -}; - -// function getInstallSection(name: string) { -// return ` -// ### Installation - -// If you are using node and NPM, you can install this component using npm: - -// ::: code-group - -// \`\`\`bash [npm] -// npm install @tapsioss/web-components -// \`\`\` - -// \`\`\`bash [yarn] -// yarn add @tapsioss/web-components -// \`\`\` - -// \`\`\`bash [pnpm] -// pnpm install @tapsioss/web-components -// \`\`\` - -// ::: - -// Then import this component into your project by using a bare module specifier: - -// \`\`\`js -// import '@tapsioss/web-components/${name}/${name}.js'; -// \`\`\` -// `; -// } - -const getPageTitle = (componentName: string): string => { - const result = componentName?.replace(/^-*(.)|-+(.)/g, (_, c, d) => - c ? c.toUpperCase() : " " + d.toUpperCase(), - ); - const title = result?.charAt(0)?.toUpperCase() + result?.slice(1); - return `${title} Component`.replace("Tap ", ""); -}; diff --git a/docs/dev/components/elements-config.json b/docs/dev/components/elements-config.json deleted file mode 100644 index 6dd4a695..00000000 --- a/docs/dev/components/elements-config.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "tap-avatar": { - "propertyDefaultValues": { - "image": "https://picsum.photos/200", - "label": "a beautiful avatar" - }, - "slotKnobs": { - "default": { - "simple": "" - } - } - }, - "tap-stepper": { - "propertyDefaultValues": { - "unit": "دقیقه", - "value": 10, - "step": 5, - "min": 5, - "max": 60 - } - }, - "tap-nps": { - "propertyDefaultValues": { - "value": 5, - "min": 0, - "max": 10 - } - }, - "tap-skeleton": { - "propertyDefaultValues": { - "width": "100px", - "height": "100px", - "variant": "circle" - } - }, - "tap-badge": { - "propertyDefaultValues": { - "value": "درخواست", - "color": "info", - "priority": "low", - "variant": "pill" - }, - "slotKnobs": {} - }, - "tap-tooltip": { - "propertyDefaultValues": { - "width": "100px" - }, - "slotKnobs": { - "target-element": { - "simple": "
" - }, - "label": { - "simple": "متن" - } - } - }, - "tap-icon-button": { - "slotKnobs": { - "default": { - "simple": "" - } - } - }, - "tap-notice": { - "propertyDefaultValues": { - "variant": "error", - "noticeTitle": "عنوان را وارد کنید" - }, - "slotKnobs": { - "default": { - "simple": "متن شما در اینجا قرار می‌گیرد" - }, - "actions": { - "empty": "", - "one button": "
عنوان دکمه
", - "two buttons": "
عنوان دکمهعنوان دکمه
" - }, - "artwork": { - "sample": "" - } - } - }, - "tap-empty-state": { - "propertyDefaultValues": { - "title": "عدم دسترسی به موقعیت مکانی", - "description": "با فعال کردن گزینه موقعیت مکانی (لوکیشن)، مبدأ و مقصد خود را راحت‌تر ثبت کنید" - }, - "slotKnobs": { - "icon": { - "simple icon": "" - }, - "action": { - "button": "روشن کن" - } - } - }, - "tap-badge-wrapper": { - "slotKnobs": { - "default": { - "Button": "عنوان دکمه", - "Icon Button": "" - }, - "badge": { - "Pill Badge": "", - "Dot Badge": "" - } - } - }, - "tap-row": { - "slotKnobs": { - "leading": { - "checkbox": "", - "radio": "", - "avatar": "" - }, - "trailing": { - "button": "پرداخت", - "badge": "", - "icon": "", - "price": "

۱۵۷٬۰۰۰ تومان

" - }, - "content": { - "address": "

انتخاب آدرس

", - "simple text": "

متن ساده

" - } - } - }, - "tap-route": { - "slotKnobs": { - "content": { - "standard": "
\n

عنوان را وارد کنید

\n

توضیحات لیست

\n
", - "reverse": "
\n

عنوان را وارد کنید

\n

توضیحات لیست

\n
", - "address": "

سعادت\u200Cآباد، بلوار بهزاد

" - } - } - }, - "tap-segmented-button": { - "propertyDefaultValues": { - "selected": true - }, - "slotKnobs": { - "default": { - "simple": "عنوان" - } - } - }, - "tap-segmented-button-group": { - "slotKnobs": { - "default": { - "simple": "مسافر\n راننده" - } - } - }, - "tap-button": { - "slotKnobs": { - "default": { - "text": "ثبت مبدأ" - } - } - }, - "tap-pinwheel": { - "propertyDefaultValues": { - "items": [ - "فروردین", - "اردیبهشت", - "خرداد", - "تیر", - "مرداد", - "شهریور", - "مهر", - "آبان", - "آذر", - "دی", - "بهمن", - "اسفند" - ] - } - }, - "tap-bottom-navigation-item": { - "slotKnobs": { - "icon": { - "profile": "" - }, - "default": { - "profile": "پروفایل" - } - } - }, - "tap-bottom-navigation": { - "slotKnobs": { - "default": { - "sample": "پروفایلباشگاه مشتریان" - } - } - }, - "tap-banner": { - "propertyDefaultValues": { - "heading": "با ۱۶۳۰، تلفنی تپسی بگیر", - "description": "با شماره‌گیری ۱۶۳۰، بدون نیاز به اینرتنت، درخواست سفرتان را ثبت کنید", - "image": "https://able-media.tapsi.cab/statics/EA3ORTXURYJ02LA4XF.jpg", - "background-color": "#ff5722", - "textColor": "#fff" - } - }, - "tap-text-field": { - "slotKnobs": { - "leading-icon": { - "empty": "", - "icon": "" - }, - "trailing": { - "empty": "", - "suffix": "

تومان", - "icon": "", - "button": "" - } - }, - "propertyDefaultValues": { - "label": "نام گیرنده", - "supporting-text": "لطفا نام و نام خانوادگی کامل خود را وارد نمایید" - } - }, - "tap-text-area": { - "slotKnobs": { - "leading-icon": { - "empty": "", - "icon": "" - }, - "trailing": { - "empty": "", - "suffix": "

تومان", - "icon": "", - "button": "" - } - }, - "propertyDefaultValues": { - "label": "توضیحات سفارش (اختیاری)", - "placeholder": "مثلا بسته با احتیاط حمل شود...", - "supporting-text": "در صورت پر بودن این فیلد، متن آن برای راننده نمایش داده خواهد شد" - } - }, - "tap-chip": { - "slotKnobs": { - "default": { - "simple": "عنوان" - } - } - }, - "tap-chip-group": { - "slotKnobs": { - "default": { - "simple": "مسافر\nراننده" - } - } - }, - "tap-bottom-sheet": { - "propertyDefaultValues": { - "title": "عنوان" - }, - "slotKnobs": { - "bottom-sheet-body": { - "security": "

\n
\n \n \n \n \n
\n

سفرتان را به اشتراک بگذارید

\n

اشتراک سفر با دوستان و آشنایان

\n
\n
\n
\n \n
\n \n \n \n \n
\n

در شرایط خطرناکی هستید؟

\n

برقراری تماس با تیم امنیت تپسی

\n
\n
\n
\n
", - "tip": "
\n
\n

مبلغی که شما جهت قدردانی به سفیر می پردازید، بدون کسر کارمزد برای سفیر واریز می شود.

\n

۱۲,۰۰۰ تومان

\n \n

۱۸,۰۰۰ تومان

\n \n

۲۵,۰۰۰ تومان

\n \n

مبلغ دلخواه

\n
\n
" - } - } - } -} diff --git a/docs/dev/getting-started.md b/docs/dev/getting-started.md deleted file mode 100644 index df54fb7a..00000000 --- a/docs/dev/getting-started.md +++ /dev/null @@ -1,200 +0,0 @@ ---- -outline: 'deep' ---- - -# Getting Started - -## Installation - -### Web - -Implementation of the Tapsi icon library for web applications. - -::: code-group -```bash [npm] -npm install @tapsi-oss/design-system/web-components -``` - -```bash [yarn] -yarn add @tapsi-oss/design-system/web-components -``` - -```bash [pnpm] -pnpm install @tapsi-oss/design-system/web-components -``` -::: - -::: info -Please note that [lit](https://www.npmjs.com/package/lit) is a peer dependency, meaning you should ensure they are -installed before installing Tapsi design system in web components. -::: - -### React - -Implementation of the Tapsi icon library in React. - -::: code-group -```bash [npm] -npm install @tapsi-oss/design-system/react -``` - -```bash [yarn] -yarn add @tapsi-oss/design-system/react -``` - -```bash [pnpm] -pnpm install @tapsi-oss/design-system/react -``` -::: - - -::: info -Please note that [react](https://www.npmjs.com/package/react) and [react-dom](https://www.npmjs.com/package/react-dom) -are peer dependencies, meaning you should ensure they are installed before installing Tapsi design system in React. -::: - -## Usage - -Many components have properties that can be set using attributes. For example, buttons accept a `size` attribute that -maps to the size property which dictates the button’s size. - -```html -Click me! -``` - -Some properties are boolean, so they only have true\/false values. To activate a boolean property, add the -corresponding attribute without a value. - -```html -Click me! -``` - -In rare cases, a property may require an array, an object, or a function. For example, to customize the pinwheel’s -list of preset items, you set the `items` property to an array of `stirng`. This can be done with JavaScript. - -```html - - - -``` - -Refer to a component’s documentation for a complete list of its properties. - - -## Events - -You can listen for standard events such as `click`, `mouseover`, etc. as you normally would. However, it’s important to -note that many events emitted within a component’s shadow root will be retargeted to the host element. This may result -in, for example, multiple `click` handlers executing even if the user clicks just once. Furthermore, `event.target` will -point to the host element, making things even more confusing. - -As a result, you should almost always listen for custom events instead. For example, instead of listening to click to -determine when an `` gets toggled, listen to `tap-change`. - -```html -Check me - - -``` - -## Slots - -Many components use slots to accept content inside of them. The most common slot is the **default** slot, which includes any -content inside the component that doesn't have a `slot` attribute. - -For example, a button’s default slot is used to populate its label. - -```html -Click me! -``` - -Some components also have **named** slots. A named slot can be populated by adding a child element with the appropriate -`slot` attribute. Notice how the icons below has the `slot="icon"` and `slot="active-icon"` attribute? This tells the -component to place the icon into its prefix slot. - -```html - - Profile - - - -``` - -:::info -The location of a named slot doesn't matter. You can put it anywhere inside the component and the browser will move it -to the right place automatically! -::: - -Refer to a component’s documentation for a complete list of available slots. - -## Visual Customization - - -Tapsi components can be customized at a high level through design tokens. This gives you control over theme colors and -general styling. For more advanced customizations, you can make use of CSS parts and custom properties to target -individual components. - -### Design Tokens - -The style of the components in this library are using [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/var). -Each property has a **custom property name** and a **declaration value** with this format: - -```css -{ - property-name: var(--custom-property-name, var(--declaration-value)); /* [!code highlight] */ -} -``` - - -The declaration value is Tapsi design system tokens and the custom property name can be set by component users. You need -to set value for the custom property name is a global CSS file in your project. The list of these tokens are available -in our [CSS Tokens Documentation](/dev/references/components-tokens). Additionally, each component has it's own tokens in the "CSS Properties" section in -their documentation. - -### CSS Parts - - -Elements in this library components has the `part` attribute. This attribute let the users be able to modify the styles -of a component from outside using the [`::part` CSS pseudo-element](https://developer.mozilla.org/en-US/docs/Web/CSS/::part). -All you need to do is to visit our [CSS part documentation](/dev/references/css-parts) and modify the style of the component. Also, the documentation -of each component contains these CSS parts in "CSS Parts" section. - -Here is an example of modifying the style of the `modal` component using `part`: - -```css -tap-modal::part(overlay) { /* [!code highlight] */ - backdrop-filter: blur(14px); -} - - -tap-modal::part(dialog) { /* [!code highlight] */ - background-color: #1f1f1f; - color: #ffffff; -} -``` - - - - - diff --git a/docs/dev/references/[reference].md b/docs/dev/references/[reference].md deleted file mode 100644 index d518cd46..00000000 --- a/docs/dev/references/[reference].md +++ /dev/null @@ -1,8 +0,0 @@ ---- -outline: 'deep' ---- - - diff --git a/docs/dev/references/[reference].paths.ts b/docs/dev/references/[reference].paths.ts deleted file mode 100644 index 902f4bd1..00000000 --- a/docs/dev/references/[reference].paths.ts +++ /dev/null @@ -1,42 +0,0 @@ -import getColorTokensReferenceContent from "../../utils/getColorTokensReferenceContent"; -import getComponentsTokensReferenceContent from "../../utils/getComponentsTokensReferenceContent"; -import getCssPartsReferenceContent from "../../utils/getCssPartsReferenceContent"; -import getRadiusTokensReferenceContent from "../../utils/getRadiusTokensReferenceContent"; -import getSpacingTokensReferenceContent from "../../utils/getSpacingTokensReferenceContent"; -import getStrokeTokensReferenceContent from "../../utils/getStrokeTokensReferenceContent"; -import getTypographyTokensReferenceContent from "../../utils/getTypographyTokensReferenceContent"; - -export default { - paths() { - return [ - { - params: { reference: "css-parts" }, - content: getCssPartsReferenceContent(), - }, - { - params: { reference: "components-tokens" }, - content: getComponentsTokensReferenceContent(), - }, - { - params: { reference: "color-tokens" }, - content: getColorTokensReferenceContent(), - }, - { - params: { reference: "radius-tokens" }, - content: getRadiusTokensReferenceContent(), - }, - { - params: { reference: "spacing-tokens" }, - content: getSpacingTokensReferenceContent(), - }, - { - params: { reference: "stroke-tokens" }, - content: getStrokeTokensReferenceContent(), - }, - { - params: { reference: "typography-tokens" }, - content: getTypographyTokensReferenceContent(), - }, - ]; - }, -}; diff --git a/docs/index.md b/docs/index.md index a539358e..70eab417 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,20 +1,22 @@ ---- -layout: home +# Getting Started -hero: - name: TDS - text: Tapsi Design System +## Installation - actions: - - theme: brand - text: Getting Started - link: /dev/getting-started - - theme: alt - text: Explore Components - link: /dev/components/tap-avatar - - theme: alt - text: Design System Guidelines - link: /design/guidelines +::: code-group ---- +```bash [pnpm] +pnpm install @tapsioss/web-components # installing Tapsi components +pnpm install @tapsioss/theme # installing the Tapsi theme +``` +```bash [npm] +npm install @tapsioss/web-components # installing Tapsi components +npm install @tapsioss/theme # installing the Tapsi theme +``` + +```bash [yarn] +yarn add @tapsioss/web-components # installing Tapsi components +yarn add @tapsioss/theme # installing the Tapsi theme +``` + +::: diff --git a/docs/internals/utils/tabulateData.ts b/docs/internals/utils/tabulateData.ts new file mode 100644 index 00000000..ad8e8ebc --- /dev/null +++ b/docs/internals/utils/tabulateData.ts @@ -0,0 +1,13 @@ +export const tabulateData = (headers: string[], data: string[][]): string => { + if (headers.length === 0) return ""; + if (data.length === 0 || data[0]!.length === 0) return ""; + + const headersMarkdown = `| ${headers.join(" | ")} |`; + const separator = `| ${headers.map(() => "---").join(" | ")} |`; + + const rowsMarkdown = data.map(row => `| ${(row).join(" | ")} |`).join("\n"); + + return `${headersMarkdown}\n${separator}\n${rowsMarkdown}`; +}; + +export const codify = (inputString: string) => `\`${inputString}\``; diff --git a/docs/sidebar.ts b/docs/sidebar.ts new file mode 100644 index 00000000..9f54f387 --- /dev/null +++ b/docs/sidebar.ts @@ -0,0 +1,27 @@ +import fs from "fs"; +import path from "node:path"; +import { type DefaultTheme } from "vitepress"; +import { type Metadata } from "../internals/doc-helpers/types"; +import { getFileMeta } from "../scripts/utils"; + +const { dirname } = getFileMeta(import.meta.url); + +const docsVitepressDir = path.join(dirname, ".vitepress"); +const docsDistDir = path.join(docsVitepressDir, "dist"); +const metadataFile = path.join(docsDistDir, "components-metadata.json"); + +const getComponentsSidebarItems = (): DefaultTheme.SidebarItem[] => + (JSON.parse(fs.readFileSync(metadataFile).toString("utf-8")) as Metadata) + .sidebarItems; + +const getComponentsSidebar = (): DefaultTheme.Sidebar => { + return [ + { + text: "Components", + collapsed: false, + items: getComponentsSidebarItems(), + }, + ]; +}; + +export default getComponentsSidebar(); diff --git a/docs/.vitepress/socialLinks.ts b/docs/social-links.ts similarity index 80% rename from docs/.vitepress/socialLinks.ts rename to docs/social-links.ts index e30c775e..4a68fdbc 100644 --- a/docs/.vitepress/socialLinks.ts +++ b/docs/social-links.ts @@ -1,4 +1,4 @@ -import { DefaultTheme } from "vitepress"; +import { type DefaultTheme } from "vitepress"; export default [ { icon: "github", link: "https://github.com/Tap30/web-components" }, diff --git a/docs/utils/getColorTokensReferenceContent.ts b/docs/utils/getColorTokensReferenceContent.ts deleted file mode 100644 index 00f48c19..00000000 --- a/docs/utils/getColorTokensReferenceContent.ts +++ /dev/null @@ -1,56 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -import { fileURLToPath } from "node:url"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const workspaceDir = path.resolve(__dirname, "../.."); - -const colorsFile = fs.readFileSync( - path.resolve(workspaceDir, "packages/theme/src/tokens/colors.css"), -); - -const paletteFile = fs.readFileSync( - path.resolve(workspaceDir, "packages/theme/src/tokens/palette.css"), -); - -const getColorTokensReferenceContent = () => { - let content = ""; - - content += "# Color Tokens\n\n"; - content += `## Palette\n`; - content += `| Token | Value | Example |\n`; - content += `| ----- | ----- | ----- |\n`; - - paletteFile - .toString() - .split("\n") - .forEach(line => { - if (line.trim().startsWith("--tap-")) { - const x = line.replace(":", ";").trim().split(";"); - - content += `| \`${x[0]}\` | \`${x[1]}\` |
\n`; - } - }); - - colorsFile - .toString() - .split("\n") - .forEach(line => { - if (line.trim().startsWith("/*") && !line.includes("TODO")) { - const subtitle = line.replace("/*", "").replace("*/", "").trim(); - - content += `## ${subtitle}\n`; - content += `| Token | Value | Preview |\n`; - content += `| ----- | ----- | ----- |\n`; - } else if (line.trim().startsWith("--tap-")) { - const x = line.replace(":", ";").trim().split(";"); - - content += `| \`${x[0]}\` | \`${x[1]}\` |
\n`; - } - }); - return content; -}; - -export default getColorTokensReferenceContent; diff --git a/docs/utils/getComponentsTokensReferenceContent.ts b/docs/utils/getComponentsTokensReferenceContent.ts deleted file mode 100644 index ef5479b6..00000000 --- a/docs/utils/getComponentsTokensReferenceContent.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { CustomElement, Package } from "custom-elements-manifest"; -import * as fs from "node:fs"; - -const file = fs.readFileSync("dist/custom-elements.json"); -const manifest = JSON.parse(file.toString()) as Package; - -const getComponentsTokensReferenceContent = () => { - let content = "# Components Tokens\n"; - - manifest.modules - .filter(module => !!module.declarations?.length) - .forEach(module => { - if (!module.exports) - throw new Error(`Module has no export: ${module.path}`); - - const component = module.declarations?.[0] as CustomElement; - - if ( - !component || - !component.tagName || - !component.tagName.startsWith("tap") - ) { - return; - } - - if (component.cssProperties?.length) { - content += `\n## \`${component.tagName}\`\n\n`; - content += "| Name | Default Value | Description |\n"; - content += "| ---- | ---- | ---- |\n"; - - component.cssProperties.forEach(cssPart => { - content += `| \`${cssPart.name}\` | \`${cssPart.default}\`| ${cssPart.description || "-"} |\n`; - }); - } - }); - return content; -}; - -export default getComponentsTokensReferenceContent; diff --git a/docs/utils/getCssPartsReferenceContent.ts b/docs/utils/getCssPartsReferenceContent.ts deleted file mode 100644 index ed6755e8..00000000 --- a/docs/utils/getCssPartsReferenceContent.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { CustomElement, Package } from "custom-elements-manifest"; -import fs from "node:fs"; - -const file = fs.readFileSync("dist/custom-elements.json"); -const manifest = JSON.parse(file.toString()) as Package; - -const getCssPartsReferenceContent = () => { - let content = "# CSS Parts API Reference\n"; - - manifest.modules - .filter(module => !!module.declarations?.length) - .forEach(module => { - if (!module.exports) - throw new Error(`Module has no export: ${module.path}`); - - const component = module.declarations?.[0] as CustomElement; - - if ( - !component || - !component.tagName || - !component.tagName.startsWith("tap") - ) { - return; - } - - if (component.cssParts?.length) { - content += `\n## \`${component.tagName}\`\n\n`; - content += "| Name | Description |\n"; - content += "| ---- | ---- |\n"; - - component.cssParts.forEach(cssPart => { - content += `| \`${cssPart.name}\` | ${cssPart.description} |\n`; - }); - - content += "\n\n```css\n"; - - component.cssParts.forEach(cssPart => { - content += `${component.tagName}::part(${cssPart.name}) { /* ... */ }\n`; - }); - - content += "```"; - } - }); - return content; -}; - -export default getCssPartsReferenceContent; diff --git a/docs/utils/getRadiusTokensReferenceContent.ts b/docs/utils/getRadiusTokensReferenceContent.ts deleted file mode 100644 index f6d7f6e7..00000000 --- a/docs/utils/getRadiusTokensReferenceContent.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -import { fileURLToPath } from "node:url"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const workspaceDir = path.resolve(__dirname, "../.."); - -const radiusFile = fs.readFileSync( - path.resolve(workspaceDir, "packages/theme/src/tokens/radius.css"), -); - -const getRadiusTokensReferenceContent = () => { - let content = ""; - - content += "# Radius Tokens\n\n"; - - content += `| Token | Value | Example |\n`; - content += `| ----- | ----- | ----- |\n`; - - radiusFile - .toString() - .split("\n") - .forEach(line => { - if (line.trim().startsWith("--tap-")) { - const x = line.replace(":", ";").trim().split(";"); - - content += `| \`${x[0]}\` | \`${x[1]}\` |
\n`; - } - }); - - return content; -}; - -export default getRadiusTokensReferenceContent; diff --git a/docs/utils/getSpacingTokensReferenceContent.ts b/docs/utils/getSpacingTokensReferenceContent.ts deleted file mode 100644 index 9bf3f9f1..00000000 --- a/docs/utils/getSpacingTokensReferenceContent.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -import { fileURLToPath } from "node:url"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const workspaceDir = path.resolve(__dirname, "../.."); - -const spacingFile = fs.readFileSync( - path.resolve(workspaceDir, "packages/theme/src/tokens/spacing.css"), -); - -const getSpacingTokensReferenceContent = () => { - let content = ""; - - content += "# Spacing Tokens\n\n"; - - content += `| Token | Value | Example |\n`; - content += `| ----- | ----- | ----- |\n`; - - spacingFile - .toString() - .split("\n") - .forEach(line => { - if (line.trim().startsWith("--tap-")) { - const x = line.replace(":", ";").trim().split(";"); - - content += `| \`${x[0]}\` | \`${x[1]}\` |
\n`; - } - }); - - return content; -}; - -export default getSpacingTokensReferenceContent; diff --git a/docs/utils/getStrokeTokensReferenceContent.ts b/docs/utils/getStrokeTokensReferenceContent.ts deleted file mode 100644 index b0cdaed4..00000000 --- a/docs/utils/getStrokeTokensReferenceContent.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -import { fileURLToPath } from "node:url"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const workspaceDir = path.resolve(__dirname, "../.."); - -const strokeFile = fs.readFileSync( - path.resolve(workspaceDir, "packages/theme/src/tokens/strokes.css"), -); - -const getStrokeTokensReferenceContent = () => { - let content = ""; - - content += "# Stroke Tokens\n\n"; - - content += `| Token | Value | Example |\n`; - content += `| ----- | ----- | ----- |\n`; - - strokeFile - .toString() - .split("\n") - .forEach(line => { - if (line.trim().startsWith("--tap-")) { - const x = line.replace(":", ";").trim().split(";"); - - content += `| \`${x[0]}\` | \`${x[1]}\` |
\n`; - } - }); - - return content; -}; - -export default getStrokeTokensReferenceContent; diff --git a/docs/utils/getTypographyTokensReferenceContent.ts b/docs/utils/getTypographyTokensReferenceContent.ts deleted file mode 100644 index a96b98f5..00000000 --- a/docs/utils/getTypographyTokensReferenceContent.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -import { fileURLToPath } from "node:url"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const workspaceDir = path.resolve(__dirname, "../.."); - -const typographyFile = fs.readFileSync( - path.resolve(workspaceDir, "packages/theme/src/tokens/colors.css"), -); - -const getTypographyTokensReferenceContent = () => { - let content = ""; - - content += "# Typography Tokens Not Complete\n\n"; - - typographyFile - .toString() - .split("\n") - .forEach((line, index) => { - if (line.trim().startsWith("/*") && !line.includes("TODO") && index > 0) { - const subtitle = line.replace("/*", "").replace("*/", "").trim(); - - content += `## ${subtitle}\n`; - content += `| Token | Value | Preview (English) | Preview (Persian) |\n`; - content += `| ----- | ----- | ----- | ----- |\n`; - } else if (line.trim().startsWith("--tap-") && index > 0) { - const x = line.replace(":", ";").trim().split(";"); - - content += `| \`${x[0]}\` | \`${x[1]}\` | - | - |\n`; - } - }); - return content; -}; - -export default getTypographyTokensReferenceContent; diff --git a/internals/doc-helpers/schema.json b/internals/doc-helpers/schema.json index ee23a24a..63685b3e 100644 --- a/internals/doc-helpers/schema.json +++ b/internals/doc-helpers/schema.json @@ -39,14 +39,28 @@ "type": "object", "properties": { "name": { "type": "string" }, - "path": { "type": "string", "pattern": "^(\\/|(\\.\\.?\\/)+)?([a-zA-Z0-9-_]+\\/)*[a-zA-Z0-9-_]+\\.html$" }, + "path": { + "type": "string", + "pattern": "^(\\/|(\\.\\.?\\/)+)?([a-zA-Z0-9-_]+\\/)*[a-zA-Z0-9-_]+\\.html$" + }, "mutates": { "type": "array", "items": { "type": "object", "properties": { - "target": { "type": "string", "pattern": "^props#[a-zA-Z][\\w-]*$" }, - "value": { "type": ["string", "number", "boolean", "object", "null"] } + "target": { + "type": "string", + "pattern": "^props#[a-zA-Z][\\w-]*$" + }, + "value": { + "type": [ + "string", + "number", + "boolean", + "object", + "null" + ] + } }, "required": ["target", "value"] } diff --git a/internals/doc-helpers/types.ts b/internals/doc-helpers/types.ts new file mode 100644 index 00000000..ccb6b80e --- /dev/null +++ b/internals/doc-helpers/types.ts @@ -0,0 +1,76 @@ +import { type CustomElement } from "custom-elements-manifest"; +import { type DefaultTheme } from "vitepress"; + +export type HTMLTemplatePath = `${string}.html`; + +export type MutatorRefName = `#${string}` | `props#${string}`; + +export type Property = { + name: string; + defaultValue?: string | boolean | number; + mutators?: MutatorRefName[]; +}; + +export type SlotOption = { + name: string; + path: HTMLTemplatePath; + mutates?: Array<{ + target: `props#${string}`; + value: string | number | boolean | object | null; + }>; +}; + +export type DemoSlot = { + name: string; + options: SlotOption[]; +}; + +export type InteractiveDemo = { + requiredElements?: HTMLTemplatePath[]; + properties: Property[]; + slots: DemoSlot[]; +}; + +export type ComponentSlot = { + name: string; + description: string; +}; + +export type ComponentMember = { + type: { + text: string; + }; + description?: string; + name: string; + default?: string; + kind: string; +}; + +export type ComponentEvent = { + type?: { + text: string; + }; + description?: string; + name: string; +}; + +export type componentSuperclass = { + name: string; + module: string; +}; + +export type ImportPaths = { + webComponents?: string; +}; + +export type Component = CustomElement & { + kebabCaseName: string; + interactiveDemo?: InteractiveDemo; + importPaths: ImportPaths; + slotsEnumName?: string; +}; + +export type Metadata = { + components: Component[]; + sidebarItems: DefaultTheme.SidebarItem[]; +}; diff --git a/package.json b/package.json index 367189c4..6d368805 100644 --- a/package.json +++ b/package.json @@ -34,12 +34,12 @@ "docs:vitepress:dev": "vitepress dev docs", "docs:vitepress:build": "vitepress build docs", "docs:vitepress:preview": "vitepress preview docs", - "docs:custom-elements:dev": "shx cp ./dist/custom-elements.json ./docs/dev/components/custom-elements.json", - "docs:custom-elements:prod": "shx cp ./dist/custom-elements.json ./docs/.vitepress/dist/dev/components/custom-elements.json", + "docs:metadata:prod": "shx cp ./dist/components-metadata.json ./docs/.vitepress/dist/components-metadata.json", "docs:elements-config:prod": "shx cp ./docs/dev/components/elements-config.json ./docs/.vitepress/dist/dev/components/elements-config.json", - "docs:dev": "run-s analyze docs:custom-elements:dev docs:vitepress:dev", - "docs:build": "run-s analyze docs:vitepress:build docs:custom-elements:prod docs:elements-config:prod", + "docs:dev": "run-s analyze docs:gen:metadata docs:metadata:prod docs:vitepress:dev", + "docs:build": "run-s analyze docs:gen:metadata docs:metadata:prod docs:vitepress:build docs:metadata:prod", "docs:preview": "run-s docs:vitepress:preview", + "docs:gen:metadata": "tsx ./scripts/generate-docs-metadata.ts", "analyze": "tsx ./scripts/generate-cem.ts" }, "devDependencies": { diff --git a/packages/react-components/src/Textarea.ts b/packages/react-components/src/TextArea.ts similarity index 100% rename from packages/react-components/src/Textarea.ts rename to packages/react-components/src/TextArea.ts diff --git a/packages/web-components/src/bottom-navigation/index.ts b/packages/web-components/src/bottom-navigation/index.ts index 9d34ac86..d67b1b75 100644 --- a/packages/web-components/src/bottom-navigation/index.ts +++ b/packages/web-components/src/bottom-navigation/index.ts @@ -31,6 +31,9 @@ export { ItemSlots }; export class TapsiBottomNavigationItem extends BottomNavigationItem { public static override readonly styles = [itemStyles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: ( @@ -40,6 +43,9 @@ export class TapsiBottomNavigationItem extends BottomNavigationItem { options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: < K extends keyof TapsiBottomNavigationItemEventMap, >( @@ -78,6 +84,9 @@ interface TapsiBottomNavigationItemEventMap extends HTMLElementEventMap { export class TapsiBottomNavigation extends BottomNavigation { public static override readonly styles = [bottomNavigationStyles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: ( @@ -87,6 +96,9 @@ export class TapsiBottomNavigation extends BottomNavigation { options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: ( diff --git a/packages/web-components/src/bottom-sheet/index.ts b/packages/web-components/src/bottom-sheet/index.ts index e111f534..37ba9e33 100644 --- a/packages/web-components/src/bottom-sheet/index.ts +++ b/packages/web-components/src/bottom-sheet/index.ts @@ -19,6 +19,9 @@ export * from "./events"; * * @tag tapsi-bottom-sheet * + * + * @prop {boolean} [open=false] - + * Determines whether the bottom-sheet is open. * @prop {string} [heading-title=""] - * Sets the heading title in a declarative-way. * @prop {string} [heading-description=""] - @@ -95,12 +98,18 @@ export * from "./events"; export class TapsiBottomSheet extends BottomSheet { public static override readonly styles = [styles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: (this: TapsiBottomSheet, ev: TapsiBottomSheetEventMap[K]) => void, options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: (this: TapsiBottomSheet, ev: TapsiBottomSheetEventMap[K]) => void, diff --git a/packages/web-components/src/chip-group/index.ts b/packages/web-components/src/chip-group/index.ts index 748eccda..45ec9339 100644 --- a/packages/web-components/src/chip-group/index.ts +++ b/packages/web-components/src/chip-group/index.ts @@ -28,12 +28,18 @@ export * from "./events"; export class TapsiChipGroup extends ChipGroup { public static override readonly styles = [styles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: (this: TapsiChipGroup, ev: TapsiChipGroupEventMap[K]) => void, options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: (this: TapsiChipGroup, ev: TapsiChipGroupEventMap[K]) => void, diff --git a/packages/web-components/src/chip/index.ts b/packages/web-components/src/chip/index.ts index 3c200094..04aaf002 100644 --- a/packages/web-components/src/chip/index.ts +++ b/packages/web-components/src/chip/index.ts @@ -29,12 +29,18 @@ export { DeselectEvent, SelectEvent }; export class TapsiChip extends Chip { public static override readonly styles = [styles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: (this: TapsiChip, ev: TapsiChipEventMap[K]) => void, options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: (this: TapsiChip, ev: TapsiChipEventMap[K]) => void, diff --git a/packages/web-components/src/modal/index.ts b/packages/web-components/src/modal/index.ts index 7aab88b1..3dac51a1 100644 --- a/packages/web-components/src/modal/index.ts +++ b/packages/web-components/src/modal/index.ts @@ -19,19 +19,25 @@ export * from "./events"; * @slot imagery - The slot for imagery element. * @slot action-bar - The slot for actionbar element. * - * @fires {ShowEvent} show - Fires when the modal should be visible. - * @fires {HideEvent} hide - Fires when the modal should be hidden. + * @fires {ShowEvent} show - Fires when the modal should be visible. (cancelable) + * @fires {HideEvent} hide - Fires when the modal should be hidden. (cancelable) */ @customElement("tapsi-modal") export class TapsiModal extends Modal { public static override readonly styles = [styles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: (this: TapsiModal, ev: TapsiModalEventMap[K]) => void, options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: (this: TapsiModal, ev: TapsiModalEventMap[K]) => void, diff --git a/packages/web-components/src/notice/index.ts b/packages/web-components/src/notice/index.ts index 000ae69b..6b02f80f 100644 --- a/packages/web-components/src/notice/index.ts +++ b/packages/web-components/src/notice/index.ts @@ -46,13 +46,18 @@ export * from "./events"; @customElement("tapsi-notice") export class TapsiNotice extends Notice { public static override readonly styles = [styles]; - + /** + * @internal + */ declare addEventListener: ( type: K, listener: (this: TapsiNotice, ev: TapsiNoticeEventMap[K]) => void, options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: (this: TapsiNotice, ev: TapsiNoticeEventMap[K]) => void, diff --git a/packages/web-components/src/pinwheel-group/index.ts b/packages/web-components/src/pinwheel-group/index.ts index e9582b4b..6c11e214 100644 --- a/packages/web-components/src/pinwheel-group/index.ts +++ b/packages/web-components/src/pinwheel-group/index.ts @@ -18,7 +18,7 @@ export { Slots } from "./constants"; * The value of the currently selected items. * It's not an attribute and will only work in CSR. * - * @fires change - Fires when a pinwheel selected state changes. + * @fires {Event} change - Fires when a pinwheel selected state changes. */ @customElement("tapsi-pinwheel-group") export class TapsiPinwheelGroup extends PinwheelGroup { diff --git a/packages/web-components/src/pinwheel/index.ts b/packages/web-components/src/pinwheel/index.ts index 67d409b7..dd2e3ca7 100644 --- a/packages/web-components/src/pinwheel/index.ts +++ b/packages/web-components/src/pinwheel/index.ts @@ -46,7 +46,7 @@ export class TapsiPinwheelItem extends PinwheelItem { * * https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-labelledby * - * @fires change - Fires when the pinwheel selected state changes. + * @fires {Event} change - Fires when the pinwheel selected state changes. */ @customElement("tapsi-pinwheel") export class TapsiPinwheel extends Pinwheel { diff --git a/packages/web-components/src/segmented-view/index.ts b/packages/web-components/src/segmented-view/index.ts index 8199bab8..8f317012 100644 --- a/packages/web-components/src/segmented-view/index.ts +++ b/packages/web-components/src/segmented-view/index.ts @@ -37,6 +37,9 @@ export { ItemSlots }; export class TapsiSegmentedViewItem extends SegmentedViewItem { public static override readonly styles = [itemStyles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: ( @@ -46,6 +49,9 @@ export class TapsiSegmentedViewItem extends SegmentedViewItem { options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: ( @@ -77,12 +83,18 @@ interface TapsiSegmentedViewItemEventMap extends HTMLElementEventMap { export class TapsiSegmentedView extends SegmentedView { public static override readonly styles = [segmentedViewStyles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: (this: TapsiSegmentedView, ev: TapsiSegmentedViewMap[K]) => void, options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: (this: TapsiSegmentedView, ev: TapsiSegmentedViewMap[K]) => void, diff --git a/packages/web-components/src/snackbar/index.ts b/packages/web-components/src/snackbar/index.ts index f1cc742c..e207817c 100644 --- a/packages/web-components/src/snackbar/index.ts +++ b/packages/web-components/src/snackbar/index.ts @@ -21,8 +21,8 @@ export * from "./events"; * * @slot icon - The slot for icon when color is `inverse`. * - * @fires {ShowEvent} show - Fires when the snackbar should be visible. - * @fires {HideEvent} hide - Fires when the snackbar should be hidden. + * @fires {ShowEvent} show - Fires when the snackbar should be visible. (cancelable) + * @fires {HideEvent} hide - Fires when the snackbar should be hidden. (cancelable) * * @method show * @description - Opens the snackbar if it is not already open. @@ -36,12 +36,18 @@ export * from "./events"; export class TapsiSnackbar extends Snackbar { public static override readonly styles = [styles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: (this: TapsiSnackbar, ev: TapsiSnackbarEventMap[K]) => void, options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: (this: TapsiSnackbar, ev: TapsiSnackbarEventMap[K]) => void, diff --git a/packages/web-components/src/tooltip/index.ts b/packages/web-components/src/tooltip/index.ts index 84724d40..65e5128e 100644 --- a/packages/web-components/src/tooltip/index.ts +++ b/packages/web-components/src/tooltip/index.ts @@ -20,19 +20,25 @@ export * from "./events"; * @prop {boolean} [no-escape-deactivation=false] - Whether to hide tooltip on escape or not. * @prop {string} [anchor=""] - The id of the anchor element. * - * @fires {ShowEvent} show - Fires when the tooltip should be visible. - * @fires {HideEvent} hide - Fires when the tooltip should be hidden. + * @fires {ShowEvent} show - Fires when the tooltip should be visible. (cancelable) + * @fires {HideEvent} hide - Fires when the tooltip should be hidden. (cancelable) */ @customElement("tapsi-tooltip") export class TapsiTooltip extends Tooltip { public static override readonly styles = [styles]; + /** + * @internal + */ declare addEventListener: ( type: K, listener: (this: TapsiTooltip, ev: TapsiTooltipEventMap[K]) => void, options?: boolean | AddEventListenerOptions, ) => void; + /** + * @internal + */ declare removeEventListener: ( type: K, listener: (this: TapsiTooltip, ev: TapsiTooltipEventMap[K]) => void, diff --git a/scripts/generate-docs-metadata.ts b/scripts/generate-docs-metadata.ts new file mode 100644 index 00000000..3ca69ce4 --- /dev/null +++ b/scripts/generate-docs-metadata.ts @@ -0,0 +1,167 @@ +/* eslint-disable no-console */ +import { + type CustomElement, + type Declaration, + type Package, +} from "custom-elements-manifest"; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { type DefaultTheme } from "vitepress"; +import { + type Component, + type ImportPaths, +} from "../internals/doc-helpers/types"; +import { getFileMeta } from "./utils"; + +const { dirname } = getFileMeta(import.meta.url); +const workspaceDir = path.resolve(dirname, ".."); +const webComponentsSrcDir = path.join( + workspaceDir, + "packages/web-components/src", +); + +const distDir = path.join(workspaceDir, "dist"); +const metadataFile = path.join(distDir, "components-metadata.json"); +const cemFile = path.join(distDir, "custom-elements.json"); + +const cem = JSON.parse(fs.readFileSync(cemFile, "utf8")) as Package; + +const getKebabCaseComponentName = (component: Declaration) => { + if (!("tagName" in component) || !component.tagName) return null; + + const tagName = component.tagName; + + return tagName.replace("tapsi-", ""); +}; + +void (() => { + console.log("🧩 generating components metadata..."); + + const sidebarItemsMap: Record = {}; + const components: Component[] = []; + + const filteredModules = cem.modules.filter(module => { + const hasDeclarations = (module.declarations ?? []).length > 0; + const hasCEExports = (module.exports ?? []).some( + ex => ex.kind === "custom-element-definition", + ); + + return hasDeclarations && hasCEExports; + }); + + filteredModules.forEach(module => { + const moduleSrc = module.path; + const moduleDir = path.dirname(moduleSrc); + const relativePath = path.relative(webComponentsSrcDir, moduleDir); + + if (!relativePath) return; + + const declarations = module.declarations; + const exports = module.exports; + + if (!declarations || !exports) return; + + const exportedSlots = exports + .filter(m => m.name.includes("Slots")) + .map(s => s.name); + + declarations.forEach(component => { + const kebabCaseName = getKebabCaseComponentName(component); + + if (!kebabCaseName) return; + + const compoundPartialName = kebabCaseName.replace( + relativePath.concat("-"), + "", + ); + + const isCompoundPartialComponent = compoundPartialName !== kebabCaseName; + + const importPaths: ImportPaths = {}; + let slotsEnumName; + + if (exportedSlots.length === 1) { + slotsEnumName = exportedSlots[0]; + } else if (exportedSlots.length > 1) { + if (isCompoundPartialComponent) { + slotsEnumName = exportedSlots.find(slotName => + slotName.toLowerCase().includes(compoundPartialName), + ); + } else { + slotsEnumName = exportedSlots.find( + slotName => + slotName.toLowerCase().replace("slots", "").length === 0, + ); + } + } + + importPaths.webComponents = `@tapsioss/web-components/${relativePath}`; + + components.push({ + ...(component as CustomElement), + kebabCaseName, + importPaths, + slotsEnumName, + }); + const sidebarItem: DefaultTheme.SidebarItem = {}; + const childPath = relativePath.split("/")[1]; + + sidebarItem.text = childPath ?? relativePath; + + if (declarations.length === 1) { + sidebarItem.link = `components/${kebabCaseName}`; + } else { + if (!Array.isArray(sidebarItem.items)) { + sidebarItem.items = []; + } + + sidebarItem.items = declarations.map(component => { + const name = getKebabCaseComponentName(component) || ""; + + return { + text: name, + link: `components/${name}`, + }; + }); + } + + if (!childPath) { + sidebarItemsMap[sidebarItem.text] = sidebarItem; + } else { + const [parentPath] = relativePath.split("/"); + + const parentItem = sidebarItemsMap[parentPath!]; + + if (parentItem) { + if (!Array.isArray(parentItem.items)) { + parentItem.items = []; + } + + parentItem.items.push(sidebarItem); + } else { + sidebarItemsMap[parentPath!] = { + text: parentPath, + items: [sidebarItem], + }; + } + } + }); + }); + + fs.writeFileSync( + metadataFile, + JSON.stringify( + { + components, + sidebarItems: Object.values(sidebarItemsMap).sort((a, b) => + a.text!.localeCompare(b.text!), + ), + }, + null, + 2, + ), + ); + + console.log("✅ components metadata generated."); +})(); +/* eslint-enable no-console */ diff --git a/tsconfig.json b/tsconfig.json index 5a3fb79d..ab1eaa5b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -49,6 +49,6 @@ "@tapsioss/theme": ["./packages/theme/dist/index.css"] } }, - "include": ["**/*.ts", "**/*.tsx"], + "include": ["**/*.ts", "**/*.tsx", "docs/.vitepress"], "exclude": ["node_modules", "dist"] }