diff --git a/.changeset/forty-mammals-find.md b/.changeset/forty-mammals-find.md deleted file mode 100644 index 118e1f805..000000000 --- a/.changeset/forty-mammals-find.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@qualcomm-ui/react": patch ---- - -refactor: cleaned up alert banner types diff --git a/.claude/agents/code-connect-generator.md b/.claude/agents/code-connect-generator.md new file mode 100644 index 000000000..327651df1 --- /dev/null +++ b/.claude/agents/code-connect-generator.md @@ -0,0 +1,242 @@ +--- +name: code-connect-generator +description: | + Generates Figma Code Connect files for components in the @qualcomm-ui repository. Invoke when the user needs Code Connect configurations created or updated for Figma design system integration. +model: inherit +color: blue +--- + +You are a Figma Code Connect specialist for the @qualcomm-ui design system. Your job is to create accurate Code Connect configuration files that map Figma components to their code implementations. + +## Critical Rules + +### 1. Verify Figma Properties Before Using Them + +**Always use Figma MCP tools** (`get_design_context`, `get_metadata`) to verify property names exist. Never assume. + +- Property names vary between similar components (e.g., `inputText` vs `passwordText`) +- If a property doesn't exist but is needed, hardcode it conditionally + +```tsx +// ❌ WRONG - assumed property exists +errorText: figma.string("errorText") + +// ✓ CORRECT - property doesn't exist, hardcode for relevant states +errorText: figma.enum("state", { + invalid: "Error message", + "invalid-focus": "Error message", +}) + +// ✓ CORRECT - hardcode icon when instance mapping is impractical +startIcon: figma.boolean("startIcon", { + true: "KeyRound", +}) +``` + +### 2. Never Include Default Values + +Before writing any `figma.enum()`, identify the component's default and omit it: + +```tsx +// If "md" is the default size: + +// ❌ WRONG +size: figma.enum("size", { lg: "lg", md: "md", sm: "sm" }) + +// ✓ CORRECT +size: figma.enum("size", { lg: "lg", sm: "sm" }) +``` + +### 3. Use Uncontrolled Props for Form Components + +Code Connect examples are static snapshots. Use uncontrolled variants (`default*` props) instead of controlled ones: + +```tsx +// ❌ WRONG - controlled prop implies state management +checked: figma.enum("variant", { checked: true }) + +// ✓ CORRECT - uncontrolled prop for static example +defaultChecked: figma.enum("variant", { checked: true }) +``` + +Common mappings: +- `checked` → `defaultChecked` +- `value` → `defaultValue` +- `selected` → `defaultSelected` +- `pageSize` → `defaultPageSize` +- `page` → `defaultPage` + +### 4. Code Connect Files Are Templates, Not TypeScript + +The parser extracts text patterns - no runtime JS/TS features work: + +```tsx +// ❌ WRONG - these will fail +status={showStatus ? "active" : undefined} +{showStatus && } +true: "active" as const + +// ✓ CORRECT - handle conditionals in props definition +status: figma.boolean("status", { true: "active" }) +statusIndicator: figma.boolean("status", { true: }) +``` + +### 5. Derive Multiple Props from One Figma Property + +When Figma shows a combined visual but React needs multiple props to achieve it, use the same Figma property to derive both: + +```tsx +// Figma shows "0/100" when count=true +// React needs both `counter` AND `maxLength` to display "n/max" + +counter: figma.boolean("count"), +maxLength: figma.boolean("count", { + true: 100, +}), +``` + +### 6. Ignore Display-Only Figma Properties + +Some Figma properties exist purely for design preview and have no React equivalent because the component auto-generates that content. Do not map these. + +Examples: +- `countText` ("0/100") - React's counter auto-generates this from `maxLength` +- `inputText` when used only for visual preview - map via `defaultValue` instead + +### 7. No Figma Helpers Inside JSX Children + +You cannot interpolate figma helpers as children inside JSX elements: + +```tsx +// ❌ WRONG - figma.string() inside JSX children doesn't work +appTitle: figma.boolean("showTitle", { + true: {figma.string("titleText")}, +}) + +// ✓ CORRECT - use hardcoded content in conditional JSX +appTitle: figma.boolean("showTitle", { + true: App Name, +}) + +// ✓ CORRECT - or get the string separately and use in example +props: { + titleText: figma.string("titleText"), +}, +example: ({titleText}) => {titleText} +``` + +**Trade-off:** You cannot have both conditional rendering AND dynamic text in the same element. Choose one: +- Conditional with hardcoded text (preferred - shows the pattern to users) +- Always shown with dynamic text (when the actual value matters more) + +### 8. Extract Shared Props Into Constants + +When multiple `figma.connect` calls use the same props, extract them to avoid repetition: + +```tsx +const sharedProps = { + size: figma.enum("size", {md: "md"}), + showIcon: figma.boolean("icon", {true: }), +} + +figma.connect(Component, URL, { variant: {type: "a"}, props: sharedProps, ... }) +figma.connect(Component, URL, { variant: {type: "b"}, props: sharedProps, ... }) +``` + +### 9. Showcase Frequently-Used API Features + +Code Connect teaches developers how to use components - not just map Figma properties. Hardcode examples of frequently-used props even without Figma property mappings. Review the component's documentation demos to identify high-value props worth including. + +### 10. Composite Components + +Create separate `figma.connect` calls for sub-components when it makes sense for the examples. Reference the component's documentation page to understand the recommended usage patterns. + +### 11. Icon Handling + +Prefer `figma.instance("iconProp")` when feasible—getting content from Figma is always better. Hardcode icon names only when instance mapping isn't practical. + +### 12. Always Use the User-Provided Node ID + +**When the user provides a Figma URL, use that exact node ID.** Do not replace it with variant node IDs found in metadata. + +```tsx +// User provides: https://figma.com/design/XXX?node-id=3746-4648 + +// ❌ WRONG - replacing user's URL with variant node IDs from metadata +figma.connect(Component, "?node-id=3746-4649", {...}) // variant node +figma.connect(Component, "?node-id=3746-4651", {...}) // another variant + +// ✓ CORRECT - use the user's node ID for ALL connects, differentiate with `variant` +const FIGMA_URL = "?node-id=3746-4648" + +figma.connect(Component, FIGMA_URL, { + variant: {type: "a"}, + ... +}) +figma.connect(Component, FIGMA_URL, { + variant: {type: "b"}, + ... +}) +``` + +Using variant node IDs will fail validation with: "node is not a top level component or component set" + +**Verification workflow:** +1. User provides a URL → extract the node ID +2. Use `get_metadata` or `get_design_context` to verify it's a component set (not a variant) +3. If valid component set → use it directly +4. If it's a documentation frame → ask user which component set within it to use +5. **Never substitute the user's node ID with variant node IDs from metadata** + +## File Location + +- React: `packages/frameworks/react/src/[component]/figma/[component].figma.tsx` +- Angular: `packages/frameworks/angular/src/[component]/figma/[component].figma.ts` + +## Syntax Reference + +| Helper | Use Case | +|--------|----------| +| `figma.boolean("prop")` | Boolean toggles | +| `figma.boolean("prop", { true: value })` | Conditional values | +| `figma.enum("prop", { FigmaVal: "codeVal" })` | Variants/enums (omit defaults) | +| `figma.string("prop")` | String properties | +| `figma.textContent("Layer Name")` | Text from layers (not properties) | +| `figma.instance("prop")` | Nested component instances | +| `figma.nestedProps("Layer", { ... })` | Properties on nested components | +| `figma.children("Slot")` | Slot content | + +Prefer real imports at file top over `imports` prop (which can cause duplicates when components nest). + +## Workflow + +1. **Read the component** - understand props, identify defaults + - Check type definitions in `packages/common/qds-core/src/[component]/[component].types.ts` + - **Read component documentation** in `packages/docs/react-docs/src/routes/components+/[component]+/_[component].mdx` for implementation guidelines that should be reflected in examples (required attributes, recommended patterns, accessibility notes, etc.) + - Look for `Qds[Component]Size` or similar type unions to find valid values + - The first value in a union is typically the default (verify in component source) +2. **Verify Figma properties** via MCP tools - don't assume from similar components +3. **Map properties** - plan hardcodes for missing properties +4. **Generate file** - omit defaults, no ternary operators +5. **Run dry-run validation** from `packages/frameworks/react`: + ```bash + pnpm figma connect publish --dry-run --config ./figma/components.config.json + ``` +6. **Verify checklist**: + - [ ] Using user-provided node ID (not variant node IDs from metadata) + - [ ] Every Figma property verified via MCP + - [ ] No default values in enum mappings + - [ ] No ternary/logical operators in example + - [ ] No figma helpers interpolated inside JSX children + - [ ] Missing Figma properties hardcoded appropriately + - [ ] Documentation guidelines reflected in examples + - [ ] Real imports used instead of imports prop where possible + - [ ] Shared props extracted into constants (no duplication across connects) + - [ ] Uncontrolled props used for form/state values +7. **If dry-run fails** - read the error, attempt to fix it. If stuck, report the error to the user. + +## Resources + +- Figma file: `https://www.figma.com/design/ETvFgN3bbNvr6sbpoZyNuA/branch/G6YKSbQ5Jn83xQBRvlqe6M/Base-Component-Library-v1.0.4` +- Code Connect docs: `https://developers.figma.com/docs/code-connect/react/` +- Code Connect source (when docs are unclear): `https://github.com/figma/code-connect` diff --git a/.github/workflows/publish-figma.yml b/.github/workflows/publish-figma.yml new file mode 100644 index 000000000..aa8c19079 --- /dev/null +++ b/.github/workflows/publish-figma.yml @@ -0,0 +1,35 @@ +name: "Publish Figma" +env: + TURBO_API: "http://127.0.0.1:8585" + TURBO_TEAM: "qualcomm-ui" + TURBO_TOKEN: "turbo-cache-token" + +on: + push: + branches: [ main ] + paths: + - 'packages/frameworks/react/**/*.figma.tsx' + - 'packages/frameworks/angular/**/*.figma.ts' + workflow_dispatch: + +jobs: + publish-figma: + runs-on: ubuntu-latest + concurrency: + group: PR Publish Figma - ${{github.head_ref}} + cancel-in-progress: true + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/init-node-and-install + + - uses: ./.github/actions/init-remote-cache + with: + turborepo-remote-cache-bucket-name: ${{ secrets.TURBOREPO_REMOTE_CACHE_BUCKET_NAME }} + turborepo-remote-cache-access-key: ${{ secrets.TURBOREPO_REMOTE_CACHE_ACCESS_KEY }} + turborepo-remote-cache-secret-key: ${{ secrets.TURBOREPO_REMOTE_CACHE_SECRET_KEY }} + + - run: pnpm build --filter @qui/react + + - run: pnpm react figma:publish-components diff --git a/eslint.config.js b/eslint.config.js index 3453c3e50..697c9734c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -39,7 +39,7 @@ export default defineConfig( "**/public/exports/**", "**/frameworks/react-internal/files/component-list.md", "packages/docs/*/knowledge/**", - "**/*.figma.tsx", + "**/generated/**", ], }, { diff --git a/packages/common/qds-core/CHANGELOG.md b/packages/common/qds-core/CHANGELOG.md index 46dc2e97d..361462c3c 100644 --- a/packages/common/qds-core/CHANGELOG.md +++ b/packages/common/qds-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @qualcomm-ui/qds-core Changelog +## 1.18.0 (2026/02/13) + +### Features + +- feat(link): add `brand` and `white-persistent` emphasis + ## 1.17.0 (2026/02/11) ### Features diff --git a/packages/common/qds-core/package.json b/packages/common/qds-core/package.json index bed4fdec2..0d043f081 100644 --- a/packages/common/qds-core/package.json +++ b/packages/common/qds-core/package.json @@ -1,6 +1,6 @@ { "name": "@qualcomm-ui/qds-core", - "version": "1.17.0", + "version": "1.18.0", "description": "qds core components", "author": "Ryan Bower", "license": "BSD-3-Clause-Clear", diff --git a/packages/common/qds-core/src/button/qds-button.css b/packages/common/qds-core/src/button/qds-button.css index 144c4b58f..94a8a0b4d 100644 --- a/packages/common/qds-core/src/button/qds-button.css +++ b/packages/common/qds-core/src/button/qds-button.css @@ -315,6 +315,15 @@ --icon-size: 16px; } } + + /* For when the icon is declared as a node, i.e. `` */ + .lucide { + height: var(--icon-size); + stroke: currentColor; + stroke-linecap: round; + stroke-linejoin: round; + width: var(--icon-size); + } } .qui-button-group { diff --git a/packages/common/qds-core/src/icon/qds-icon.css b/packages/common/qds-core/src/icon/qds-icon.css index bd42e08eb..72573de3d 100644 --- a/packages/common/qds-core/src/icon/qds-icon.css +++ b/packages/common/qds-core/src/icon/qds-icon.css @@ -12,6 +12,14 @@ vertical-align: middle; width: var(--icon-width, var(--icon-size)); + svg.lucide { + height: var(--icon-height, var(--icon-size)); + stroke: currentColor; + stroke-linecap: round; + stroke-linejoin: round; + width: var(--icon-width, var(--icon-size)); + } + &[hidden] { display: none; } diff --git a/packages/common/qds-core/src/input/qds-input.css b/packages/common/qds-core/src/input/qds-input.css index 79d6b75d8..801b0e3d8 100644 --- a/packages/common/qds-core/src/input/qds-input.css +++ b/packages/common/qds-core/src/input/qds-input.css @@ -181,6 +181,11 @@ --icon-width: var(--sizing-80); margin-right: calc(var(--spacing-80) - var(--input-group-gap)); } + + svg.lucide { + height: var(--icon-height); + width: var(--icon-width); + } } .qui-input__clear-button { diff --git a/packages/common/qds-core/src/link/link.types.ts b/packages/common/qds-core/src/link/link.types.ts index 74e1825f2..85db23f50 100644 --- a/packages/common/qds-core/src/link/link.types.ts +++ b/packages/common/qds-core/src/link/link.types.ts @@ -6,7 +6,11 @@ import type {DirectionProperty} from "@qualcomm-ui/utils/direction" import type {linkClasses} from "./link.classes" -export type QdsLinkEmphasis = "default" | "neutral" +export type QdsLinkEmphasis = + | "default" + | "neutral" + | "brand" + | "white-persistent" export type QdsLinkSize = "xs" | "sm" | "md" | "lg" | "xl" | "xxl" diff --git a/packages/common/qds-core/src/link/qds-link.css b/packages/common/qds-core/src/link/qds-link.css index e989ee20b..d89b76f50 100644 --- a/packages/common/qds-core/src/link/qds-link.css +++ b/packages/common/qds-core/src/link/qds-link.css @@ -54,20 +54,60 @@ color: var(--color-interactive-text-link-neutral-pressed); } + &:focus-visible { + border: solid 1px var(--color-utility-focus-border); + color: var(--color-interactive-text-link-neutral-idle); + outline: solid 1px var(--color-utility-focus-border); + } + &:visited:not(:hover) { - border-bottom-color: var(--color-interactive-text-link-neutral-visited); + color: var(--color-interactive-text-link-neutral-visited); + } + } + + &[data-emphasis="brand"] { + color: var(--color-interactive-text-link-brand-idle); + + &:hover { + border-bottom-color: var(--color-interactive-text-link-brand-hover); + color: var(--color-interactive-text-link-brand-hover); + } + + &:active { + color: var(--color-interactive-text-link-brand-pressed); } &:focus-visible { border: solid 1px var(--color-utility-focus-border); - border-radius: var(--border-radius-md); - color: var(--color-interactive-text-link-neutral-idle); + color: var(--color-interactive-text-link-brand-idle); outline: solid 1px var(--color-utility-focus-border); - outline-offset: 0; } &:visited:not(:hover) { - color: var(--color-interactive-text-link-neutral-visited); + color: var(--color-interactive-text-link-brand-visited); + } + } + + &[data-emphasis="white-persistent"] { + color: var(--color-utility-persistent-white); + + &:hover { + border-bottom-color: var(--color-utility-persistent-white); + color: var(--color-utility-persistent-white); + } + + &:active { + color: var(--color-utility-persistent-transparent-white); + } + + &:focus-visible { + border: solid 1px var(--color-utility-focus-border); + color: var(--color-interactive-text-link-brand-idle); + outline: solid 1px var(--color-utility-focus-border); + } + + &:visited:not(:hover) { + color: var(--color-interactive-text-link-brand-visited); } } diff --git a/packages/docs/angular-docs/src/routes/components+/link+/demos/link-colors-demo.ts b/packages/docs/angular-docs/src/routes/components+/link+/demos/link-colors-demo.ts index e201d036c..6623562cf 100644 --- a/packages/docs/angular-docs/src/routes/components+/link+/demos/link-colors-demo.ts +++ b/packages/docs/angular-docs/src/routes/components+/link+/demos/link-colors-demo.ts @@ -8,8 +8,12 @@ import {LinkDirective} from "@qualcomm-ui/angular/link" template: ` `, diff --git a/packages/docs/react-docs/src/routes/components+/link+/demos/link-colors-demo.tsx b/packages/docs/react-docs/src/routes/components+/link+/demos/link-colors-demo.tsx index ff5582057..72e6a4319 100644 --- a/packages/docs/react-docs/src/routes/components+/link+/demos/link-colors-demo.tsx +++ b/packages/docs/react-docs/src/routes/components+/link+/demos/link-colors-demo.tsx @@ -6,8 +6,12 @@ export function LinkColorsDemo(): ReactElement { return (
{/* preview */} - Default - Neutral + default + neutral + brand +
+ white-persistent +
{/* preview */}
) diff --git a/packages/frameworks/angular/.gitignore b/packages/frameworks/angular/.gitignore new file mode 100644 index 000000000..3ff7e51fb --- /dev/null +++ b/packages/frameworks/angular/.gitignore @@ -0,0 +1 @@ +figma/generated diff --git a/packages/frameworks/angular/CHANGELOG.md b/packages/frameworks/angular/CHANGELOG.md index 24ede3f84..699f949b5 100644 --- a/packages/frameworks/angular/CHANGELOG.md +++ b/packages/frameworks/angular/CHANGELOG.md @@ -1,5 +1,15 @@ # @qualcomm-ui/angular Changelog +## 1.18.0 (2026/02/13) + +### Features + +- feat(link): add `brand` and `white-persistent` emphasis + +### Miscellaneous Chores + +- **deps:** update dependencies [@qualcomm-ui/qds-core] + ## 1.17.0 (2026/02/11) ### Features diff --git a/packages/frameworks/angular/figma/components.config.json b/packages/frameworks/angular/figma/components.config.json new file mode 100644 index 000000000..f7be77160 --- /dev/null +++ b/packages/frameworks/angular/figma/components.config.json @@ -0,0 +1,15 @@ +{ + "codeConnect": { + "include": ["src/**/*"], + "exclude": ["src/**/*.spec.ts"], + "parser": "html", + "label": "Angular", + "paths": { + "@qualcomm-ui/angular/*": ["./src/*/index.ts"] + }, + "documentUrlSubstitutions": { + "": "https://www.figma.com/design/G6YKSbQ5Jn83xQBRvlqe6M", + "": "https://www.figma.com/design/fJC9KDk1b8v5KxHRttSbqS" + } + } +} diff --git a/packages/frameworks/angular/figma/icons.config.json b/packages/frameworks/angular/figma/icons.config.json new file mode 100644 index 000000000..b33772062 --- /dev/null +++ b/packages/frameworks/angular/figma/icons.config.json @@ -0,0 +1,14 @@ +{ + "codeConnect": { + "include": ["figma/generated/icons/**"], + "exclude": ["src/**/*.spec.ts"], + "parser": "html", + "label": "Angular", + "paths": { + "@qualcomm-ui/angular/*": ["./src/*/index.ts"] + }, + "documentUrlSubstitutions": { + "": "https://www.figma.com/design/fJC9KDk1b8v5KxHRttSbqS" + } + } +} diff --git a/packages/frameworks/angular/package.json b/packages/frameworks/angular/package.json index 2d3ed855e..635374c1a 100644 --- a/packages/frameworks/angular/package.json +++ b/packages/frameworks/angular/package.json @@ -1,6 +1,6 @@ { "name": "@qualcomm-ui/angular", - "version": "1.17.0", + "version": "1.18.0", "description": "QUI Angular Components", "author": "Ryan Bower", "license": "BSD-3-Clause-Clear", @@ -29,7 +29,9 @@ "build:bundle-size": "ng build test-bundle-size-app", "analyze-bundle": "source-map-explorer bundle-size-dist/main.js", "explore-bundle": "run-s build:bundle-size analyze-bundle", - "publish-angular-package": "tsx apply-package && qui-cli publish-angular qui-angular" + "publish-angular-package": "tsx apply-package && qui-cli publish-angular qui-angular", + "figma:publish-components": "figma connect publish -c figma/components.config.json", + "figma:publish-icons": "figma connect publish -c figma/icons.config.json --batch-size 500" }, "devDependencies": { "@analogjs/platform": "catalog:", @@ -55,12 +57,14 @@ "@angular/ssr": "catalog:", "@faker-js/faker": "~9.6.0", "@floating-ui/dom": "^1.7.0", + "@figma/code-connect": "^1.3.13", "@jsdevtools/coverage-istanbul-loader": "~3.0.5", "@module-federation/enhanced": "0.21.2", "@qualcomm-ui/angular-core": "workspace:^1.5.0", "@qualcomm-ui/cli": "workspace:^1.0.6", "@qualcomm-ui/core": "workspace:^1.3.0", - "@qualcomm-ui/qds-core": "workspace:^1.17.0", + "@qualcomm-ui/dom": "workspace:^1.0.7", + "@qualcomm-ui/qds-core": "workspace:^1.18.0", "@qualcomm-ui/tailwind-plugin": "workspace:^1.5.1", "@qualcomm-ui/tsconfig": "workspace:^1.0.4", "@qualcomm-ui/utils": "workspace:^1.1.0", @@ -102,7 +106,7 @@ "@angular/platform-browser-dynamic": ">=20 <23", "@qualcomm-ui/angular-core": "workspace:^1.5.0", "@qualcomm-ui/core": "workspace:^1.3.0", - "@qualcomm-ui/qds-core": "workspace:^1.17.0", + "@qualcomm-ui/qds-core": "workspace:^1.18.0", "@qualcomm-ui/utils": "workspace:^1.1.0", "@tanstack/virtual-core": ">=3.13.12", "lucide-angular": ">=0.487.0 <1", diff --git a/packages/frameworks/angular/src/text-input/figma/text-input.figma.ts b/packages/frameworks/angular/src/text-input/figma/text-input.figma.ts new file mode 100644 index 000000000..9eb310448 --- /dev/null +++ b/packages/frameworks/angular/src/text-input/figma/text-input.figma.ts @@ -0,0 +1,47 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma, {html} from "@figma/code-connect/html" + +const sharedProps = { + defaultInputValue: figma.boolean("filled", { + true: figma.string("inputText"), + }), + disabled: figma.enum("state", { + disabled: true, + }), + errorText: figma.string("errorText"), + hint: figma.string("helperText"), + invalid: figma.enum("state", { + invalid: true, + }), + label: figma.boolean("label", { + true: figma.string("labelText"), + }), + placeholder: figma.string("holderText"), + required: figma.boolean("required"), + size: figma.enum("size", { + lg: "lg", + md: "md", + sm: "sm", + }), +} + +figma.connect("?node-id=4227-2418", { + example: (props) => html` + + + `, + props: { + ...sharedProps, + endIcon: figma.instance("iconRxs"), + startIcon: figma.instance("iconLxs"), + }, +}) diff --git a/packages/frameworks/angular/tsconfig.angular-lib.json b/packages/frameworks/angular/tsconfig.angular-lib.json index e809ab622..1be64b84d 100644 --- a/packages/frameworks/angular/tsconfig.angular-lib.json +++ b/packages/frameworks/angular/tsconfig.angular-lib.json @@ -25,7 +25,7 @@ } ], "include": ["./src"], - "exclude": ["**/*.spec.ts"], + "exclude": ["**/*.spec.ts", "**/*.figma.ts"], "angularCompilerOptions": { "flatModuleId": "qui-angular", "flatModuleOutFile": "qui-angular.js" diff --git a/packages/frameworks/angular/tsconfig.figma.json b/packages/frameworks/angular/tsconfig.figma.json new file mode 100644 index 000000000..ee6601c61 --- /dev/null +++ b/packages/frameworks/angular/tsconfig.figma.json @@ -0,0 +1,32 @@ +{ + "extends": "@qualcomm-ui/tsconfig/tsconfig.strict.json", + "compilerOptions": { + "composite": true, + "baseUrl": "./", + "incremental": true, + "rootDir": "./src", + "outDir": "./node_modules/.tmp/figma", + "tsBuildInfoFile": "node_modules/.tmp/tsbuildinfo.figma", + "paths": { + "@qualcomm-ui/angular/*": ["./src/*/index.ts"], + } + }, + "include": ["./src/**/*.figma.tsx"], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "../react-core/tsconfig.lib.json" + }, + { + "path": "../../common/core/tsconfig.lib.json" + }, + { + "path": "../../common/qds-core/tsconfig.lib.json" + }, + { + "path": "../../common/utils/tsconfig.lib.json" + } + ] +} diff --git a/packages/frameworks/angular/tsconfig.json b/packages/frameworks/angular/tsconfig.json index d495fbeef..2657afc2f 100644 --- a/packages/frameworks/angular/tsconfig.json +++ b/packages/frameworks/angular/tsconfig.json @@ -26,6 +26,9 @@ { "path": "./tsconfig.angular-lib.json" }, + { + "path": "./tsconfig.figma.json" + }, { "path": "./tsconfig.lib.json" }, diff --git a/packages/frameworks/angular/tsconfig.lib.json b/packages/frameworks/angular/tsconfig.lib.json index 0b1b06d2a..4bf80feaf 100644 --- a/packages/frameworks/angular/tsconfig.lib.json +++ b/packages/frameworks/angular/tsconfig.lib.json @@ -23,7 +23,7 @@ } ], "include": ["./src"], - "exclude": ["**/*.spec.ts"], + "exclude": ["**/*.spec.ts", "**/*.figma.ts"], "angularCompilerOptions": { "flatModuleId": "qui-angular", "flatModuleOutFile": "qui-angular.js" diff --git a/packages/frameworks/react-mdx/CHANGELOG.md b/packages/frameworks/react-mdx/CHANGELOG.md index dfa59f22a..277cbe21a 100644 --- a/packages/frameworks/react-mdx/CHANGELOG.md +++ b/packages/frameworks/react-mdx/CHANGELOG.md @@ -1,5 +1,11 @@ # @qualcomm-ui/react-mdx Changelog +## 1.11.9 (2026/02/13) + +### Miscellaneous Chores + +- **deps:** update dependencies [@qualcomm-ui/react] + ## 1.11.8 (2026/02/11) ### Miscellaneous Chores diff --git a/packages/frameworks/react-mdx/package.json b/packages/frameworks/react-mdx/package.json index 126fb521f..adb5fca75 100644 --- a/packages/frameworks/react-mdx/package.json +++ b/packages/frameworks/react-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@qualcomm-ui/react-mdx", - "version": "1.11.8", + "version": "1.11.9", "description": "React components and utilities for MDX documentation sites based on QUI Docs", "author": "Ryan Bower", "license": "BSD-3-Clause-Clear", @@ -55,7 +55,7 @@ "@qualcomm-ui/dom": "workspace:^1.0.7", "@qualcomm-ui/esbuild": "workspace:^1.0.5", "@qualcomm-ui/mdx-common": "workspace:^1.9.1", - "@qualcomm-ui/react": "workspace:^1.12.0", + "@qualcomm-ui/react": "workspace:^1.13.0", "@qualcomm-ui/react-core": "workspace:^1.2.0", "@qualcomm-ui/react-test-utils": "workspace:^1.0.0", "@qualcomm-ui/tsconfig": "workspace:^1.0.4", @@ -97,7 +97,7 @@ "@mdx-js/react": ">=3.0.1 <4", "@qualcomm-ui/core": "workspace:^1.3.0", "@qualcomm-ui/mdx-common": "workspace:^1.9.1", - "@qualcomm-ui/react": "workspace:^1.12.0", + "@qualcomm-ui/react": "workspace:^1.13.0", "@qualcomm-ui/react-core": "workspace:^1.2.0", "@qualcomm-ui/utils": "workspace:^1.1.0", "@shikijs/langs": ">=3.13.0", diff --git a/packages/frameworks/react-swagger/CHANGELOG.md b/packages/frameworks/react-swagger/CHANGELOG.md index a315f32c1..8f2b742da 100644 --- a/packages/frameworks/react-swagger/CHANGELOG.md +++ b/packages/frameworks/react-swagger/CHANGELOG.md @@ -1,5 +1,11 @@ # @qualcomm-ui/react-swagger Changelog +## 1.2.5 (2026/02/13) + +### Miscellaneous Chores + +- **deps:** update dependencies [@qualcomm-ui/qds-core] + ## 1.2.4 (2026/02/11) ### Miscellaneous Chores diff --git a/packages/frameworks/react-swagger/package.json b/packages/frameworks/react-swagger/package.json index 0e319c85d..ba865ab81 100644 --- a/packages/frameworks/react-swagger/package.json +++ b/packages/frameworks/react-swagger/package.json @@ -1,6 +1,6 @@ { "name": "@qualcomm-ui/react-swagger", - "version": "1.2.4", + "version": "1.2.5", "description": "Swagger component replacements using QUI React", "author": "Ryan Bower", "license": "BSD-3-Clause-Clear", @@ -45,10 +45,10 @@ "@qualcomm-ui/core": "workspace:^1.3.0", "@qualcomm-ui/css-utils": "workspace:*", "@qualcomm-ui/esbuild": "workspace:^1.0.5", - "@qualcomm-ui/qds-core": "workspace:^1.17.0", - "@qualcomm-ui/react": "workspace:^1.12.0", + "@qualcomm-ui/qds-core": "workspace:^1.18.0", + "@qualcomm-ui/react": "workspace:^1.13.0", "@qualcomm-ui/react-core": "workspace:^1.2.0", - "@qualcomm-ui/react-mdx": "workspace:^1.11.8", + "@qualcomm-ui/react-mdx": "workspace:^1.11.9", "@qualcomm-ui/react-test-utils": "workspace:^1.0.0", "@qualcomm-ui/tsconfig": "workspace:^1.0.4", "@qualcomm-ui/utils": "workspace:^1.1.0", @@ -101,7 +101,7 @@ "peerDependencies": { "@braintree/sanitize-url": "^7.1.1", "@qualcomm-ui/core": "workspace:^1.3.0", - "@qualcomm-ui/qds-core": "workspace:^1.17.0", + "@qualcomm-ui/qds-core": "workspace:^1.18.0", "@qualcomm-ui/react-core": "workspace:^1.2.0", "@qualcomm-ui/utils": "workspace:^1.1.0", "@tanstack/react-virtual": ">=3.13.9", diff --git a/packages/frameworks/react/.gitignore b/packages/frameworks/react/.gitignore index dc0003cbb..70c7f137c 100644 --- a/packages/frameworks/react/.gitignore +++ b/packages/frameworks/react/.gitignore @@ -1 +1,2 @@ -src/**/*/__visual-regression__/*actual.png \ No newline at end of file +src/**/*/__visual-regression__/*actual.png +figma/generated diff --git a/packages/frameworks/react/CHANGELOG.md b/packages/frameworks/react/CHANGELOG.md index 3c88ec806..0ad64d74d 100644 --- a/packages/frameworks/react/CHANGELOG.md +++ b/packages/frameworks/react/CHANGELOG.md @@ -1,5 +1,20 @@ # @qualcomm-ui/react Changelog +## 1.13.0 (2026/02/13) + +### Features + +- support Figma code connect +- feat(link): add `brand` and `white-persistent` emphasis + +### Code Refactoring + +- cleaned up alert banner types + +### Miscellaneous Chores + +- **deps:** update dependencies [@qualcomm-ui/qds-core] + ## 1.12.0 (2026/02/11) ### Features diff --git a/packages/frameworks/react/figma/components.config.json b/packages/frameworks/react/figma/components.config.json new file mode 100644 index 000000000..c55963c07 --- /dev/null +++ b/packages/frameworks/react/figma/components.config.json @@ -0,0 +1,14 @@ +{ + "codeConnect": { + "include": ["src/**/*"], + "exclude": ["src/**/*.spec.tsx"], + "parser": "react", + "paths": { + "@qualcomm-ui/react/*": ["./src/*/index.ts"] + }, + "documentUrlSubstitutions": { + "": "https://www.figma.com/design/ETvFgN3bbNvr6sbpoZyNuA", + "": "https://www.figma.com/design/4xDg5Mrv4mxjsK3xC3L5up" + } + } +} diff --git a/packages/frameworks/react/figma/icons.config.json b/packages/frameworks/react/figma/icons.config.json new file mode 100644 index 000000000..9e7787c66 --- /dev/null +++ b/packages/frameworks/react/figma/icons.config.json @@ -0,0 +1,13 @@ +{ + "codeConnect": { + "include": ["figma/generated/icons/**"], + "exclude": ["src/**/*.spec.tsx"], + "parser": "react", + "paths": { + "@qualcomm-ui/react/*": ["./src/*/index.ts"] + }, + "documentUrlSubstitutions": { + "": "https://www.figma.com/design/4xDg5Mrv4mxjsK3xC3L5up" + } + } +} diff --git a/packages/frameworks/react/package.json b/packages/frameworks/react/package.json index 9c0f31028..0868e1368 100644 --- a/packages/frameworks/react/package.json +++ b/packages/frameworks/react/package.json @@ -1,6 +1,6 @@ { "name": "@qualcomm-ui/react", - "version": "1.12.0", + "version": "1.13.0", "description": "QUI React components", "author": "Ryan Bower", "license": "BSD-3-Clause-Clear", @@ -35,14 +35,20 @@ "test:react:ci": "cross-env NODE_OPTIONS='' vitest -c vitest.config.ts --run --pool=forks --no-file-parallelism", "test:watch": "cross-env vitest -c vitest.config.ts", "watch:js": "tsx build.ts --watch --mode development", - "watch:ts": "pnpm build:ts -w --preserveWatchOutput" + "watch:ts": "pnpm build:ts -w --preserveWatchOutput", + "code-connect": "tsx scripts/code-connect/cli.ts", + "figma:publish-components": "figma connect publish -c figma/components.config.json", + "figma:publish-icons": "figma connect publish -c figma/icons.config.json --batch-size 500" }, "devDependencies": { "@babel/core": "^7.28.4", + "@commander-js/extra-typings": "^14.0.0", + "@figma/code-connect": "^1.3.13", "@qualcomm-ui/cli": "workspace:^1.0.6", "@qualcomm-ui/core": "workspace:^1.3.0", + "@qualcomm-ui/dom": "workspace:^1.0.7", "@qualcomm-ui/esbuild": "workspace:^1.0.5", - "@qualcomm-ui/qds-core": "workspace:^1.17.0", + "@qualcomm-ui/qds-core": "workspace:^1.18.0", "@qualcomm-ui/react-core": "workspace:^1.2.0", "@qualcomm-ui/react-test-utils": "workspace:^1.0.0", "@qualcomm-ui/tsconfig": "workspace:^1.0.4", @@ -50,6 +56,7 @@ "@tailwindcss/vite": "^4.1.11", "@tanstack/react-virtual": "^3.13.12", "@types/babel__core": "^7.20.5", + "@types/lodash-es": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@types/react-transition-group": "^4.4.6", @@ -59,7 +66,9 @@ "@vitest/coverage-v8": "^4.0.8", "@vitest/ui": "^4.0.8", "babel-plugin-react-compiler": "^1.0.0", + "commander": "^14.0.2", "esbuild": "^0.25.8", + "lodash-es": "catalog:", "lucide-react": "0.525.0", "playwright": "1.56.1", "quick-lru": "^7.2.0", @@ -78,7 +87,7 @@ }, "peerDependencies": { "@qualcomm-ui/core": "workspace:^1.3.0", - "@qualcomm-ui/qds-core": "workspace:^1.17.0", + "@qualcomm-ui/qds-core": "workspace:^1.18.0", "@qualcomm-ui/react-core": "workspace:^1.2.0", "@qualcomm-ui/utils": "workspace:^1.1.0", "@tanstack/react-virtual": ">=3.13.9", diff --git a/packages/frameworks/react/scripts/code-connect/cli.ts b/packages/frameworks/react/scripts/code-connect/cli.ts new file mode 100644 index 000000000..38293f98a --- /dev/null +++ b/packages/frameworks/react/scripts/code-connect/cli.ts @@ -0,0 +1,177 @@ +#!/usr/bin/env node + +import {Command} from "@commander-js/extra-typings" +import {client} from "@figma/code-connect" +import {chunk, sortBy, uniqBy} from "lodash-es" +import {mkdir, writeFile} from "node:fs/promises" +import path from "node:path" + +import {dedent} from "@qualcomm-ui/utils/dedent" + +import {logger, LogLevel} from "./logger.js" + +const program = new Command() + .name("code-connect") + .description("CLI for fetching Figma nodes for Code Connect") + .version("1.0.0") + +program + .command("fetch") + .description("Fetch all components from a Figma page") + .argument("", "Figma file or page URL") + .option("-n, --node-id ", "Specific node ID to fetch") + .option("-o, --out ", "Write JSON output to file") + .option("-v, --verbose", "Enable verbose logging", false) + .action(async (url, options) => { + if (options.verbose) { + logger.setLogLevel(LogLevel.Debug) + } + + if (!process.env.FIGMA_ACCESS_TOKEN) { + logger.error("FIGMA_ACCESS_TOKEN environment variable is not set") + process.exit(1) + } + + try { + const fetchUrl = options.nodeId ? `${url}?node-id=${options.nodeId}` : url + const components = await client.getComponents(fetchUrl) + const json = JSON.stringify(components, null, 2) + + if (options.out) { + await writeFile(options.out, json) + logger.info(`Wrote ${components.length} components to ${options.out}`) + } else { + console.log(json) + } + } catch (error) { + logger.error( + `Failed to fetch components: ${error instanceof Error ? error.message : String(error)}`, + ) + process.exit(1) + } + }) + +program + .command("generate-icons") + .description("Generate Code Connect files for icon components") + .argument("", "Figma file or page URL containing icons") + .option("-o, --out ", "Output directory", "generated/figma/icons") + .option("-v, --verbose", "Enable verbose logging", false) + .action(async (url, options) => { + if (options.verbose) { + logger.setLogLevel(LogLevel.Debug) + } + + if (!process.env.FIGMA_ACCESS_TOKEN) { + logger.error("FIGMA_ACCESS_TOKEN environment variable is not set") + process.exit(1) + } + + try { + const components = await client.getComponents(url) + + const icons = uniqBy( + components.filter((c) => c.name.startsWith("utl/")), + (obj) => obj.name, + ) + if (icons.length === 0) { + logger.warn( + "No icon components found (expected names starting with 'utl/')", + ) + process.exit(0) + } + + logger.info(`Found ${icons.length} icon components`) + + await mkdir(options.out, {recursive: true}) + + const codeConnects: IconCodeConnect[] = sortBy( + icons.map((icon) => { + const iconName = toPascalCase(icon.name.replace(/^utl\//, "")) + const props = icon.componentPropertyDefinitions + const sizeProp = props.size || props.Size + let figmaProps: string | undefined = undefined + if (sizeProp && (sizeProp.type as string) === "VARIANT") { + // inconsistent naming in the Figma icons file. (>_>) + figmaProps = dedent` + {iconName: "${iconName}", size: figma.enum("${props.size ? "size" : "Size"}", ${JSON.stringify( + sizeProp.variantOptions?.reduce( + (acc: Record, current) => { + acc[current] = current + return acc + }, + {}, + ), + )})} + ` + } + return { + figmaNodeId: encodeURIComponent(icon.id), + iconName, + props: figmaProps, + } + }), + (icon) => icon.iconName, + ) + + const batches: IconCodeConnect[][] = chunk(codeConnects, 100) + + await Promise.all( + batches.map(async (batch, index) => { + const suffix = batches.length > 1 ? `-${index + 1}` : "" + const outputFile = path.join(options.out, `icons${suffix}.figma.tsx`) + const content = generateIconsFile(batch) + await writeFile(outputFile, content) + logger.info(`Generated ${outputFile} with ${batch.length} icons`) + }), + ) + + logger.info( + `Generated ${batches.length} file(s) with ${icons.length} total icons`, + ) + } catch (error) { + logger.error( + `Failed to generate icons: ${error instanceof Error ? error.message : String(error)}`, + ) + process.exit(1) + } + }) + +function toPascalCase(str: string): string { + return str + .split(/[-_\s]+/) + .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join("") +} + +interface IconCodeConnect { + figmaNodeId: string + iconName: string + props: string | undefined +} + +function generateIconsFile(icons: IconCodeConnect[]): string { + const imports = icons.map((i) => i.iconName).join(", ") + const connects = icons + .map((i) => + dedent(` + figma.connect(${i.iconName}, "?node-id=${i.figmaNodeId}", { + imports: ['import {${i.iconName}} from "lucide-react"'], + props: ${i.props}, + }) + `), + ) + .join("\n\n") + + return dedent` + /* eslint-disable */ + // this file was automatically generated, do not edit it directly + + import figma from "@figma/code-connect" + import {${imports}} from "lucide-react" + + ${connects} + ` +} + +program.parse() diff --git a/packages/frameworks/react/scripts/code-connect/logger.ts b/packages/frameworks/react/scripts/code-connect/logger.ts new file mode 100644 index 000000000..eb678ce28 --- /dev/null +++ b/packages/frameworks/react/scripts/code-connect/logger.ts @@ -0,0 +1,45 @@ +import {styleText} from "node:util" + +export enum LogLevel { + Nothing = 0, + Error = 1, + Warn = 2, + Info = 3, + Debug = 4, +} + +let currentLogLevel: LogLevel = LogLevel.Info + +export const logger = { + debug(...messages: unknown[]) { + if (currentLogLevel >= LogLevel.Debug) { + console.debug(styleText("gray", formatMessages(messages))) + } + }, + + error(...messages: unknown[]) { + if (currentLogLevel >= LogLevel.Error) { + console.error(styleText("red", formatMessages(messages))) + } + }, + + info(...messages: unknown[]) { + if (currentLogLevel >= LogLevel.Info) { + console.info(styleText("white", formatMessages(messages))) + } + }, + + setLogLevel(level: LogLevel) { + currentLogLevel = level + }, + + warn(...messages: unknown[]) { + if (currentLogLevel >= LogLevel.Warn) { + console.warn(styleText("yellow", formatMessages(messages))) + } + }, +} + +function formatMessages(messages: unknown[]): string { + return messages.map((m) => (typeof m === "string" ? m : String(m))).join(" ") +} diff --git a/packages/frameworks/react/src/accordion/figma/accordion.figma.tsx b/packages/frameworks/react/src/accordion/figma/accordion.figma.tsx new file mode 100644 index 000000000..b1bc02fc8 --- /dev/null +++ b/packages/frameworks/react/src/accordion/figma/accordion.figma.tsx @@ -0,0 +1,103 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsAccordionSize} from "@qualcomm-ui/qds-core/accordion" +import {Accordion} from "@qualcomm-ui/react/accordion" + +/** Accordion Group */ + +const sharedGroupProps = { + children: figma.children("Accordion"), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + uncontained: figma.enum("contained", { + false: true, + }), +} + +// Accordion group +figma.connect(Accordion.Root, "?node-id=2191-5476", { + example: ({children, size, uncontained}) => { + return ( + + {/* Each item requires a unique value */} + {children} + + ) + }, + props: { + ...sharedGroupProps, + }, +}) + +/** Accordion Item */ + +const sharedItemProps = { + disabled: figma.enum("state", { + disabled: true, + }), +} + +figma.connect(Accordion.Item, "?node-id=2161-18192", { + example: ({disabled, icon, secondaryText}) => { + return ( + + Lorem Ipsum + + ) + }, + imports: ["import {SomeIcon} from 'lucide-react'"], + props: { + ...sharedItemProps, + icon: figma.boolean("icon", { + false: undefined, + true: SomeIcon, + }), + secondaryText: figma.boolean("subHeader", { + false: undefined, + true: "Secondary text", + }), + }, + variant: { + chevron: "right", + }, +}) + +figma.connect(Accordion.Item, "?node-id=2161-18192", { + example: ({disabled, secondaryText}) => { + return ( + + + + Title of accordion + {secondaryText} + + Lorem Ipsum + + ) + }, + props: { + ...sharedItemProps, + secondaryText: figma.boolean("subHeader", { + false: undefined, + true: ( + + Secondary text + + ), + }), + }, + variant: { + chevron: "left", + }, +}) diff --git a/packages/frameworks/react/src/alert-banner/figma/alert-banner.figma.tsx b/packages/frameworks/react/src/alert-banner/figma/alert-banner.figma.tsx index adf1998de..71ef3f83a 100644 --- a/packages/frameworks/react/src/alert-banner/figma/alert-banner.figma.tsx +++ b/packages/frameworks/react/src/alert-banner/figma/alert-banner.figma.tsx @@ -1,3 +1,5 @@ +// @ts-nocheck + // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. // SPDX-License-Identifier: BSD-3-Clause-Clear @@ -10,102 +12,102 @@ const FIGMA_URL = "https://www.figma.com/design/G6YKSbQ5Jn83xQBRvlqe6M/Code-Connect?node-id=3566-16209" const sharedProps = { - heading: figma.string("heading"), description: figma.boolean("description", { true: figma.string("descriptionText"), }), emphasis: figma.enum("emphasis", { - success: "success", - warning: "warning", danger: "danger", neutral: "neutral", + success: "success", + warning: "warning", }), + heading: figma.string("heading"), } // Strong variant with icon (simple API) figma.connect(AlertBanner, FIGMA_URL, { - variant: {variant: "strong", showIcon: true}, + example: (props) => , props: { ...sharedProps, - dismissable: figma.boolean("dismiss"), action: figma.boolean("button", { true: ( - ), }), + dismissable: figma.boolean("dismiss"), }, - example: (props) => , + variant: {showIcon: true, variant: "strong"}, }) // Strong variant without icon (composite API) figma.connect(AlertBanner.Root, FIGMA_URL, { - variant: {variant: "strong", showIcon: false}, + example: ({action, description, dismissButton, emphasis, heading}) => ( + + {heading} + {description} + {action} + {dismissButton} + + ), props: { ...sharedProps, - dismissButton: figma.boolean("dismiss", { - true: , - }), action: figma.boolean("button", { true: ( - ), }), + dismissButton: figma.boolean("dismiss", { + true: , + }), }, - example: ({emphasis, heading, description, action, dismissButton}) => ( - - {heading} - {description} - {action} - {dismissButton} - - ), + variant: {showIcon: false, variant: "strong"}, }) // Subtle variant with icon (simple API) figma.connect(AlertBanner, FIGMA_URL, { - variant: {variant: "subtle", showIcon: true}, + example: (props) => , props: { ...sharedProps, - variant: "subtle", - dismissable: figma.boolean("dismiss"), action: figma.boolean("button", { true: ( - ), }), + dismissable: figma.boolean("dismiss"), + variant: "subtle", }, - example: (props) => , + variant: {showIcon: true, variant: "subtle"}, }) // Subtle variant without icon (composite API) figma.connect(AlertBanner.Root, FIGMA_URL, { - variant: {variant: "subtle", showIcon: false}, + example: ({action, description, dismissButton, emphasis, heading}) => ( + + {heading} + {description} + {action} + {dismissButton} + + ), props: { ...sharedProps, - variant: "subtle", - dismissButton: figma.boolean("dismiss", { - true: , - }), action: figma.boolean("button", { true: ( - ), }), + dismissButton: figma.boolean("dismiss", { + true: , + }), + variant: "subtle", }, - example: ({emphasis, heading, description, action, dismissButton}) => ( - - {heading} - {description} - {action} - {dismissButton} - - ), + variant: {showIcon: false, variant: "subtle"}, }) diff --git a/packages/frameworks/react/src/avatar/avatar-root.tsx b/packages/frameworks/react/src/avatar/avatar-root.tsx index 2ba2a1409..133521ce2 100644 --- a/packages/frameworks/react/src/avatar/avatar-root.tsx +++ b/packages/frameworks/react/src/avatar/avatar-root.tsx @@ -7,7 +7,6 @@ import {type AvatarApiProps, splitAvatarProps} from "@qualcomm-ui/core/avatar" import { createQdsAvatarApi, type QdsAvatarApiProps, - type QdsAvatarVariant, } from "@qualcomm-ui/qds-core/avatar" import {AvatarContextProvider, useAvatar} from "@qualcomm-ui/react-core/avatar" import {normalizeProps} from "@qualcomm-ui/react-core/machine" @@ -23,18 +22,13 @@ import {QdsAvatarContextProvider} from "./qds-avatar-context" export interface AvatarRootProps extends AvatarApiProps, - Omit, + QdsAvatarApiProps, IdProp, Omit, "dir"> { /** * React {@link https://react.dev/learn/passing-props-to-a-component#passing-jsx-as-children children} prop. */ children?: ReactNode - - /** - * @deprecated use {@link emphasis} instead - */ - variant?: QdsAvatarVariant } /** diff --git a/packages/frameworks/react/src/avatar/figma/avatar.figma.tsx b/packages/frameworks/react/src/avatar/figma/avatar.figma.tsx new file mode 100644 index 000000000..7cd34eb8e --- /dev/null +++ b/packages/frameworks/react/src/avatar/figma/avatar.figma.tsx @@ -0,0 +1,143 @@ +// @ts-nocheck + +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {User} from "lucide-react" + +import type { + QdsAvatarEmphasis, + QdsAvatarSize, +} from "@qualcomm-ui/qds-core/avatar" +import {Avatar} from "@qualcomm-ui/react/avatar" + +const statusProps = { + status: figma.boolean("status", { + true: "active", + }), + statusIndicator: figma.boolean("status", { + true: , + }), +} + +const sharedProps = { + ...statusProps, + emphasis: figma.enum("emphasis", { + brand: "brand", + "high-contrast": "contrast", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + xl: "xl", + xs: "xs", + }), +} + +// Icon variant +figma.connect(Avatar, "?node-id=17804-5308", { + example: ({emphasis, size, status, statusIndicator}) => ( + + + {statusIndicator} + + ), + props: sharedProps, +}) + +// Initial variant +figma.connect(Avatar, "?node-id=17804-5517", { + example: ({emphasis, initialText, size, status, statusIndicator}) => ( + + {initialText} + {statusIndicator} + + ), + props: { + ...sharedProps, + initialText: figma.string("initialText"), + }, +}) + +// Image variant +figma.connect(Avatar, "?node-id=17804-5726", { + example: ({size, status, statusIndicator}) => ( + + + {statusIndicator} + + ), + props: { + size: figma.enum("size", { + lg: "lg", + sm: "sm", + xl: "xl", + xs: "xs", + }), + ...statusProps, + }, +}) + +// Main Example - Icon variant +figma.connect(Avatar, "?node-id=17809-2448", { + example: ({nested}) => ( + + + {nested.statusIndicator} + + ), + props: { + nested: figma.nestedProps("Avatar icon options", { + ...sharedProps, + }), + }, + variant: {variant: "icon"}, +}) + +// Main Example - Initial variant +figma.connect(Avatar, "?node-id=17809-2448", { + example: ({nested}) => ( + + {nested.initialText} + {nested.statusIndicator} + + ), + props: { + nested: figma.nestedProps("Avatar initial options", { + initialText: figma.string("initialText"), + ...sharedProps, + }), + }, + variant: {variant: "initial"}, +}) + +// Main Example - Image variant +figma.connect(Avatar, "?node-id=17809-2448", { + example: ({nested}) => ( + + + {nested.statusIndicator} + + ), + props: { + nested: figma.nestedProps("Avatar image options", { + size: figma.enum("size", { + lg: "lg", + sm: "sm", + xl: "xl", + xs: "xs", + }), + ...statusProps, + }), + }, + variant: {variant: "image"}, +}) diff --git a/packages/frameworks/react/src/badge/figma/badge.figma.tsx b/packages/frameworks/react/src/badge/figma/badge.figma.tsx new file mode 100644 index 000000000..a5db0601f --- /dev/null +++ b/packages/frameworks/react/src/badge/figma/badge.figma.tsx @@ -0,0 +1,194 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type { + QdsBadgeBasicSize, + QdsBadgeCategoryEmphasis, + QdsBadgeExtendedSize, + QdsBadgeExtraSize, + QdsBadgeSemanticEmphasis, + QdsIconBadgeVariant, + QdsNumberBadgeEmphasis, + QdsStatusBadgeVariant, + QdsTextBadgeVariant, +} from "@qualcomm-ui/qds-core/badge" +import { + Badge, + IconBadge, + NumberBadge, + StatusBadge, +} from "@qualcomm-ui/react/badge" + +// Number Badge (Badge count in Figma) +figma.connect(NumberBadge, "?node-id=13390-5460", { + example: ({disabled, emphasis, size, value}) => ( + + ), + props: { + disabled: figma.enum("disabled", { + yes: true, + }), + emphasis: figma.enum("emphasis", { + brand: "brand", + "brand-outline": "brand-outline", + danger: "danger", + info: "info", + neutral: "neutral", + "neutral-outline": "neutral-outline", + "persistent-black": "persistent-black", + "persistent-white": "persistent-white", + success: "success", + warning: "warning", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + value: figma.string("label"), + }, +}) + +// Status Badge +figma.connect(StatusBadge, "?node-id=13426-472", { + example: ({disabled, emphasis, size, variant}) => ( + + ), + props: { + disabled: figma.enum("disabled", { + yes: true, + }), + emphasis: figma.enum("emphasis", { + brand: "brand", + danger: "danger", + info: "info", + neutral: "neutral", + success: "success", + warning: "warning", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + xl: "xl", + xs: "xs", + }), + variant: figma.enum("variant", { + filled: "filled", + outline: "outlined", + }), + }, +}) + +// Icon Badge +figma.connect(IconBadge, "?node-id=10951-1155", { + example: ({disabled, emphasis, size, variant}) => ( + + ), + imports: [ + 'import {IconBadge} from "@qualcomm-ui/react/badge"', + 'import {Icon} from "lucide-react"', + ], + props: { + disabled: figma.enum("disabled", { + yes: true, + }), + emphasis: figma.enum( + "emphasis", + { + blue: "blue", + brand: "brand", + cyan: "cyan", + danger: "danger", + green: "green", + info: "info", + kiwi: "kiwi", + magenta: "magenta", + neutral: "neutral", + orange: "orange", + purple: "purple", + red: "red", + success: "success", + teal: "teal", + warning: "warning", + yellow: "yellow", + }, + ), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + xl: "xl", + xs: "xs", + xxs: "xxs", + }), + variant: figma.enum("variant", { + default: "default", + subtle: "subtle", + }), + }, +}) + +// Text Badge (Badge in code) +figma.connect(Badge, "?node-id=14305-13751", { + example: ({children, disabled, emphasis, size, variant}) => ( + + {children} + + ), + props: { + children: figma.string("label"), + disabled: figma.enum("disabled", { + yes: true, + }), + emphasis: figma.enum( + "emphasis", + { + blue: "blue", + brand: "brand", + cyan: "cyan", + danger: "danger", + green: "green", + info: "info", + kiwi: "kiwi", + magenta: "magenta", + neutral: "neutral", + orange: "orange", + purple: "purple", + red: "red", + success: "success", + teal: "teal", + warning: "warning", + yellow: "yellow", + }, + ), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + variant: figma.enum("variant", { + Default: "default", + subtle: "subtle", + }), + }, +}) diff --git a/packages/frameworks/react/src/breadcrumbs/figma/breadcrumbs.figma.tsx b/packages/frameworks/react/src/breadcrumbs/figma/breadcrumbs.figma.tsx new file mode 100644 index 000000000..8f25975fc --- /dev/null +++ b/packages/frameworks/react/src/breadcrumbs/figma/breadcrumbs.figma.tsx @@ -0,0 +1,79 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {FolderClosed} from "lucide-react" + +import type { + QdsBreadcrumbsEmphasis, + QdsBreadcrumbsSize, +} from "@qualcomm-ui/qds-core/breadcrumbs" +import {Breadcrumbs} from "@qualcomm-ui/react/breadcrumbs" + +// Shared props for size and emphasis (omitting defaults) +const sharedProps = { + emphasis: figma.enum("emphasis", { + neutral: "neutral", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), +} + +/** Breadcrumb Container (Root + List) */ +figma.connect(Breadcrumbs.Root, "?node-id=3728-17610", { + example: ({children, emphasis, size}) => ( + + {children} + + ), + props: { + ...sharedProps, + children: figma.children("_Breadcrumb item"), + }, +}) + +/** Breadcrumb Item */ + +// Default item (idle/hover/pressed/focus states) +figma.connect(Breadcrumbs.Item, "?node-id=3728-13488", { + example: ({icon}) => ( + Breadcrumb + ), + props: { + icon: figma.boolean("showIcon", { + true: FolderClosed, + }), + }, +}) + +// Active item (current page) +figma.connect(Breadcrumbs.Item, "?node-id=3728-13488", { + example: ({icon}) => ( + + Breadcrumb + + ), + props: { + icon: figma.boolean("showIcon", { + true: FolderClosed, + }), + }, + variant: {state: "active"}, +}) + +// Disabled item +figma.connect(Breadcrumbs.Item, "?node-id=3728-13488", { + example: ({icon}) => ( + + Breadcrumb + + ), + props: { + icon: figma.boolean("showIcon", { + true: FolderClosed, + }), + }, + variant: {state: "disabled"}, +}) diff --git a/packages/frameworks/react/src/button/figma/button-group.figma.tsx b/packages/frameworks/react/src/button/figma/button-group.figma.tsx new file mode 100644 index 000000000..5a97a485f --- /dev/null +++ b/packages/frameworks/react/src/button/figma/button-group.figma.tsx @@ -0,0 +1,26 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import {ButtonGroup} from "@qualcomm-ui/react/button" + +figma.connect(ButtonGroup, "?node-id=7191%3A1090", { + example: (props) => { + return ( + + {props.children} + + ) + }, + props: { + children: figma.children("*"), + layout: figma.enum("buttonWidth", { + fill: "fill", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + }, +}) diff --git a/packages/frameworks/react/src/button/figma/button.figma.tsx b/packages/frameworks/react/src/button/figma/button.figma.tsx new file mode 100644 index 000000000..3a2717d8c --- /dev/null +++ b/packages/frameworks/react/src/button/figma/button.figma.tsx @@ -0,0 +1,124 @@ +// @ts-nocheck + +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import {Button} from "@qualcomm-ui/react/button" + +const BUTTON_URL = "?node-id=3571-1400" +const COMPACT_BUTTON_URL = "?node-id=16548-1775" + +const sharedProps = { + disabled: figma.enum("state", { + disabled: true, + }), + emphasis: figma.enum("emphasis", { + "black-persistent": "black-persistent", + danger: "danger", + primary: "primary", + "white-persistent": "white-persistent", + }), + label: figma.string("label"), + size: figma.enum("size", { + large: "lg", + small: "sm", + }), + variant: figma.enum("variant", { + ghost: "ghost", + outline: "outline", + }), +} + +// Main Button +figma.connect(Button, BUTTON_URL, { + example: ({label, ...props}) => , + props: { + ...sharedProps, + startIcon: figma.enum("icon", { + start: figma.instance("iconXxs"), + }), + }, + variant: {icon: "start"}, +}) + +figma.connect(Button, BUTTON_URL, { + example: ({label, ...props}) => , + props: { + ...sharedProps, + endIcon: figma.enum("icon", { + end: figma.instance("iconXxs"), + }), + }, + variant: {icon: "end"}, +}) + +figma.connect(Button, BUTTON_URL, { + example: ({icon, ...props}) => , + props: { + ...sharedProps, + icon: figma.instance("iconXxs"), + }, + variant: {icon: "only"}, +}) + +figma.connect(Button, BUTTON_URL, { + example: ({label, ...props}) => , + props: sharedProps, + variant: {icon: "none"}, +}) + +// Compact Button +figma.connect(Button, COMPACT_BUTTON_URL, { + example: ({label, ...props}) => ( + + ), + props: { + ...sharedProps, + startIcon: figma.enum("icon", { + start: figma.instance("iconXxs"), + }), + }, + variant: {icon: "start"}, +}) + +figma.connect(Button, COMPACT_BUTTON_URL, { + example: ({label, ...props}) => ( + + ), + props: { + ...sharedProps, + endIcon: figma.enum("icon", { + end: figma.instance("iconXxs"), + }), + }, + variant: {icon: "end"}, +}) + +figma.connect(Button, COMPACT_BUTTON_URL, { + example: ({icon, ...props}) => ( + + ), + props: { + ...sharedProps, + icon: figma.instance("iconXxs"), + }, + variant: {icon: "only"}, +}) + +figma.connect(Button, COMPACT_BUTTON_URL, { + example: ({label, ...props}) => ( + + ), + props: sharedProps, + variant: {icon: "none"}, +}) diff --git a/packages/frameworks/react/src/checkbox/figma/checkbox.figma.tsx b/packages/frameworks/react/src/checkbox/figma/checkbox.figma.tsx new file mode 100644 index 000000000..c21b35c2d --- /dev/null +++ b/packages/frameworks/react/src/checkbox/figma/checkbox.figma.tsx @@ -0,0 +1,59 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsCheckboxSize} from "@qualcomm-ui/qds-core/checkbox" +import {Checkbox} from "@qualcomm-ui/react/checkbox" + +const sharedProps = { + defaultChecked: figma.enum("variant", { + checked: true, + "invalid-checked": true, + }), + disabled: figma.enum("state", { + disabled: true, + }), + indeterminate: figma.enum("variant", { + indeterminate: true, + "invalid-indeterminate": true, + }), + invalid: figma.enum("variant", { + "invalid-checked": true, + "invalid-indeterminate": true, + "invalid-unchecked": true, + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), +} + +// no label +figma.connect(Checkbox, "?node-id=12550-185694", { + example: (props) => { + return + }, + props: { + ...sharedProps, + }, +}) + +// label +figma.connect(Checkbox, "?node-id=67-706", { + example: ({hint, label, ...props}) => { + return + }, + props: { + ...sharedProps, + errorText: figma.string("errorText"), + hint: figma.boolean("hint", { + false: undefined, + true: figma.string("hintText"), + }), + label: figma.boolean("label", { + false: undefined, + true: figma.string("labelText"), + }), + }, +}) diff --git a/packages/frameworks/react/src/combobox/figma/combobox.figma.tsx b/packages/frameworks/react/src/combobox/figma/combobox.figma.tsx new file mode 100644 index 000000000..9cafce300 --- /dev/null +++ b/packages/frameworks/react/src/combobox/figma/combobox.figma.tsx @@ -0,0 +1,79 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {Layers} from "lucide-react" + +import {comboboxCollection} from "@qualcomm-ui/core/combobox" +import {Combobox} from "@qualcomm-ui/react/combobox" + +const sharedProps = { + disabled: figma.enum("state", { + disabled: true, + }), + errorText: figma.enum("state", { + invalid: "Error message", + "invalid-focus": "Error message", + "invalid-open": "Error message", + }), + hint: figma.boolean("hint", { + true: figma.string("hintText"), + }), + icon: figma.boolean("startIcon", { + true: , + }), + invalid: figma.enum("state", { + invalid: true, + "invalid-focus": true, + "invalid-open": true, + }), + readOnly: figma.enum("state", { + "read-only": true, + }), + required: figma.boolean("required"), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), +} + +// With label +figma.connect(Combobox, "?node-id=13121-11284&", { + example: ({hint, label, ...props}) => ( + + ), + props: { + ...sharedProps, + label: figma.boolean("label", { + true: figma.string("labelText"), + }), + }, + variant: { + label: true, + }, +}) + +// Without label - use aria-label on inputProps for accessibility +figma.connect(Combobox, "?node-id=13121-11284", { + example: ({hint, ...props}) => ( + + ), + props: sharedProps, + variant: { + label: false, + }, +}) diff --git a/packages/frameworks/react/src/dialog/figma/dialog.figma.tsx b/packages/frameworks/react/src/dialog/figma/dialog.figma.tsx new file mode 100644 index 000000000..138d71083 --- /dev/null +++ b/packages/frameworks/react/src/dialog/figma/dialog.figma.tsx @@ -0,0 +1,251 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsDialogSize} from "@qualcomm-ui/qds-core/dialog" +import {Button} from "@qualcomm-ui/react/button" +import {Dialog} from "@qualcomm-ui/react/dialog" + +const FIGMA_URL_DIALOG = "?node-id=17862-1908" +const FIGMA_URL_DEFAULT = "?node-id=8157-32610" +const FIGMA_URL_FORM = "?node-id=17824-6387" + +const sharedProps = { + closeButton: figma.boolean("dismiss", { + true: , + }), + heading: figma.boolean("heading", { + true: Heading, + }), + indicatorIcon: figma.boolean("icon", { + true: , + }), + size: figma.enum("size", { + md: "md", + }), + slot: figma.boolean("slot", { + true: "{/* Custom content goes here */}", + }), +} + +// Dialog component - default variant +figma.connect(Dialog.Root, FIGMA_URL_DIALOG, { + example: ({dialogDefault}) => ( + + + + {dialogDefault.indicatorIcon} + {dialogDefault.closeButton} + {dialogDefault.heading} + Lorem Ipsum + {dialogDefault.slot} + + {dialogDefault.footer} + + + ), + props: { + dialogDefault: figma.nestedProps("Dialog default options", { + closeButton: figma.boolean("dismiss", { + true: , + }), + footer: figma.boolean("buttonGroup", { + true: ( + + + + + + + + + ), + }), + heading: figma.boolean("heading", { + true: Heading, + }), + indicatorIcon: figma.boolean("icon", { + true: , + }), + size: figma.enum("size", { + md: "md", + }), + slot: figma.boolean("slot", { + true: "{/* Custom content goes here */}", + }), + }), + }, + variant: { + variant: "default", + }, +}) + +// Dialog component - form variant +figma.connect(Dialog.Root, FIGMA_URL_DIALOG, { + example: ({dialogForm}) => ( + + + + {dialogForm.closeButton} + {dialogForm.heading} + Lorem Ipsum + {/* Form content goes here */} + + {dialogForm.footer} + + + ), + props: { + dialogForm: figma.nestedProps("Dialog form options", { + closeButton: figma.boolean("dismiss", { + true: , + }), + footer: figma.boolean("buttonGroup", { + true: ( + + + + + + + + + ), + }), + heading: figma.boolean("heading", { + true: Heading, + }), + size: figma.enum("size", { + md: "md", + }), + }), + }, + variant: { + variant: "form", + }, +}) + +// _Dialog default (non-destructive) +figma.connect(Dialog.Root, FIGMA_URL_DEFAULT, { + example: ({closeButton, footer, heading, indicatorIcon, size, slot}) => ( + + + + {indicatorIcon} + {closeButton} + {heading} + Lorem Ipsum + {slot} + + {footer} + + + ), + props: { + ...sharedProps, + footer: figma.boolean("buttonGroup", { + true: ( + + + + + + + + + ), + }), + }, + variant: { + destructive: false, + }, +}) + +// Destructive dialog - uses danger button for destructive actions. +figma.connect(Dialog.Root, FIGMA_URL_DEFAULT, { + example: ({closeButton, footer, heading, indicatorIcon, size, slot}) => ( + // For destructive confirmations, consider adding role="alertdialog" + // to improve accessibility for screen readers. + + + + {indicatorIcon} + {closeButton} + {heading} + Lorem Ipsum + {slot} + + {footer} + + + ), + props: { + ...sharedProps, + footer: figma.boolean("buttonGroup", { + true: ( + + + + + + + + + ), + }), + }, + variant: { + destructive: true, + }, +}) + +// Form dialog - dialog with form content inside +figma.connect(Dialog.Root, FIGMA_URL_FORM, { + example: ({closeButton, footer, heading, size}) => ( + + + + {closeButton} + {heading} + Lorem Ipsum + {/* Form content goes here */} + + {footer} + + + ), + props: { + closeButton: figma.boolean("dismiss", { + true: , + }), + footer: figma.boolean("buttonGroup", { + true: ( + + + + + + + + + ), + }), + heading: figma.boolean("heading", { + true: Heading, + }), + size: figma.enum("size", { + md: "md", + }), + }, +}) diff --git a/packages/frameworks/react/src/divider/figma/divider.figma.tsx b/packages/frameworks/react/src/divider/figma/divider.figma.tsx new file mode 100644 index 000000000..48913201c --- /dev/null +++ b/packages/frameworks/react/src/divider/figma/divider.figma.tsx @@ -0,0 +1,26 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsDividerVariant} from "@qualcomm-ui/qds-core/divider" +import {Divider} from "@qualcomm-ui/react/divider" + +const variantProp = { + variant: figma.enum("variant", { + strong: "strong", + subtle: "subtle", + }), +} + +// Horizontal Divider +figma.connect(Divider, "?node-id=5578-13", { + example: (props) => , + props: variantProp, +}) + +// Vertical Divider +figma.connect(Divider, "?node-id=13492-10", { + example: (props) => , + props: variantProp, +}) diff --git a/packages/frameworks/react/src/drawer/figma/drawer.figma.tsx b/packages/frameworks/react/src/drawer/figma/drawer.figma.tsx new file mode 100644 index 000000000..e54011e96 --- /dev/null +++ b/packages/frameworks/react/src/drawer/figma/drawer.figma.tsx @@ -0,0 +1,69 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsDrawerSize} from "@qualcomm-ui/qds-core/drawer" +import {Button} from "@qualcomm-ui/react/button" +import {Drawer} from "@qualcomm-ui/react/drawer" + +const FIGMA_URL = "?node-id=10872-1658" + +figma.connect(Drawer.Root, FIGMA_URL, { + example: ({ + closeButton, + description, + footer, + formPlaceholder, + heading, + indicatorIcon, + size, + }) => ( + + + + {indicatorIcon} + {closeButton} + {heading} + {description} + {formPlaceholder} + + {footer} + + + ), + props: { + closeButton: figma.boolean("dismiss", { + true: , + }), + description: figma.boolean("description", { + true: Lorem Ipsum, + }), + footer: figma.boolean("buttonGroup", { + true: ( + + + + + + + + + ), + }), + formPlaceholder: figma.enum("variant", { + form: "{/* Form content goes here */}", + }), + heading: figma.boolean("heading", { + true: Heading, + }), + indicatorIcon: figma.boolean("icon", { + true: , + }), + size: figma.enum("size", { + md: "md", + }), + }, +}) diff --git a/packages/frameworks/react/src/header-bar/figma/header-bar.figma.tsx b/packages/frameworks/react/src/header-bar/figma/header-bar.figma.tsx new file mode 100644 index 000000000..4d3cbe89d --- /dev/null +++ b/packages/frameworks/react/src/header-bar/figma/header-bar.figma.tsx @@ -0,0 +1,73 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {Bell, Grid, Search, Settings, User} from "lucide-react" + +import type { + QdsHeaderBarPadding, + QdsHeaderBarSize, + QdsHeaderSurface, +} from "@qualcomm-ui/qds-core/header-bar" +import {Avatar} from "@qualcomm-ui/react/avatar" +import {HeaderBar} from "@qualcomm-ui/react/header-bar" +import {Menu} from "@qualcomm-ui/react/menu" + +figma.connect(HeaderBar, "?node-id=14622-42103", { + example: ({logo, padding, size, surface}) => ( + + {/* Logo content */} + {logo.appTitle} + {logo.divider} + + Home + Automated Jobs + + + Remote Sessions + + + + Option 1 + Option 2 + + + + Minutes + FAQ + + + + + + + Apps + + + + + + + + + ), + props: { + logo: figma.nestedProps("_Header logo", { + appTitle: figma.boolean("name", { + true: Qualcomm AI Runtime, + }), + divider: figma.boolean("divider", { + true: , + }), + }), + padding: figma.enum("padding", { + large: "large", + }), + size: figma.enum("size", { + lg: "lg", + }), + surface: figma.enum("surface", { + secondary: "secondary", + }), + }, +}) diff --git a/packages/frameworks/react/src/inline-notification/figma/inline-notification.figma.tsx b/packages/frameworks/react/src/inline-notification/figma/inline-notification.figma.tsx new file mode 100644 index 000000000..51e7d9e99 --- /dev/null +++ b/packages/frameworks/react/src/inline-notification/figma/inline-notification.figma.tsx @@ -0,0 +1,51 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsNotificationEmphasis} from "@qualcomm-ui/qds-core/inline-notification" +import {InlineNotification} from "@qualcomm-ui/react/inline-notification" +import {Link} from "@qualcomm-ui/react/link" + +figma.connect( + InlineNotification, + "?node-id=3598-17285", + { + example: ({dismissable, emphasis, inlineContent}) => { + return ( + + ) + }, + props: { + dismissable: figma.enum("dismiss", { + true: true, + }), + emphasis: figma.enum("emphasis", { + danger: "danger", + info: "info", + neutral: "neutral", + success: "success", + warning: "warning", + }), + inlineContent: figma.nestedProps("_Inline content", { + action: figma.boolean("showLink", { + true: Action, + }), + description: figma.boolean("description", { + true: figma.string("descriptionText"), + }), + heading: figma.string("heading"), + orientation: figma.enum("orientation", { + vertical: "vertical", + }), + }), + }, + }, +) diff --git a/packages/frameworks/react/src/link/figma/link.figma.tsx b/packages/frameworks/react/src/link/figma/link.figma.tsx new file mode 100644 index 000000000..19e29a06f --- /dev/null +++ b/packages/frameworks/react/src/link/figma/link.figma.tsx @@ -0,0 +1,109 @@ +// @ts-nocheck + +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsLinkEmphasis, QdsLinkSize} from "@qualcomm-ui/qds-core/link" +import {Link} from "@qualcomm-ui/react/link" + +const sharedProps = { + disabled: figma.enum("state", { + disabled: true, + }), + emphasis: figma.enum("emphasis", { + brand: "brand", + default: "default", + neutral: "neutral", + "white-persistent": "white-persistent", + }), + label: figma.textContent("Action"), + size: figma.enum("size", { + lg: "lg", + md: "md", + sm: "sm", + xl: "xl", + xs: "xs", + xxl: "xxl", + }), +} + +figma.connect(Link, "?node-id=55-4432", { + example: ({label, ...props}) => {label}, + props: { + ...sharedProps, + }, +}) + +figma.connect(Link, "?node-id=55-4432", { + example: ({label, ...props}) => {label}, + props: { + ...sharedProps, + endIcon: figma.instance("endIconXs"), + startIcon: figma.instance("startIconXs"), + }, + variant: { + size: "xs", + }, +}) + +figma.connect(Link, "?node-id=55-4432", { + example: ({label, ...props}) => {label}, + props: { + ...sharedProps, + endIcon: figma.instance("endIconXs"), + startIcon: figma.instance("startIconXs"), + }, + variant: { + size: "sm", + }, +}) + +figma.connect(Link, "?node-id=55-4432", { + example: ({label, ...props}) => {label}, + props: { + ...sharedProps, + endIcon: figma.instance("endIconSm"), + startIcon: figma.instance("startIconSm"), + }, + variant: { + size: "md", + }, +}) + +figma.connect(Link, "?node-id=55-4432", { + example: ({label, ...props}) => {label}, + props: { + ...sharedProps, + endIcon: figma.instance("endIconSm"), + startIcon: figma.instance("startIconSm"), + }, + variant: { + size: "lg", + }, +}) + +figma.connect(Link, "?node-id=55-4432", { + example: ({label, ...props}) => {label}, + props: { + ...sharedProps, + endIcon: figma.instance("endIconSm"), + startIcon: figma.instance("startIconSm"), + }, + variant: { + size: "xl", + }, +}) + +figma.connect(Link, "?node-id=55-4432", { + example: ({label, ...props}) => {label}, + props: { + ...sharedProps, + endIcon: figma.instance("endIconMd"), + startIcon: figma.instance("startIconMd"), + }, + variant: { + size: "xxl", + }, +}) diff --git a/packages/frameworks/react/src/menu/figma/menu.figma.tsx b/packages/frameworks/react/src/menu/figma/menu.figma.tsx new file mode 100644 index 000000000..e31e84682 --- /dev/null +++ b/packages/frameworks/react/src/menu/figma/menu.figma.tsx @@ -0,0 +1,153 @@ +// @ts-nocheck + +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {ArrowBigUp, Command, Component} from "lucide-react" + +import type {QdsMenuSize} from "@qualcomm-ui/qds-core/menu" +import {Icon} from "@qualcomm-ui/react/icon" +import {Menu} from "@qualcomm-ui/react/menu" +import {Portal} from "@qualcomm-ui/react-core/portal" + +const FIGMA_URL = "?node-id=9054-20157" + +const sharedProps = { + size: figma.enum("size", { + sm: "sm", + }), +} + +figma.connect(Menu.Root, FIGMA_URL, { + example: ({ + checkboxSection, + checkboxSeparator, + radioSection, + radioSeparator, + size, + }) => ( + + + Open Menu + + + + + + + Menu option name + + + Z + + + + + Menu option name + + + + Menu option name + + + + + Menu option name + + + + {/* Submenu content */} + + + + + + Menu option name + + + + Menu option name + + + {checkboxSeparator} + {checkboxSection} + {radioSeparator} + {radioSection} + + + + + ), + props: { + ...sharedProps, + checkboxSection: figma.boolean("section2", { + true: ( + + Title name + + + Checkbox label + + + + Checkbox label + + + + Checkbox label + + + + Checkbox label + + + + Checkbox label + + + + Checkbox label + + + ), + }), + checkboxSeparator: figma.boolean("section2", { + true: , + }), + radioSection: figma.boolean("section3", { + true: ( + + Title name + + + Radio button label + + + + Radio button label + + + + Radio button label + + + + Radio button label + + + + Radio button label + + + + Radio button label + + + ), + }), + radioSeparator: figma.boolean("section3", { + true: , + }), + }, +}) diff --git a/packages/frameworks/react/src/number-input/figma/number-input.figma.tsx b/packages/frameworks/react/src/number-input/figma/number-input.figma.tsx new file mode 100644 index 000000000..0d3d61faf --- /dev/null +++ b/packages/frameworks/react/src/number-input/figma/number-input.figma.tsx @@ -0,0 +1,58 @@ +// @ts-nocheck +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsInputSize} from "@qualcomm-ui/qds-core/input" +import {NumberInput} from "@qualcomm-ui/react/number-input" + +const sharedProps = { + defaultValue: figma.boolean("filled", { + true: figma.string("inputText"), + }), + disabled: figma.enum("state", { + disabled: true, + }), + errorText: figma.string("errorText"), + hint: figma.boolean("hint", { + true: figma.string("hintText"), + }), + invalid: figma.enum("state", { + invalid: true, + "invalid-focus": true, + }), + label: figma.boolean("label", { + true: figma.string("labelText"), + }), + readOnly: figma.enum("state", { + "read-only": true, + }), + required: figma.boolean("required"), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + unitOptions: figma.boolean("unitSelector", { + true: [ + {label: "$", value: "usd"}, + {label: "€", value: "eur"}, + ], + }), + unitSelect: figma.nestedProps("_Unit select", { + unitText: figma.string("unitText"), + }), +} + +figma.connect(NumberInput, "?node-id=4771-2328", { + example: ({hint, label, unitOptions, unitSelect, ...props}) => ( + + ), + props: sharedProps, +}) diff --git a/packages/frameworks/react/src/pagination/figma/pagination.figma.tsx b/packages/frameworks/react/src/pagination/figma/pagination.figma.tsx new file mode 100644 index 000000000..63dacf9e7 --- /dev/null +++ b/packages/frameworks/react/src/pagination/figma/pagination.figma.tsx @@ -0,0 +1,84 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsPaginationSize} from "@qualcomm-ui/qds-core/pagination" +import {Pagination} from "@qualcomm-ui/react/pagination" + +const FIGMA_URL = "?node-id=3746-4648" + +// Shared props for size (omitting default "sm") +const sharedProps = { + size: figma.enum("size", { + md: "md", + }), +} + +// Shared props for table-bar variants +const tableBarProps = { + ...sharedProps, + pageMetadata: figma.boolean("range", { + false: undefined, + true: ( + + {({count, pageEnd, pageStart}) => + `${pageStart} - ${pageEnd} of ${count} results` + } + + ), + }), + pageSize: figma.boolean("items", { + false: undefined, + true: ( + + Items per page + + ), + }), +} + +/** + * Pagination Nav - Simple page buttons only + */ +figma.connect(Pagination.Root, FIGMA_URL, { + example: ({size}) => ( + + + + ), + props: sharedProps, + variant: {control: "none", type: "pagination-nav"}, +}) + +/** + * Table Bar - Controls on left + * Layout: [PageSize] [PageMetadata] ... [PageButtons] + */ +figma.connect(Pagination.Root, FIGMA_URL, { + example: ({pageMetadata, pageSize, size}) => ( + + {pageSize} + {pageMetadata} + + + ), + props: tableBarProps, + variant: {control: "left", type: "table-bar"}, +}) + +/** + * Table Bar - Controls on right + * Layout: [PageButtons] ... [PageSize] [PageMetadata] + */ +figma.connect(Pagination.Root, FIGMA_URL, { + example: ({pageMetadata, pageSize, size}) => ( + + + {pageSize} + {pageMetadata} + + ), + props: tableBarProps, + variant: {control: "right", type: "table-bar"}, +}) diff --git a/packages/frameworks/react/src/password-input/figma/password-input.figma.tsx b/packages/frameworks/react/src/password-input/figma/password-input.figma.tsx new file mode 100644 index 000000000..9edc6d6ec --- /dev/null +++ b/packages/frameworks/react/src/password-input/figma/password-input.figma.tsx @@ -0,0 +1,57 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +// @ts-nocheck +import figma from "@figma/code-connect" + +import type {QdsInputSize} from "@qualcomm-ui/qds-core/input" +import {PasswordInput} from "@qualcomm-ui/react/password-input" + +const sharedProps = { + defaultValue: figma.boolean("filled", { + true: figma.string("passwordText"), + }), + defaultVisible: figma.enum("password", { + show: true, + }), + disabled: figma.enum("state", { + disabled: true, + }), + errorText: figma.enum("state", { + invalid: "Error message", + "invalid-focus": "Error message", + }), + hint: figma.boolean("hint", { + true: figma.string("hintText"), + }), + invalid: figma.enum("state", { + invalid: true, + "invalid-focus": true, + }), + label: figma.boolean("label", { + true: figma.string("labelText"), + }), + placeholder: figma.boolean("filled", { + false: figma.string("holderText"), + }), + readOnly: figma.enum("state", { + "read-only": true, + }), + required: figma.boolean("required"), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), +} + +figma.connect(PasswordInput, "?node-id=5307-3964", { + example: ({hint, label, startIcon, ...props}) => ( + + ), + props: { + ...sharedProps, + startIcon: figma.boolean("startIcon", { + true: "KeyRound", + }), + }, +}) diff --git a/packages/frameworks/react/src/popover/figma/popover.figma.tsx b/packages/frameworks/react/src/popover/figma/popover.figma.tsx new file mode 100644 index 000000000..f9f193ac3 --- /dev/null +++ b/packages/frameworks/react/src/popover/figma/popover.figma.tsx @@ -0,0 +1,66 @@ +// @ts-nocheck + +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsPopoverEmphasis} from "@qualcomm-ui/qds-core/popover" +import {Button} from "@qualcomm-ui/react/button" +import {Popover} from "@qualcomm-ui/react/popover" + +const FIGMA_URL = "?node-id=5951-1058" + +figma.connect(Popover, FIGMA_URL, { + example: ({actionItems, content, emphasis, positioning}) => ( + Show Popover} + > + {actionItems.button1} + {actionItems.button2} + + ), + props: { + actionItems: figma.nestedProps("_Popover content", { + button1: figma.boolean("actionItems", { + true: ( + + ), + }), + button2: figma.boolean("actionItems", { + true: , + }), + }), + content: figma.nestedProps("_Popover content", { + body: figma.string("body"), + label: figma.boolean("heading", { + true: figma.string("headingText"), + }), + }), + emphasis: figma.enum("emphasis", { + brand: "brand", + }), + positioning: figma.nestedProps("_Popover pointer placement", { + placement: figma.enum("position", { + "bottom-left": {placement: "bottom-start"}, + "bottom-middle": {placement: "bottom"}, + "bottom-right": {placement: "bottom-end"}, + "left-bottom": {placement: "left-end"}, + "left-middle": {placement: "left"}, + "left-top": {placement: "left-start"}, + "right-bottom": {placement: "right-end"}, + "right-middle": {placement: "right"}, + "right-top": {placement: "right-start"}, + "top-left": {placement: "top-start"}, + "top-middle": {placement: "top"}, + "top-right": {placement: "top-end"}, + }), + }), + }, +}) diff --git a/packages/frameworks/react/src/progress-ring/figma/progress-ring.figma.tsx b/packages/frameworks/react/src/progress-ring/figma/progress-ring.figma.tsx new file mode 100644 index 000000000..42b2aa9ce --- /dev/null +++ b/packages/frameworks/react/src/progress-ring/figma/progress-ring.figma.tsx @@ -0,0 +1,52 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type { + QdsProgressRingEmphasis, + QdsProgressRingSize, +} from "@qualcomm-ui/qds-core/progress-ring" +import {ProgressRing} from "@qualcomm-ui/react/progress-ring" + +const sharedProps = { + emphasis: figma.enum("emphasis", { + neutral: "neutral", + }), + errorText: figma.boolean("hint", { + true: figma.string("errorText"), + }), + invalid: figma.enum("state", { + invalid: true, + }), + label: figma.boolean("hint", { + true: figma.string("hintText"), + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + xl: "xl", + xs: "xs", + xxs: "xxs", + }), +} + +// Determinate progress ring (indeterminate=False) +figma.connect(ProgressRing, "?node-id=6622-615", { + example: (props) => , + props: { + ...sharedProps, + valueText: figma.boolean("percentage", { + false: undefined, + true: "25%", + }), + }, + variant: {indeterminate: "false"}, +}) + +// Indeterminate progress ring (indeterminate=true) +figma.connect(ProgressRing, "?node-id=6622-615", { + example: (props) => , + props: sharedProps, + variant: {indeterminate: "true"}, +}) diff --git a/packages/frameworks/react/src/progress/figma/progress.figma.tsx b/packages/frameworks/react/src/progress/figma/progress.figma.tsx new file mode 100644 index 000000000..760845937 --- /dev/null +++ b/packages/frameworks/react/src/progress/figma/progress.figma.tsx @@ -0,0 +1,52 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type { + QdsProgressEmphasis, + QdsProgressLabelOrientation, + QdsProgressSize, +} from "@qualcomm-ui/qds-core/progress" +import {Progress} from "@qualcomm-ui/react/progress" + +const sharedProps = { + emphasis: figma.enum("emphasis", { + neutral: "neutral", + }), + errorText: figma.string("errorText"), + hint: figma.string("hintText"), + invalid: figma.enum("state", { + invalid: true, + }), + label: figma.boolean("label", { + true: figma.string("labelText"), + }), + labelOrientation: figma.enum("labelPosition", { + side: "side", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), +} + +// Determinate progress (indeterminate=False) +figma.connect(Progress, "?node-id=4402-120", { + example: (props) => , + props: { + ...sharedProps, + valueText: figma.boolean("percentage", { + false: undefined, + true: "25%", + }), + }, + variant: {indeterminate: "False"}, +}) + +// Indeterminate progress (indeterminate=True) +figma.connect(Progress, "?node-id=4402-120", { + example: (props) => , + props: sharedProps, + variant: {indeterminate: "True"}, +}) diff --git a/packages/frameworks/react/src/radio/figma/radio.figma.tsx b/packages/frameworks/react/src/radio/figma/radio.figma.tsx new file mode 100644 index 000000000..7ce1de739 --- /dev/null +++ b/packages/frameworks/react/src/radio/figma/radio.figma.tsx @@ -0,0 +1,98 @@ +// @ts-nocheck + +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import type {QdsRadioSize} from "@qualcomm-ui/qds-core/radio" +import {Radio, RadioGroup} from "@qualcomm-ui/react/radio" + +const sharedRadioProps = { + defaultChecked: figma.enum("variant", { + checked: true, + "invalid-checked": true, + }), + disabled: figma.enum("state", { + disabled: true, + }), +} + +// radio with label +figma.connect(Radio, "?node-id=2270-3948", { + example: ({hint, labelText, ...props}) => { + return + }, + props: { + ...sharedRadioProps, + hint: figma.boolean("hint", { + false: undefined, + true: figma.string("hintText"), + }), + labelText: figma.string("labelText"), + }, +}) + +// radio without label +figma.connect(Radio, "?node-id=12607-174277", { + example: (props) => { + return + }, + props: { + ...sharedRadioProps, + }, +}) + +const sharedRadioGroupProps = { + children: figma.children("Radio with label"), + indented: figma.boolean("indented"), + orientation: figma.enum("orientation", { + horizontal: "horizontal", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), +} + +// radio-group with label +figma.connect(RadioGroup.Root, "?node-id=2270-4637", { + example: ({children, indented, label, size, ...props}) => { + return ( + + {label} + + {/* Each requires a value prop not shown here */} + {children} + + + ) + }, + props: { + ...sharedRadioGroupProps, + label: figma.string("labelText"), + }, + variant: { + label: true, + }, +}) + +// radio-group without label +figma.connect(RadioGroup.Root, "?node-id=2270-4637", { + example: ({children, indented, size, ...props}) => { + return ( + + + {/* Each requires a value prop not shown here */} + {children} + + + ) + }, + props: { + ...sharedRadioGroupProps, + }, + variant: { + label: false, + }, +}) diff --git a/packages/frameworks/react/src/segmented-control/figma/segmented-control.figma.tsx b/packages/frameworks/react/src/segmented-control/figma/segmented-control.figma.tsx new file mode 100644 index 000000000..c77394b0e --- /dev/null +++ b/packages/frameworks/react/src/segmented-control/figma/segmented-control.figma.tsx @@ -0,0 +1,79 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {SomeIcon} from "lucide-react" + +import {SegmentedControl} from "@qualcomm-ui/react/segmented-control" + +const sharedProps = { + layout: figma.enum("width", { + fill: "fill", + }), + orientation: figma.enum("orientation", { + vertical: "vertical", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + variant: figma.enum("emphasis", { + primary: "primary", + }), +} + +// Text-only items (icon=none) +figma.connect( + SegmentedControl.Root, + "?node-id=7169-793", + { + example: (props) => ( + + + + + ), + props: sharedProps, + variant: {icon: "none"}, + }, +) + +// Icon + text items (icon=start) +figma.connect( + SegmentedControl.Root, + "?node-id=7169-793", + { + example: (props) => ( + + + + + ), + props: sharedProps, + variant: {icon: "start"}, + }, +) + +// Icon-only items (icon=only) +figma.connect( + SegmentedControl.Root, + "?node-id=7169-793", + { + example: (props) => ( + + + + + ), + props: sharedProps, + variant: {icon: "only"}, + }, +) diff --git a/packages/frameworks/react/src/select/figma/select.figma.tsx b/packages/frameworks/react/src/select/figma/select.figma.tsx new file mode 100644 index 000000000..2c62d631e --- /dev/null +++ b/packages/frameworks/react/src/select/figma/select.figma.tsx @@ -0,0 +1,75 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {Layers} from "lucide-react" + +import {selectCollection} from "@qualcomm-ui/core/select" +import {Select} from "@qualcomm-ui/react/select" + +const sharedProps = { + disabled: figma.enum("state", { + disabled: true, + }), + errorText: figma.string("errorText"), + hint: figma.boolean("hint", { + true: figma.string("hintText"), + }), + icon: figma.boolean("startIcon", { + true: , + }), + invalid: figma.enum("state", { + invalid: true, + "invalid-focus": true, + "invalid-open": true, + }), + readOnly: figma.enum("state", { + "read-only": true, + }), + required: figma.boolean("required"), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), +} + +// With label +figma.connect(Select, "?node-id=6831-5712", { + example: ({hint, label, ...props}) => ( + + ), + props: sharedProps, + variant: { + label: false, + }, +}) diff --git a/packages/frameworks/react/src/side-nav/figma/side-nav.figma.tsx b/packages/frameworks/react/src/side-nav/figma/side-nav.figma.tsx new file mode 100644 index 000000000..2d466a2ff --- /dev/null +++ b/packages/frameworks/react/src/side-nav/figma/side-nav.figma.tsx @@ -0,0 +1,103 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {Rocket} from "lucide-react" + +import type {QdsSideNavSurface} from "@qualcomm-ui/qds-core/side-nav" +import {SideNav} from "@qualcomm-ui/react/side-nav" + +const SIDENAV_URL = "?node-id=14492-51514" +const MENU_ITEM_URL = "?node-id=14834-655454" + +const sharedProps = { + surface: figma.enum("surface", { + secondary: "secondary", + }), +} + +/** SideNav Root - Expanded */ + +figma.connect(SideNav.Root, SIDENAV_URL, { + example: ({surface}) => ( + + + {/* Logo */} + App Name + + + + + Group Title + {/* SideNav.Nodes or individual nodes */} + + + ), + props: sharedProps, + variant: { + variant: "expanded", + }, +}) + +/** SideNav Root - Collapsed */ + +figma.connect(SideNav.Root, SIDENAV_URL, { + example: ({surface}) => ( + + + {/* Logo */} + App Name + + + + + + Group Title + {/* SideNav.Nodes or individual nodes */} + + + ), + props: sharedProps, + variant: { + variant: "collapsed", + }, +}) + +/** Menu Item - Default (LeafNode) */ + +figma.connect(SideNav.LeafNode, MENU_ITEM_URL, { + example: () => ( + + + + Menu item + + ), + variant: { + variant: "default", + }, +}) + +/** Menu Item - Dropdown (Branch) */ + +figma.connect(SideNav.Branch, MENU_ITEM_URL, { + example: () => ( + + + + + Menu item + + + {/* Child nodes */} + + ), + variant: { + variant: "dropdown", + }, +}) diff --git a/packages/frameworks/react/src/slider/figma/slider.figma.tsx b/packages/frameworks/react/src/slider/figma/slider.figma.tsx new file mode 100644 index 000000000..b95cfab41 --- /dev/null +++ b/packages/frameworks/react/src/slider/figma/slider.figma.tsx @@ -0,0 +1,44 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import {Slider} from "@qualcomm-ui/react/slider" + +const sharedProps = { + disabled: figma.enum("state", { + disabled: true, + }), + errorText: figma.string("errorText"), + hint: figma.boolean("hint", { + true: figma.string("hintText"), + }), + invalid: figma.enum("state", { + invalid: true, + }), + label: figma.boolean("label", { + true: figma.string("labelText"), + }), + sideMarkers: figma.enum("valuePosition", { + side: true, + }), + size: figma.enum("size", { + sm: "sm", + }), + variant: figma.enum("emphasis", { + neutral: "neutral", + }), +} + +figma.connect(Slider, "?node-id=6427-386", { + example: ({defaultValue, hint, label, ...props}) => ( + + ), + props: { + ...sharedProps, + defaultValue: figma.enum("variant", { + range: [25, 75], + single: [25], + }), + }, +}) diff --git a/packages/frameworks/react/src/switch/figma/switch.figma.tsx b/packages/frameworks/react/src/switch/figma/switch.figma.tsx new file mode 100644 index 000000000..fd032c42d --- /dev/null +++ b/packages/frameworks/react/src/switch/figma/switch.figma.tsx @@ -0,0 +1,47 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import {Switch} from "@qualcomm-ui/react/switch" + +const sharedProps = { + defaultChecked: figma.enum("variant", { + checked: true, + "invalid-checked": true, + }), + disabled: figma.enum("state", { + disabled: true, + }), + invalid: figma.enum("variant", { + "invalid-checked": true, + "invalid-unchecked": true, + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), +} + +// Basic Switch (no label) +figma.connect(Switch, "?node-id=12609-520", { + example: (props) => , + props: sharedProps, +}) + +// Switch with label +figma.connect(Switch, "?node-id=2270-5170", { + example: ({hint, label, ...props}) => ( + + ), + props: { + ...sharedProps, + errorText: figma.string("errorText"), + hint: figma.boolean("hint", { + true: figma.string("hintText"), + }), + label: figma.boolean("label", { + true: figma.string("labelText"), + }), + }, +}) diff --git a/packages/frameworks/react/src/tabs/figma/tabs.figma.tsx b/packages/frameworks/react/src/tabs/figma/tabs.figma.tsx new file mode 100644 index 000000000..c10d94786 --- /dev/null +++ b/packages/frameworks/react/src/tabs/figma/tabs.figma.tsx @@ -0,0 +1,261 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {SomeIcon} from "lucide-react" + +import type {QdsTabsSize} from "@qualcomm-ui/qds-core/tabs" +import {Tab, Tabs} from "@qualcomm-ui/react/tabs" + +/** Line Tab Group */ + +const lineSharedProps = { + orientation: figma.enum("orientation", { + vertical: "vertical", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + xl: "xl", + }), +} + +// Line tab group - text only (icon=none) +figma.connect(Tabs.Root, "?node-id=8863-9067", { + example: (props) => ( + + + + + Tab label + + + Tab label + + + Content 1 + Content 2 + + ), + props: lineSharedProps, + variant: {icon: "none"}, +}) + +// Line tab group - icon + text (icon=start) +figma.connect(Tabs.Root, "?node-id=8863-9067", { + example: (props) => ( + + + + + Tab label + + + Tab label + + + Content 1 + Content 2 + + ), + props: lineSharedProps, + variant: {icon: "start"}, +}) + +// Line tab group - icon only (icon=only) +figma.connect(Tabs.Root, "?node-id=8863-9067", { + example: (props) => ( + + + + + + + + + + + Content 1 + Content 2 + + ), + props: lineSharedProps, + variant: {icon: "only"}, +}) + +/** Contained Tab Group */ + +const containedSharedProps = { + iconVariant: figma.enum("iconVariant", { + filled: "filled", + }), + orientation: figma.enum("orientation", { + vertical: "vertical", + }), + size: figma.enum("size", { + sm: "sm", + }), +} + +// Contained tab group - text only (icon=none) +figma.connect(Tabs.Root, "?node-id=7394-1748", { + example: ({iconVariant, ...props}) => ( + + + + Tab item 1 + + + Tab item 2 + + + Content 1 + Content 2 + + ), + props: containedSharedProps, + variant: {icon: "none"}, +}) + +// Contained tab group - icon + text (icon=start) +figma.connect(Tabs.Root, "?node-id=7394-1748", { + example: ({iconVariant, ...props}) => ( + + + + Tab item 1 + + + Tab item 2 + + + Content 1 + Content 2 + + ), + props: containedSharedProps, + variant: {icon: "start"}, +}) + +// Contained tab group - icon only (icon=only) +figma.connect(Tabs.Root, "?node-id=7394-1748", { + example: ({iconVariant, ...props}) => ( + + + + + + + + + + Content 1 + Content 2 + + ), + props: containedSharedProps, + variant: {icon: "only"}, +}) + +/** Individual Line Tab */ + +const lineTabSharedProps = { + disabled: figma.enum("state", { + disabled: true, + }), +} + +// Line tab - text only (icon=none) +figma.connect(Tab.Root, "?node-id=7755-27338", { + example: ({disabled}) => ( + + Tab label + + ), + props: lineTabSharedProps, + variant: {icon: "none"}, +}) + +// Line tab - icon + text (icon=start) +figma.connect(Tab.Root, "?node-id=7755-27338", { + example: ({disabled}) => ( + + Tab label + + ), + props: lineTabSharedProps, + variant: {icon: "start"}, +}) + +// Line tab - icon only (icon=only) +figma.connect(Tab.Root, "?node-id=7755-27338", { + example: ({disabled}) => ( + + + + ), + props: lineTabSharedProps, + variant: {icon: "only"}, +}) + +/** Individual Contained Tab */ + +const containedTabSharedProps = { + disabled: figma.enum("state", { + disabled: true, + }), + dismissButton: figma.boolean("dismissible", { + true: , + }), +} + +// Contained tab - text only (icon=none) +figma.connect(Tab.Root, "?node-id=7363-3965", { + example: ({disabled, dismissButton}) => ( + + Tab label + {dismissButton} + + ), + props: containedTabSharedProps, + variant: {icon: "none"}, +}) + +// Contained tab - icon + text (icon=start) +figma.connect(Tab.Root, "?node-id=7363-3965", { + example: ({disabled, dismissButton}) => ( + + Tab label + {dismissButton} + + ), + props: containedTabSharedProps, + variant: {icon: "start"}, +}) + +// Contained tab - icon only (icon=only) +figma.connect(Tab.Root, "?node-id=7363-3965", { + example: ({disabled, dismissButton}) => ( + + + {dismissButton} + + ), + props: containedTabSharedProps, + variant: {icon: "only"}, +}) diff --git a/packages/frameworks/react/src/tag/figma/tag.figma.tsx b/packages/frameworks/react/src/tag/figma/tag.figma.tsx new file mode 100644 index 000000000..6f16897d3 --- /dev/null +++ b/packages/frameworks/react/src/tag/figma/tag.figma.tsx @@ -0,0 +1,362 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" +import {Star} from "lucide-react" + +import type { + QdsTagEmphasis, + QdsTagRadius, + QdsTagSize, +} from "@qualcomm-ui/qds-core/tag" +import {Tag} from "@qualcomm-ui/react/tag" + +const sharedProps = { + disabled: figma.enum("state", { + disabled: true, + }), + emphasis: figma.enum("emphasis", { + blue: "blue", + cyan: "cyan", + green: "green", + kiwi: "kiwi", + magenta: "magenta", + neutral: "neutral", + orange: "orange", + "outline-neutral": "outline-neutral", + purple: "purple", + red: "red", + teal: "teal", + yellow: "yellow", + }), + endIcon: figma.boolean("endIcon", { + false: undefined, + true: Star, + }), + label: figma.string("label"), + radius: figma.enum("radius", { + rounded: "rounded", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + startIcon: figma.boolean("startIcon", { + false: undefined, + true: Star, + }), +} + +// General Tag - Read Only variant +figma.connect(Tag, "?node-id=17020-4041", { + example: ({nested}) => ( + + {nested.label} + + ), + props: { + nested: figma.nestedProps("Tag read only options", { + disabled: figma.enum("state", { + disabled: true, + }), + emphasis: figma.enum("emphasis", { + blue: "blue", + cyan: "cyan", + green: "green", + kiwi: "kiwi", + magenta: "magenta", + neutral: "neutral", + orange: "orange", + "outline-neutral": "outline-neutral", + purple: "purple", + red: "red", + teal: "teal", + yellow: "yellow", + }), + endIcon: figma.boolean("endIcon", { + false: undefined, + true: Star, + }), + label: figma.string("label"), + radius: figma.enum("radius", { + rounded: "rounded", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + startIcon: figma.boolean("startIcon", { + false: undefined, + true: Star, + }), + }), + }, + variant: {variant: "read-only"}, +}) + +// General Tag - Link variant +figma.connect(Tag, "?node-id=17020-4041", { + example: ({nested}) => ( + + {nested.label} + + ), + props: { + nested: figma.nestedProps("Tag link options", { + disabled: figma.enum("state", { + disabled: true, + }), + emphasis: figma.enum("emphasis", { + blue: "blue", + cyan: "cyan", + green: "green", + kiwi: "kiwi", + magenta: "magenta", + neutral: "neutral", + orange: "orange", + "outline-neutral": "outline-neutral", + purple: "purple", + red: "red", + teal: "teal", + yellow: "yellow", + }), + endIcon: figma.boolean("endIcon", { + false: undefined, + true: Star, + }), + label: figma.string("label"), + radius: figma.enum("radius", { + rounded: "rounded", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + startIcon: figma.boolean("startIcon", { + false: undefined, + true: Star, + }), + }), + }, + variant: {variant: "link"}, +}) + +// General Tag - Selectable variant +figma.connect(Tag, "?node-id=17020-4041", { + example: ({nested}) => ( + + {nested.label} + + ), + props: { + nested: figma.nestedProps("Tag selectable options", { + disabled: figma.enum("state", { + disabled: true, + }), + emphasis: figma.enum("emphasis", { + blue: "blue", + cyan: "cyan", + green: "green", + kiwi: "kiwi", + magenta: "magenta", + neutral: "neutral", + orange: "orange", + "outline-neutral": "outline-neutral", + purple: "purple", + red: "red", + teal: "teal", + yellow: "yellow", + }), + endIcon: figma.boolean("endIcon", { + false: undefined, + true: Star, + }), + label: figma.string("label"), + radius: figma.enum("radius", { + rounded: "rounded", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + startIcon: figma.boolean("startIcon", { + false: undefined, + true: Star, + }), + }), + }, + variant: {variant: "selectable"}, +}) + +// General Tag - Dismissible variant +figma.connect(Tag, "?node-id=17020-4041", { + example: ({nested}) => ( + + {nested.label} + + ), + props: { + nested: figma.nestedProps("Tag dismissible options", { + disabled: figma.enum("state", { + disabled: true, + }), + emphasis: figma.enum("emphasis", { + blue: "blue", + cyan: "cyan", + green: "green", + kiwi: "kiwi", + magenta: "magenta", + neutral: "neutral", + orange: "orange", + "outline-neutral": "outline-neutral", + purple: "purple", + red: "red", + teal: "teal", + yellow: "yellow", + }), + label: figma.string("label"), + radius: figma.enum("radius", { + rounded: "rounded", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + startIcon: figma.boolean("icon", { + false: undefined, + true: Star, + }), + }), + }, + variant: {variant: "dismissible"}, +}) + +// Link Tag +figma.connect(Tag, "?node-id=16776-17872", { + example: ({disabled, emphasis, endIcon, label, radius, size, startIcon}) => ( + + {label} + + ), + props: sharedProps, +}) + +// Selectable Tag +figma.connect(Tag, "?node-id=16835-3076", { + example: ({disabled, emphasis, endIcon, label, radius, size, startIcon}) => ( + + {label} + + ), + props: sharedProps, +}) + +// Dismissible Tag +figma.connect(Tag, "?node-id=16928-3307", { + example: ({disabled, emphasis, label, radius, size, startIcon}) => ( + + {label} + + ), + props: { + disabled: figma.enum("state", { + disabled: true, + }), + emphasis: figma.enum("emphasis", { + blue: "blue", + cyan: "cyan", + green: "green", + kiwi: "kiwi", + magenta: "magenta", + neutral: "neutral", + orange: "orange", + "outline-neutral": "outline-neutral", + purple: "purple", + red: "red", + teal: "teal", + yellow: "yellow", + }), + label: figma.string("label"), + radius: figma.enum("radius", { + rounded: "rounded", + }), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), + startIcon: figma.boolean("icon", { + false: undefined, + true: Star, + }), + }, +}) + +// Read Only Tag +figma.connect(Tag, "?node-id=16762-2131", { + example: ({disabled, emphasis, endIcon, label, radius, size, startIcon}) => ( + + {label} + + ), + props: sharedProps, +}) diff --git a/packages/frameworks/react/src/text-area/figma/text-area.figma.tsx b/packages/frameworks/react/src/text-area/figma/text-area.figma.tsx new file mode 100644 index 000000000..93ef0edc8 --- /dev/null +++ b/packages/frameworks/react/src/text-area/figma/text-area.figma.tsx @@ -0,0 +1,46 @@ +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +// SPDX-License-Identifier: BSD-3-Clause-Clear + +import figma from "@figma/code-connect" + +import {TextArea} from "@qualcomm-ui/react/text-area" + +const sharedProps = { + counter: figma.boolean("count"), + defaultValue: figma.boolean("filled", { + true: figma.string("inputText"), + }), + disabled: figma.enum("state", { + disabled: true, + }), + errorText: figma.string("errorText"), + hint: figma.boolean("hint", { + true: figma.string("hintText"), + }), + invalid: figma.enum("state", { + invalid: true, + "invalid-focus": true, + }), + label: figma.boolean("label", { + true: figma.string("labelText"), + }), + maxLength: figma.boolean("count", { + true: 100, + }), + placeholder: figma.string("holderText"), + readOnly: figma.enum("state", { + "read-only": true, + }), + required: figma.boolean("required"), + size: figma.enum("size", { + lg: "lg", + sm: "sm", + }), +} + +figma.connect(TextArea, "?node-id=4587-9674", { + example: ({hint, label, ...props}) => ( +