npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@sanring/gx-card

v5.0.1

Published

一個功能強大且高度靈活的 Angular 卡片組件庫,提供多種卡片樣式、佈局選項和豐富的互動功能。採用 Angular Standalone Components 架構實現,內建 Signal 狀態管理,適合用於儀表板、產品列表、內容展示等場景。

Readme

GX-Card

一個功能強大且高度靈活的 Angular 卡片組件庫,提供多種卡片樣式、佈局選項和豐富的互動功能。採用 Angular Standalone Components 架構實現,內建 Signal 狀態管理,適合用於儀表板、產品列表、內容展示等場景。

✅ 已完成的組件

核心組件

  • GxCard - 卡片容器組件(支援多種變體、形狀、點擊互動)
  • GxCardHeader - 卡片標頭(支援頭像、標題、副標題、連結)
  • GxCardContent - 卡片內容(支援圖片、標籤、描述文字收合)
  • GxCardFooter - 卡片底部(支援動作按鈕、自定義樣式)
  • GxCardGroup - 卡片群組容器(統一管理多個卡片樣式)

指令

  • GxClickableDirective - 可點擊指令(提供統一的點擊互動體驗)

服務

  • GxCardConfigService - 全局卡片配置服務
  • HeightMeasurementService - 文字高度測量服務(用於描述文字收合)

類型定義

  • IGxCard - 卡片資料介面
  • IGxCardHeader - 卡片標頭介面
  • IGxCardContent - 卡片內容介面
  • IGxCardFooter - 卡片底部介面
  • GxCardVariant - 卡片變體類型(elevated, outlined, flat)
  • GxCardLayout - 卡片佈局類型(grid, single, masonry)
  • GxCardShape - 卡片形狀類型(classic, square, landscape, portrait, custom)
  • GxAction - 動作按鈕介面
  • IGxTag - 標籤介面
  • IGxDescriptionCollapse - 描述文字收合配置

📦 安裝

npm install @sanring/gx-card

依賴

此套件依賴 @sanring/gx-ui 提供按鈕和標籤組件:

npm install @sanring/gx-ui

🎯 使用方式

基本卡片範例

import { Component } from '@angular/core';
import { GxCard, GxCardHeader, GxCardContent, GxCardFooter } from '@sanring/gx-card';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [GxCard, GxCardHeader, GxCardContent, GxCardFooter],
  template: `
    <gx-card>
      <gx-card-header
        [title]="'卡片標題'"
        [subtitle]="'這是副標題'">
      </gx-card-header>

      <gx-card-content
        [description]="'這是卡片的描述內容。可以放置任何文字資訊。'">
      </gx-card-content>

      <gx-card-footer
        [actions]="actions"
        (actionClick)="onAction($event)">
      </gx-card-footer>
    </gx-card>
  `
})
export class ExampleComponent {
  actions = [
    { id: 'view', label: '查看', intent: 'primary' as const },
    { id: 'edit', label: '編輯', intent: 'secondary' as const }
  ];

  onAction(action: any) {
    console.log('動作被點擊:', action);
  }
}

使用資料模式(向後兼容)

import { Component } from '@angular/core';
import { GxCard, IGxCard } from '@sanring/gx-card';

@Component({
  selector: 'app-data-example',
  standalone: true,
  imports: [GxCard],
  template: `
    <gx-card
      [data]="cardData"
      [variant]="'elevated'"
      (actions)="onAction($event)">
    </gx-card>
  `
})
export class DataExampleComponent {
  cardData: IGxCard = {
    id: 'card-1',
    header: {
      title: '產品名稱',
      subtitle: '產品副標題',
      avatar: {
        src: 'avatar.jpg',
        alt: '產品圖片'
      }
    },
    content: {
      title: '內容標題',
      description: '這是產品的詳細描述...',
      tags: [
        { id: 'tag1', label: '新品', intent: 'success' },
        { id: 'tag2', label: '熱銷', intent: 'error' }
      ]
    },
    footer: {
      actions: [
        { id: 'buy', label: '立即購買', intent: 'primary' },
        { id: 'fav', label: '加入收藏', intent: 'secondary' }
      ]
    }
  };

  onAction(action: any) {
    console.log('動作:', action);
  }
}

卡片變體

