---
# System prepended metadata

title: cdt-fp-component

---

# cdt-fp-component
一個基於 IBM Carbon-Design, Centanet 和 Find-Property (app 6.0 base) 的 Vue component liberary.(元件庫)



- [cdt-fp-component](#cdt-fp-component)
  - [簡介 Introduction](#簡介-introduction)
      - [這個元件庫要求](#這個元件庫要求)
      - [這元件庫**並不會**包括 :](#這元件庫並不會包括-)
  - [項目設定 | Project setting](#項目設定--project-setting)
    - [項目開發依靠套件 | Project Depandance](#項目開發依靠套件--project-depandance)
    - [項目開發文件目錄 | Project Directory](#項目開發文件目錄--project-directory)
    - [安裝開發環境 | Project Setup](#安裝開發環境--project-setup)
    - [開始 Storybook 開發之旅! | Run the storybook](#開始-storybook-開發之旅--run-the-storybook)
    - [建立 Storybook 靜態示範文檔(Web App) | Build the storybook static sample](#建立-storybook-靜態示範文檔web-app--build-the-storybook-static-sample)
    - [生成元件的 document json / *.stories.js 文件 | Generate the storybook stories](#生成元件的-document-json--storiesjs-文件--generate-the-storybook-stories)
    - [打包成dist | build as package](#打包成dist--build-as-package)
    - [最後, 打包成 npm package | Finally, pack up](#最後-打包成-npm-package--finally-pack-up)
    - [好啊, 那麼我怎麼用這個酷東西? | how to use package ?](#好啊-那麼我怎麼用這個酷東西--how-to-use-package-)
  - [如何去維護這項目 | how to contribute](#如何去維護這項目--how-to-contribute)
    - [Styles](#styles)
    - [Script](#script)
    - [Vue](#vue)
      - [Template](#template)
      - [Script](#script-1)
      - [Style](#style)
  - [NO CSS MODULE, NO SCOPED CSS, THANK YOU!](#no-css-module-no-scoped-css-thank-you)
- [TODO:](#todo)
  - [0. 問設計組分工 ask Designer for artboard reference](#0-問設計組分工-ask-designer-for-artboard-reference)
  - [1. 用最簡單的方式, 去加入官方的Storybook示範 | implement from offical example, with simplest way code](#1-用最簡單的方式-去加入官方的storybook示範--implement-from-offical-example-with-simplest-way-code)
  - [2. 加入 Centanet 和 Find-Property 的元件 | implement more from Centanet / Findproperty project!](#2-加入-centanet-和-find-property-的元件--implement-more-from-centanet--findproperty-project)
  - [3. 加入 Carbon-Chart / D3 包 implement from offical D3.js package](#3-加入-carbon-chart--d3-包-implement-from-offical-d3js-package)
  - [4. 做一個簡單的 Web-app 去示範! | build a new exist project, with this lovely package](#4-做一個簡單的-web-app-去示範--build-a-new-exist-project-with-this-lovely-package)
  - [5. Rollup Bundler : need to review package export layout](#5-rollup-bundler--need-to-review-package-export-layout)
  - [6. SCSS : 參數要分離](#6-scss--參數要分離)

## 簡介 Introduction
基於既有的項目,例如 Centanet, Find-property, Estate 等項目,有不少有共通的元件,
然而有不少情況, 例如新頁面更新(CCL,地圖搵樓等等), 而導致元件在各個項目出現不同.
加上, 有不少元件庫(沒錯,就是`element-ui` 和 `Vent`)上 CSS/JS 的寫法不夠彈性和不夠全面, 
而令部分功能要加額外類同的dependance package(封包?)

此外, 也跟據*設計組(Henry)* 和 *前人(Jason的[vuecomlib](http://10.29.205.33/GitServer/Repository/Tree/b840aa88-53d6-4512-b3cf-3f6f831a0a2a))* 的經驗和期望, 

#### 這個元件庫要求 ####

1. 原子設計方法論:

   1. 原子級元件 Atomic Component: 
      1. 會以基於IBM [Carbon-Design](https://vue.carbondesignsystem.com) 元件庫項目為主
      2. 如果有功能上不足, 會以[Element-ui](https://element.eleme.io/#/zh-CN) **樣式**為副, (CSS/JS也多多把關,拜託!)

   2. 分子級元件 Molecules Component / Composed Component:
      1. 會以基於IBM [Carbon-Design](https://vue.carbondesignsystem.com) 元件庫為主
      2. 也包括既有的設計,例如: Find-property 的 Card-Property

   3. 組織級元件 Organisms Component:
      1. 既有的設計, 例如: Centanet 的 Header / Footer

   4. Layout Helper:
      1. e.g. container, row, column, guid

2. 設計系統 Design System:
   1. 變數 Variable (會以SCSS的 variable 去修定):
      1. 顏色/色票 color token
      2. 字體/排版 font/typography
      3. 大小/間隔 size and spacing
   2. 素材庫 Pattern Library:
      1. 元件互動 (e.g. hover/active/disable)


3. 元件之間的互動 animation and reaction 
   1. e.g. Dialog Open / Close
   
4. 元件範例和文檔 Demo and Documentation
   1. 文檔 Documentation
      1. 元件的參考設計 design reference
      2. 元件的用法和可用參數 component method and properties
   2. 範例 Demo
   3. API reference 
      1. Typed defined / JS Doc
      2. Vetur file (attributes.json / tags.json)
      3. VS-code code-snippet

#### 這元件庫**並不會**包括 : ####

1. 模板級元件 Templates Component 
   1. 留給Vue web-app 的 Component 去做啊, 頁面排版在這元件庫搞的話就會變成同時做幾個項目啊?
   2. e.g. 特定的 Section tag

2. 頁面級元件 Page Component
   1. 不解釋了 🤣


頁面和模板會用另一個 Demo Project 去示範

---

## 項目設定 | Project setting ##

### 項目開發依靠套件 | Project Depandance ###
- Carbon-Design 系列
  - 基於[Carbon-Design-Vue](https://github.com/carbon-design-system/carbon-components-vue) 的最低要求和SCSS 的依靠套件
    - [`@carbon/colors`](https://www.npmjs.com/package/@carbon/colors)
    - [`@carbon/elements`](https://www.npmjs.com/package/@carbon/elements)
    - [`@carbon/icons-vue`](https://www.npmjs.com/package/@carbon/icons-vue)
    - [`@carbon/layout`](https://www.npmjs.com/package/@carbon/layout)
    - [`@carbon/themes`](https://www.npmjs.com/package/@carbon/themes)
    - [`@carbon/type`](https://www.npmjs.com/package/@carbon/type)
  - 減少原有[`carbon-components-vue`](https://github.com/carbon-design-system/carbon-components-vue)的依靠套件:
    - 不利於component liberary (本身是[`carbon-components`](https://github.com/carbon-design-system/carbon/tree/main/packages/components) 的延伸, SCSS的部分是在[`carbon-components`](https://github.com/carbon-design-system/carbon/tree/main/packages/components)implement)

- Vue.js framework (v2.x)
  - 就, 我們用Nuxt.js (v2.x) 啊?
  - 對應Vue@v3.x, 要額外開branch去處理 (vue3-base)

- Storybook  
  - 文檔示範, 可以好好地示範你的酷東西~
  - 有了 playground , 可以示範你的酷東西怎樣用
  - Case by case, 可以示範元件在不同環境的用例(use case)

- Rollup
  - rollup 有 Vue component (v2.x & v3.x)的支持
  - 可以獨立生成 ES Module 和 Common JS 的版本
   
- Typescript
  - typescript is just for advance, just give it try 


### 項目開發文件目錄 | Project Directory ###
```bash

-- [src]                      (這項目主要更改的地方)
    | 
    |-- [assets] 
    |   |
    |   |-- [images]          (圖片/icon的svg)
    |   | 
    |   |-- [styles]          (SCSS / CSS 檔)
    |   |   |
    |   |   |-- [carbon-base] (原有 Carbon Component 的 Variable SCSS檔)
    |   |   |
    |   |   |-- [carbon-base] (原有 Carbon Component 的 Component SCSS檔)
    |   |   |
    |   |   |-- index.scss    (SCSS 的置入檔)
    |   |
    |   |-- [js]              (常用的JS?)
    |
    |-- [components]          (元件列)
    |   |
    |   |-- [(component-name)]
    |       |
    |       |-- [_doc]                      (元件文檔, vetur-tag,vetur-attrubute, vs-code等等)
    |       |
    |       |-- component-name.vue          (元件的Vue檔)
    |       |
    |       |-- _(ComponentName).stories.js (元件Storybook文檔)
    |       |
    |       |-- index.js                    (元件的置入檔)
    |       | 
    |       |-- component-name-notes.md     (元件的官方文檔)
    |
    |-- [directives]          (Vue 元件的 directive, 官方配置的)
    | 
    |-- [mixins]              (Vue 元件的 mixin, 官方配置的)
    |
    |-- [public]              (vue-cli 生成的, 先不要動, 謝謝)
    |
    |-- [store]               (Vuex, 先不要動, 謝謝)
    |
    |-- [types]               (typescript 文檔)
    |
    |-- App.vue               (vue-cli 生成的, 先不要動, 謝謝)
    |
    |-- main.ts               (vue-cli 生成的, 先不要動, 謝謝)

-- [public]                   (vue-cli 生成的, 先不要動, 謝謝)

-- [artboard]                 (由 Adobe XD 生成的文檔圖片)

-- [build]                    (develop usage, 一切 rollup / document generating 的js)

-- [config]                   (develop usage config, 一切 Storybook的設定)

-- [doc]                      (Storybook Component 以外的文件文檔)
    |
    |-- *.stories.mdx

-- [storybook_offical_doc]    (Carbon-Design-Vue 的原有storybook)
    |
    |-- [stories]             (Carbon-Design-Vue 原有的元件庫storybook文檔)
    |
    |-- [storybook-static]    (Carbon-Design-Vue 原有的storybook文檔 (web app, 可以直接放在http server 去檢視))

-- [tests]                     (元件的test-case, ?有時間再研究test-case / E2E testing)


```

### 安裝開發環境 | Project Setup ###

**記得! 先看看自己的node 版本是不是不同的!**

因為 node-sass 的關係,必要時請自行更改 node-sass 的版本
> see also : [node-sass](https://www.npmjs.com/package/node-sass)


| software | version |
| -------- | ------- |
| node     | 12.X    |
| python   | 2.7.18  |


```bash
yarn install
# or use npm
npm i
```

### 開始 Storybook 開發之旅! | Run the storybook ###

```
yarn dev:sb
```


### 建立 Storybook 靜態示範文檔(Web App) | Build the storybook static sample ###

```
yarn storybook:build
```


### 生成元件的 document json / *.stories.js 文件 | Generate the storybook stories ###
```
yarn doc-gen
```

> 這功能是自家寫的, 小心一點, 還在實驗性質!

它會:
1. 根據 Vue元件的directory生成 vetur attributes (`_doc/component-name.attributes.json`) 和 tags  (`_doc/component-name.tags.json`)
2. 根據 Vue元件的directory生成 vs-code code-snippet (`_doc/component-name.code-snippet`) 和 tags  (`_doc/component-name.tags.json`)
3. 根據 Vue元件生成 storybook 用例 (`./_ComponentName.stories.js`) 

> 之後, 可能要改改 
> 
> `yarn doc-gen [component-name]`

### 打包成dist | build as package ###
```bash
yarn ru:build
# or use npm 
npm run ru:build
```
> 嘿, 我還未寫好啊
> 
> see also: [#todo](#todo)

這個指令會:
1. rollup bundler 根據元件的置入檔(`src/components/{component-name}/index.js`), 生成 ESM module (`dist/esm`) 和 common-js (`dist/cjs`) 的版本
2. rollup bundler 根據 assets 的 styles (`src/assets/styles`), 生成可再用的 SCSS  (`dist/scss`) 和 CSS (`dist/css`) 的版本
3. rollup bundler 根據 assets 的 js (`src/assets/js`), 生成可再用的 JS util libs (`dist/js/util.js`)
4. rollup bundler 根據 `src/components/{component-name}/_doc`, 生成vetur attributes.json and tags.json (`dist/vetur/attributes.json` , `dist/vetur/tags.json`), 和vs-code code-snippet  (`dist/vscode/cdt.code-snippet`)

如沒意外, `dist` 會這樣子:

```bash
-- [dist]
   |
   |-- [esm]
   |   |
   |   |-- [{components}]
   |   |   |
   |   |   |-- index.js
   |   |
   |   |-- index.js
   |
   |-- [cjs]
   |   | 
   |   |-- [{components}]
   |   |
   |   |-- index.js
   |
   |-- [scss]
   |   |
   |   |-- [carbon-base]
   |   |
   |   |-- [carbon-component]
   |   |
   |   |-- [find-property/others-based]
   |   |
   |   |-- index.scss
   |
   |-- [css]
   |   |
   |   |-- [carbon-component]
   |   |   |
   |   |   |-- [component-name].css
   |   |   |
   |   |   |-- [component-name].min.css
   |   |
   |   |-- [find-property/others-based]
   |   |   |
   |   |   |-- [component-name].css
   |   |   |
   |   |   |-- [component-name].min.css
   |   |
   |   |-- cdt-fp-component.css
   |   |
   |   |-- cdt-fp-component.min.css
   |
   |-- [vetur]
   |   | 
   |   |-- aattributes.json
   |   | 
   |   |-- tags.json
   |
   |-- [vscode]
   |   |
   |   |-- cdt.code-snippets

```


### 最後, 打包成 npm package | Finally, pack up ###
```bash
yarn pack 
# or , use npm
npm pack
```



---
### 好啊, 那麼我怎麼用這個酷東西? | how to use package ? ###

不建議用 git repo url 作為 package reference, 這樣會吃掉 node_package 

先 npm pack 了 package, 再 npm install

```bash
npm i package-name.tgz
```

有待研究, 畢竟要是這樣更新, 就真的被人說"這個deploy好麻煩啊"


--- 
## 如何去維護這項目 | how to contribute ##

### Styles ###

因為這項目主要是可持續維護原子級元件和分子級元件,也是可以用其他架構(framework)可以重建! 所以!請儘量把 SCSS (pre-compile css) 分拆出來寫, 不要混在 Vue 當成 SFC (single-file-component) 去寫! 
(試想想:之後改用Vue v3, 或者用 React, 重寫的工程會大大加重大家的工作量, 所以! 分開!)

this package is containing for the atomic-level and molecules-level components.  Therefore, Think it can impletment without Vue framework! Try to write the SCSS file into seperate file instead of SFC format.

1. 善用variable 
2. No Scoped!
3. 儘量保持 4個 Sector level 內 !
4. 儘管不要用 `>` 和 element 去做reference
5. 命名方法用[BEM方式](https://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/)
```scss
.a {
   .b{
      .c{
         .d{
            // is ok
         }

         &:hover{
            .d {
               // not ok 
            }
         }

         span {
            // not ok ?
         }
      }
   }
}

.a {
   .b {
      .c {
         .d{
            .e{
               // 太長了吧
            }
         }
      }
   }
}

.a .b .c .d {
   .e{
      // 呃, 也不要這樣
   }
}
```

> 可能有StyleLint可以幫忙, 之後加stylelint 
> 
> StyleLint (https://stylelint.io/user-guide/rules/list/max-nesting-depth/)

refenece: https://sass-guidelin.es/


### Script ###

除了 carbon-design 原裝 JS, ~~hmm, 也可以用typescript吧?~~ 

Javascript linting rule 可以之後決定, 但原則上:
1. 要簡單易明, 
2. 就算寫 lodash chaining, 也麻煩寫一寫預計結果! 
   e.g. 
   ```javascript
   // 就算寫 lodash chaining, 也麻煩寫一寫預計結果! 
   _.(array).map(e=>({a:e.a, b:e.a.b})).reduce(() , [] ,0) // <- 寫完我也不知道我寫什麼
   ```

3. 儘量寫註釋(commit), 用 JsDoc 的原則簡單說明在幹嘛
   e.g.: 
   ```javascript
   /// function 
   /**
    * @func  HelloWorld
    * @description : 給我酷的 hello-world
    * @param {string} name - 你的名字
    * @return {string} - 帥帥的招呼
   */
   function HelloWorld(name){
      return "hello "+ name ;
   }
   ```

### Vue ###

詳細可reference: [Vue-docgen-api](https://vue-styleguidist.github.io/docs/Documenting.html#code-comments)

#### Template ####
1. 是用html tag 
2. 自由! 要給別人自由! 請善用`<slot>`
    e.g.: 
   ```xml
   <template>
      <button
         class="cdt-btn"
         :class="{
            'is-blue' : isblue
         }"
         role="button"
      >
         <slot></slot> <!-- 給別人自由 -->
      </button>
   </template>
   ...
   <!-- 用法 -->
   <awesome-btn> Hello World </awesome-btn>
   ```
3. `<slot>` 也是可以下註明(comment), 也是 JSDoc 規範 (不然doc-gen沒有註解)
   e.g. : 
   ```xml
   <template>
      <button
         class="cdt-btn"
         :class="{
            'is-blue' : isblue
         }"
         role="button"
      >
       <!--
         @slot button content
            @binding {object} icon icon of the menu item
            @binding {string} text text of the menu item
         -->
         <slot name="content" :icon="icon" :text="text"></slot>
      </button>
   </template>
   ...
   <!-- 用法 -->
   <awesome-btn> Hello World </awesome-btn>
   ```


#### Script ####

1. No Fetching function ,不要在元件內加 axios request, 不然就不是"可再用元件"
   1. CLink 是可圈可點的
   2. Buniness logic keep it into Buniness, non of this component liberay's Buniness

2. properties (`props`)
   1. 要寫註明(comment), 也是 JSDoc 規範 (不然doc-gen沒有註解)
   2. options 要加 validator 
   3. type 一定要下
   ```javascript
   export default{
      ...,
      props: {
         /**
          * The type of button theming. <- description
          * @values primary, secondary, tertiary, ghost, danger, danger--ghost, danger--tertiary
          * @example icon="primary"
          * 
          */
         kind: {
            type: String,
            default: "primary",
            // 如果 validator 有 array ,還可以用在 vue-docgen-api
            validator: (val) =>
            [
               "",
               "primary",
               "secondary",
               "tertiary",
               "ghost",
               "danger",
               "danger--ghost",
               "danger--tertiary",
            ].includes(val),
         },
      }, 
   }
   ```
3. event (`$emit`)
   1. 要寫註明(comment), 也是 JSDoc 規範 (不然 doc-gen 沒有註解)
   2. event naming: camelCase, with `on`
  
  ```javascript
  export default{
    ...,
    methods: {
      sothing_is_trigger(event, item){
      /**
         * Success event.
         *
         * @event success
         * @property {number} newValue new value set
         * @property {number} oldValue value that was set before the change
         */
        let old_val = item.copy();
        let new_val  = item.made_sothing();
        this.$emit('onEventTriggered' , event, new_val, old_val );
      }
    },
  }
  ```
  如果是在 template 裡發生, 也是可以在template 裡註解:
  ```xml
  <div>
    <!--
      triggered on click
      @event click
      @property {object} demo - example
      @property {number} called - test called
    -->
    <button @click="$emit('click', test)"></button>
  </div>
  ```
  
4. Others: 
  Directive : 
    1. business logic  (e.g. : google GA ) : keep it separate , separate as library  
    2. component related (e.g.: click outside for exit): put the function file in `src/directive`

#### Style ####

one rule for all vue file style block: `NO (prerender) STYLE IN THE VUE FILE`

NO CSS MODULE, NO SCOPED CSS, THANK YOU!
---

# TODO: #

## 0. 問設計組分工 ask Designer for artboard reference ##

香港設計同事 (Cece) 在幫手把 Carbon-Design V11 和 App v6.0 的設計進行整合, 有可能 Artboard 的資料會跟 adobe XD Link 有不同步的問題;

雖然是正常現象, 但在前端和設計之間的溝通, 可能要用 Trello 的方式去記錄/分工處理


## 1. 用最簡單的方式, 去加入官方的Storybook示範 | implement from offical example, with simplest way code ##

雖然官方 Storybook 示範可算是易懂的, 但不太容易去修護, 
所以:
  1. 會加 "文件產生" 的指令 (`doc-gen`)
  2. 會保留原有的官方元件文件 (component-name-note.md)
  3. 會在每個元件裡加上 change-log.md 文件去更新


## 2. 加入 Centanet 和 Find-Property 的元件 | implement more from Centanet / Findproperty project!

因這項目其中一個重點是減少重複的組織級元件 (e.g.: Header/ Footer)

  1. 找回有關的設計文件, 加入design spec; add the design reference document for it, add the reference XD link
  2. 儘量用 Carbon-Design 的元件去重組組織級元件, 而不是"複製和貼上"原有的程式碼 


## 3. 加入 Carbon-Chart / D3 包 implement from offical D3.js package 

可以後期再加, 因為官方的[Carbon-Chart-Vue](https://carbon-design-system.github.io/carbon-charts/vue/) 出現不少問題, 
加上原有 Centanet 有關圖表的設計也固定用了 Apache ECharts, 所以可以之後加

need to get impletment, 
  1. testing how to use Carbon-Chart based D3.js 
  2. custom D3 component to be similar to echart

## 4. 做一個簡單的 Web-app 去示範! | build a new exist project, with this lovely package

## 5. Rollup Bundler : need to review package export layout

## 6. SCSS : 參數要分離

儘量把元件的數值參數儘量分離,
例如: `src/assets/styles/carbon-component/button/_mixin.scss`

雖然carbon-design 是把參數放左`src/assets/styles/carbon-base/scss/_var.scss`,

但我希望可以把參數放在檔案裡面的前面, 方便大家直接改
``` scss
/// 之前 
@mixin button-base {
  @include reset;
  @include type-style("body-short-01");

  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: space-between;
  max-width: rem(320px);
  min-height: $button-height;
  // Fix to remove added margins on buttons in safari (see #5155)
  margin: 0;
  padding: $button-padding;
  text-align: left;
  text-decoration: none;
  vertical-align: top;
  border-radius: $button-border-radius;
  outline: none;
  cursor: pointer;
  transition: background $duration--fast-01 motion(entrance, productive),
    box-shadow $duration--fast-01 motion(entrance, productive),
    border-color $duration--fast-01 motion(entrance, productive),
    outline $duration--fast-01 motion(entrance, productive);

  &:disabled,
  &:hover:disabled,
  &:focus:disabled,
  &.#{$prefix}--btn--disabled,
  &.#{$prefix}--btn--disabled:hover,
  &.#{$prefix}--btn--disabled:focus {
    color: $disabled-03;
    background: $disabled-02;
    border-color: $disabled-02;
    box-shadow: none;
    cursor: not-allowed;
  }

  .#{$prefix}--btn__icon {
    position: absolute;
    right: rem(16px);
    flex-shrink: 0;
    width: rem(16px);
    height: rem(16px);
  }
}

```


``` scss
/// 之後

// 數字參數抽出來
$button--max-width :                rem(320px) !default; 
$button--min-height:                $button-height !default; 
$button--border-radius:             $button-border-radius !default;
$button--animation-time:            $duration--fast-01 motion(entrance, productive) !default;

$button--disable--text-color:       $disabled-03 !default;
$button--disable--background-color: $disabled-02 !default;

$button--icon--size:                rem(16px) !default;

@mixin button-base {
  @include reset;
  @include type-style("body-short-01");

  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: space-between;
  max-width: $button--max-width;
  min-height: $button--min-height;
  // Fix to remove added margins on buttons in safari (see #5155)
  margin: 0;
  padding: $button-padding;
  text-align: left;
  text-decoration: none;
  vertical-align: top;
  border-radius: $button--border-radius;
  outline: none;
  cursor: pointer;
  transition: background $button--animation-time,
    box-shadow $button--animation-time,
    border-color $button--animation-time,
    outline $button--animation-time;

  &:disabled,
  &:hover:disabled,
  &:focus:disabled,
  &.#{$prefix}--btn--disabled,
  &.#{$prefix}--btn--disabled:hover,
  &.#{$prefix}--btn--disabled:focus {
    color: $button--disable--text-color;
    background: $button--disable--background-color;
    border-color: $button--disable--background-color;
    box-shadow: none;
    cursor: not-allowed;
  }

  .#{$prefix}--btn__icon {
    position: absolute;
    right: $button--icon--size;
    flex-shrink: 0;
    width: $button--icon--size;
    height: $button--icon--size;
  }
}

```




## 7. Carbon-Design Vue : 移走官方 deprecated 的程式碼