@Component({
  template: `
    <!-- 陰影卡片(預設) -->
    <gx-card [variant]="'elevated'">
      <gx-card-content [title]="'陰影卡片'"></gx-card-content>
    </gx-card>

    <!-- 邊框卡片 -->
    <gx-card [variant]="'outlined'">
      <gx-card-content [title]="'邊框卡片'"></gx-card-content>
    </gx-card>

    <!-- 扁平卡片 -->
    <gx-card [variant]="'flat'">
      <gx-card-content [title]="'扁平卡片'"></gx-card-content>
    </gx-card>
  `
})

卡片形狀

@Component({
  template: `
    <!-- 經典卡片(垂直佈局,完整功能) -->
    <gx-card [shape]="'classic'">
      <gx-card-header [title]="'經典卡片'"></gx-card-header>
      <gx-card-content [description]="'這是經典樣式的卡片'"></gx-card-content>
      <gx-card-footer [actions]="actions"></gx-card-footer>
    </gx-card>

    <!-- 方形卡片(精簡版) -->
    <gx-card [shape]="'square'">
      <gx-card-content [title]="'方形卡片'"></gx-card-content>
      <gx-card-footer [actions]="[{ id: '1', label: '查看' }]"></gx-card-footer>
    </gx-card>

    <!-- 長條形卡片(水平佈局) -->
    <gx-card [shape]="'landscape'" [layout]="'single'">
      <gx-card-header [title]="'長條形卡片'"></gx-card-header>
      <gx-card-content [description]="'適合單一排列的場景'"></gx-card-content>
      <gx-card-footer [actions]="actions"></gx-card-footer>
    </gx-card>
  `
})

可點擊卡片

import { Component } from '@angular/core';
import { GxCard, GxCardContent } from '@sanring/gx-card';

@Component({
  selector: 'app-clickable-card',
  standalone: true,
  imports: [GxCard, GxCardContent],
  template: `
    <gx-card
      [clickable]="true"
      (cardClick)="onCardClick($event)">
      <gx-card-content
        [title]="'可點擊的卡片'"
        [description]="'點擊卡片任何地方都會觸發事件'">
      </gx-card-content>
    </gx-card>
  `
})
export class ClickableCardComponent {
  onCardClick(event: MouseEvent) {
    console.log('卡片被點擊了!', event);
  }
}

Header 點擊功能

@Component({
  template: `
    <gx-card
      [headerClickable]="{ avatar: true, title: true }"
      (headerItemClick)="onHeaderItemClick($event)">
      <gx-card-header
        [avatar]="{ src: 'user.jpg', alt: '使用者頭像' }"
        [title]="'使用者名稱'"
        [subtitle]="'2024-01-01'">
      </gx-card-header>
      <gx-card-content [description]="'內容...'"></gx-card-content>
    </gx-card>
  `
})
export class HeaderClickExample {
  onHeaderItemClick(event: any) {
    console.log(`${event.part} 被點擊:`, event.value);
    // event.part 可以是 'avatar', 'title', 或 'subtitle'
  }
}

描述文字自動收合

@Component({
  template: `
    <gx-card>
      <gx-card-content
        [description]="longDescription"
        [descriptionCollapse]="{
          enabled: true,
          maxLines: 3,
          expandText: '顯示更多',
          collapseText: '顯示較少'
        }">
      </gx-card-content>
    </gx-card>
  `
})
export class CollapsibleTextExample {
  longDescription = '這是一段很長的描述文字...(超過3行會自動收合)';
}

標籤功能

import { Component } from '@angular/core';
import { GxCard, GxCardContent } from '@sanring/gx-card';
import { IGxTag } from '@sanring/gx-card';

@Component({
  selector: 'app-tags-example',
  standalone: true,
  imports: [GxCard, GxCardContent],
  template: `
    <gx-card (tagClick)="onTagClick($event)">
      <gx-card-content
        [title]="'產品標題'"
        [tags]="tags">
      </gx-card-content>
    </gx-card>
  `
})
export class TagsExampleComponent {
  tags: IGxTag[] = [
    { id: '1', label: '新品', intent: 'success' },
    { id: '2', label: '熱賣', intent: 'error' },
    { id: '3', label: '限時', intent: 'warning' }
  ];

  onTagClick(event: { tag: IGxTag, event: MouseEvent }) {
    console.log('標籤被點擊:', event.tag);
  }
}

卡片群組

import { Component } from '@angular/core';
import { GxCardGroup, GxCard, GxCardContent } from '@sanring/gx-card';

@Component({
  selector: 'app-card-group',
  standalone: true,
  imports: [GxCardGroup, GxCard, GxCardContent],
  template: `
    <gx-card-group>
      <gx-card>
        <gx-card-content [title]="'卡片 1'"></gx-card-content>
      </gx-card>

      <gx-card>
        <gx-card-content [title]="'卡片 2'"></gx-card-content>
      </gx-card>

      <gx-card>
        <gx-card-content [title]="'卡片 3'"></gx-card-content>
      </gx-card>
    </gx-card-group>
  `,
  styles: [`
    gx-card-group {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
      gap: 1rem;
    }
  `]
})
export class CardGroupExample {}

自定義顏色

import { Component } from '@angular/core';
import { GxCard, GxCardContent, IGxCardColors } from '@sanring/gx-card';

@Component({
  selector: 'app-custom-colors',
  standalone: true,
  imports: [GxCard, GxCardContent],
  template: `
    <gx-card [colors]="customColors">
      <gx-card-content
        [title]="'自定義顏色卡片'"
        [description]="'這張卡片使用自定義的顏色配置'">
      </gx-card-content>
    </gx-card>
  `
})
export class CustomColorsExample {
  customColors: IGxCardColors = {
    background: '#f0f9ff',
    textColor: '#0c4a6e',
    borderColor: '#0ea5e9',
    titleColor: '#075985',
    subtitleColor: '#0369a1',
    hoverBackground: '#e0f2fe'
  };
}

使用 GxClickable 指令

import { Component } from '@angular/core';
import { GxClickableDirective, GxClickableEvent } from '@sanring/gx-card';

@Component({
  selector: 'app-clickable-directive',
  standalone: true,
  imports: [GxClickableDirective],
  template: `
    <div
      gxClickable
      [gxClickableData]="userData"
      [gxClickableType]="'text'"
      (gxClick)="onClick($event)">
      點擊這段文字
    </div>

    <img
      gxClickable
      [gxClickableData]="imageData"
      [gxClickableType]="'avatar'"
      [gxClickableShowCursor]="true"
      (gxClick)="onImageClick($event)"
      src="avatar.jpg"
      alt="頭像">
  `
})
export class ClickableDirectiveExample {
  userData = { id: 1, name: 'John' };
  imageData = { url: 'avatar.jpg' };

  onClick(event: GxClickableEvent) {
    console.log('文字被點擊:', event.data);
  }

  onImageClick(event: GxClickableEvent) {
    console.log('圖片被點擊:', event.data);
  }
}

📖 API 文檔

GxCard

Inputs

| 屬性 | 類型 | 預設值 | 說明 | |------|------|--------|------| | data | IGxCard \| undefined | undefined | 卡片資料(向後兼容模式) | | variant | GxCardVariant \| undefined | 'elevated' | 卡片變體(elevated, outlined, flat) | | layout | GxCardLayout \| undefined | 'grid' | 佈局方式(grid, single, masonry) | | shape | GxCardShape \| undefined | 'classic' | 卡片形狀(classic, square, landscape, portrait, custom) | | clickable | boolean | false | 是否可點擊 | | headerClickable | boolean \| {avatar?: boolean; title?: boolean; subtitle?: boolean} | false | Header 點擊配置 | | colors | IGxCardColors \| undefined | undefined | 自定義顏色配置 |

Outputs

| 事件 | 類型 | 說明 | |------|------|------| | actions | EventEmitter<GxAction> | 動作按鈕點擊事件 | | cardClick | EventEmitter<MouseEvent> | 卡片點擊事件 | | tagClick | EventEmitter<{tag: IGxTag, event: MouseEvent}> | 標籤點擊事件 | | headerItemClick | EventEmitter<{part: string, value: any, event: MouseEvent}> | Header 子項點擊事件 |

GxCardHeader

Inputs

| 屬性 | 類型 | 預設值 | 說明 | |------|------|--------|------| | data | IGxCardHeader \| undefined | undefined | Header 資料 | | avatar | GxMedia \| undefined | undefined | 頭像圖片 | | title | string \| undefined | undefined | 標題 | | subtitle | string \| undefined | undefined | 副標題 | | href | string \| undefined | undefined | 連結 URL | | target | '_self' \| '_blank' \| undefined | '_self' | 連結目標 | | headerClickable | boolean \| {avatar?: boolean; title?: boolean; subtitle?: boolean} | false | Header 點擊控制 |

Outputs

| 事件 | 類型 | 說明 | |------|------|------| | headerItemClick | EventEmitter<{part: string, value: any, event: MouseEvent}> | Header 子項點擊事件 |

插槽:

  • 支援 ng-content 自定義 Header 內容

GxCardContent

Inputs

| 屬性 | 類型 | 預設值 | 說明 | |------|------|--------|------| | data | IGxCardContent \| undefined | undefined | Content 資料 | | title | string \| undefined | undefined | 標題 | | subtitle | string \| undefined | undefined | 副標題 | | description | string \| undefined | undefined | 描述文字 | | image | GxMedia \| undefined | undefined | 圖片 | | tags | IGxTag[] \| undefined | undefined | 標籤列表 | | descriptionCollapse | IGxDescriptionCollapse \| undefined | undefined | 描述文字收合配置 |

Outputs

| 事件 | 類型 | 說明 | |------|------|------| | tagClick | EventEmitter<{tag: IGxTag, event: MouseEvent}> | 標籤點擊事件 |

插槽:

  • 支援 ng-content 自定義 Content 內容

GxCardFooter

Inputs

| 屬性 | 類型 | 預設值 | 說明 | |------|------|--------|------| | data | IGxCardFooter \| undefined | undefined | Footer 資料 | | actions | GxAction[] \| undefined | undefined | 動作按鈕列表 | | buttonVariant | 'filled' \| 'outline' \| 'soft' \| 'ghost' \| undefined | 'filled' | 按鈕樣式 | | maxActions | number \| undefined | Infinity | 最大顯示動作數量 |

Outputs

| 事件 | 類型 | 說明 | |------|------|------| | actionClick | EventEmitter<GxAction> | 動作點擊事件 |

插槽:

  • 支援 ng-content 自定義 Footer 內容

GxCardGroup

GxCardGroup 是一個容器組件,用於統一管理多個卡片的樣式。

提供者:

  • 提供 GxCardGroupContext 給子卡片

GxClickableDirective

Inputs

| 屬性 | 類型 | 預設值 | 說明 | |------|------|--------|------| | gxClickableData | T \| undefined | undefined | 要傳遞的資料 | | gxClickableDisabled | boolean | false | 是否禁用點擊 | | gxClickableShowCursor | boolean | true | 是否顯示點擊游標 | | gxClickableClass | string | '' | 自定義樣式類別 | | gxClickableType | 'text' \| 'avatar' \| 'header' \| '' | '' | 預設型別樣式 | | gxClickableHoverClass | string | 'gx-clickable-hover' | Hover 樣式類別 |

Outputs

| 事件 | 類型 | 說明 | |------|------|------| | gxClick | EventEmitter<GxClickableEvent<T>> | 點擊事件 |

介面定義

IGxCard

interface IGxCard {
  id?: string;
  variant?: GxCardVariant;
  header?: IGxCardHeader;
  content: IGxCardContent;
  footer?: IGxCardFooter;
  shape?: GxCardShape;
  href?: string;
  target?: '_self' | '_blank';
}

IGxCardHeader

interface IGxCardHeader {
  avatar?: GxMedia;
  title?: string;
  subtitle?: string;
  href?: string;
  target?: '_self' | '_blank';
}

IGxCardContent

interface IGxCardContent {
  title?: string;
  subtitle?: string;
  description?: string;
  descriptionCollapse?: IGxDescriptionCollapse;
  tags?: IGxTag[];
  image?: GxMedia;
}

IGxCardFooter

interface IGxCardFooter {
  actions: GxAction[];
}

GxAction

interface GxAction {
  id: string;
  label: string;
  icon?: string;
  intent?: 'primary' | 'secondary' | 'danger';
  disabled?: boolean;
  href?: string;
  target?: '_self' | '_blank';
}

IGxTag

interface IGxTag {
  id: string;
  label: string;
  intent?: 'info' | 'success' | 'warning' | 'error';
  disabled?: boolean;
  removable?: boolean;
}

IGxDescriptionCollapse

interface IGxDescriptionCollapse {
  enabled: boolean;
  maxLines?: number;        // 預設: 3
  fontSize?: number;        // 預設: 14
  lineHeight?: number;      // 預設: 20
  expandText?: string;      // 預設: '展開更多'
  collapseText?: string;    // 預設: '收起內容'
}

IGxCardColors

interface IGxCardColors {
  background?: string;
  textColor?: string;
  borderColor?: string;
  titleColor?: string;
  subtitleColor?: string;
  hoverBackground?: string;
}

GxMedia

type GxMedia = {
  src: string;
  alt?: string;
  ratio?: string;  // 例如: '1:1', '16:9', '3:4', 'auto'
};

🎨 樣式自定義

卡片容器樣式

:root {
  /* 卡片背景 */
  --gx-card-background: #ffffff;
  --gx-card-background-hover: #f9fafb;

  /* 卡片陰影 */
  --gx-card-shadow-elevated: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
  --gx-card-shadow-hover: 0 4px 6px -1px rgba(0, 0, 0, 0.1);

  /* 卡片邊框 */
  --gx-card-border-color: #e5e7eb;
  --gx-card-border-width: 1px;
  --gx-card-border-radius: 0.5rem;

  /* 卡片間距 */
  --gx-card-padding: 1rem;
}

文字顏色

:root {
  /* 標題顏色 */
  --gx-card-title-color: #111827;
  --gx-card-title-font-size: 1.125rem;
  --gx-card-title-font-weight: 600;

  /* 副標題顏色 */
  --gx-card-subtitle-color: #6b7280;
  --gx-card-subtitle-font-size: 0.875rem;

  /* 內容文字 */
  --gx-card-text-color: #374151;
  --gx-card-description-color: #4b5563;
}

Header 樣式

:root {
  /* Header 背景 */
  --gx-card-header-bg: transparent;
  --gx-card-header-padding: 1rem;

  /* Avatar */
  --gx-card-avatar-size: 2.5rem;
  --gx-card-avatar-border-radius: 50%;
}

Footer 樣式

:root {
  /* Footer 背景 */
  --gx-card-footer-bg: transparent;
  --gx-card-footer-padding: 1rem;
  --gx-card-footer-border-top: 1px solid #e5e7eb;

  /* 按鈕間距 */
  --gx-card-actions-gap: 0.5rem;
}

🔄 技術實現

本組件庫採用 Angular 18+ 的 Standalone Components 架構,提供現代化的卡片解決方案。

組件架構

| 組件名稱 | 功能說明 | |---------|---------| | GxCard | 卡片容器,管理整體佈局與樣式 | | GxCardHeader | 卡片標頭,顯示頭像、標題、副標題 | | GxCardContent | 卡片內容,支援圖片、標籤、描述文字 | | GxCardFooter | 卡片底部,提供動作按鈕 | | GxCardGroup | 卡片群組,統一管理多個卡片 | | GxClickableDirective | 可點擊指令,提供統一的點擊互動 | | GxCardConfigService | 全局配置服務 | | HeightMeasurementService | 高度測量服務 |

核心特點

  • 響應式狀態管理: 完全使用 Angular Signals,提供高效能的資料響應
  • 靈活的佈局系統: 支援 Grid、Single、Masonry 多種佈局
  • 多樣的卡片形狀: Classic、Square、Landscape、Portrait、Custom
  • 豐富的互動功能: 卡片點擊、Header 點擊、標籤點擊、動作按鈕
  • 智能文字收合: 自動測量文字高度,提供展開/收起功能
  • 完整的樣式系統: CSS 變數與自定義類別雙重支援
  • 無障礙支援: 遵循 ARIA 標準,提供良好的可訪問性
  • 高度可客製化: 支援 ng-content 投影與資料模式雙重使用方式

使用場景

  • 產品列表與卡片展示
  • 儀表板與資訊卡片
  • 部落格文章列表
  • 使用者個人資料卡
  • 社群媒體貼文
  • 任何需要卡片佈局的場景

🛠️ 開發

# 安裝依賴
cd packages/gx-card
npm install

# 構建
npm run build

# 查看生成的文件
ls -la ../../dist/gx-card

🤝 貢獻

歡迎提交 Issue 與 Pull Request 來改進組件功能!

📄 授權

MIT