snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Code scanning - action / CodeQL-Build (push) Failing after 2s
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,422 @@
# Change Log
## 1.1.0
### Minor Changes
- ([#39858](https://github.com/RocketChat/Rocket.Chat/pull/39858)) Adds support to room information on ViewSubmit and ViewClose events for ContextualBar surface
### Patch Changes
- ([#39858](https://github.com/RocketChat/Rocket.Chat/pull/39858)) Fixes an issue that prevented BlockAction interactions from having room information when triggered in a ContextualBar surface
## 1.1.0-rc.0
### Minor Changes
- ([#39858](https://github.com/RocketChat/Rocket.Chat/pull/39858)) Adds support to room information on ViewSubmit and ViewClose events for ContextualBar surface
### Patch Changes
- ([#39858](https://github.com/RocketChat/Rocket.Chat/pull/39858)) Fixes an issue that prevented BlockAction interactions from having room information when triggered in a ContextualBar surface
## 1.0.0
### Major Changes
- ([#39268](https://github.com/RocketChat/Rocket.Chat/pull/39268)) refactor(ui-kit): Remove UiKit deprecations
### Patch Changes
- ([#38989](https://github.com/RocketChat/Rocket.Chat/pull/38989)) chore(eslint): Upgrades ESLint and its configuration
## 1.0.0-rc.0
### Major Changes
- ([#39268](https://github.com/RocketChat/Rocket.Chat/pull/39268)) refactor(ui-kit): Remove UiKit deprecations
### Patch Changes
- ([#38989](https://github.com/RocketChat/Rocket.Chat/pull/38989)) chore(eslint): Upgrades ESLint and its configuration
## 0.39.0
### Minor Changes
- ([#37726](https://github.com/RocketChat/Rocket.Chat/pull/37726)) Introduces a new variation of the `Icon` element to `ui-kit` through the new `framed` optional property.
## 0.39.0-rc.0
### Minor Changes
- ([#37726](https://github.com/RocketChat/Rocket.Chat/pull/37726)) Introduces a new variation of the `Icon` element to `ui-kit` through the new `framed` optional property.
## 0.38.0
### Minor Changes
- ([#37398](https://github.com/RocketChat/Rocket.Chat/pull/37398)) Adds new elements for building apps (ui-kit): `icon` (block element), `icon_button` (block element) and `info_card` (layout block).
## 0.38.0-rc.0
### Minor Changes
- ([#37398](https://github.com/RocketChat/Rocket.Chat/pull/37398)) Adds new elements for building apps (ui-kit): `icon` (block element), `icon_button` (block element) and `info_card` (layout block).
## 0.37.0
### Minor Changes
- ([#33528](https://github.com/RocketChat/Rocket.Chat/pull/33528)) Adds `accessory` properties to `CalloutBlock`
- ([#33066](https://github.com/RocketChat/Rocket.Chat/pull/33066)) Introduces new property `category` for Rocket.Chat Apps to register UI action buttons. This property is used to group buttons in the UI.
## 0.37.0-rc.0
### Minor Changes
- ([#33528](https://github.com/RocketChat/Rocket.Chat/pull/33528)) Adds `accessory` properties to `CalloutBlock`
- ([#33066](https://github.com/RocketChat/Rocket.Chat/pull/33066)) Introduces new property `category` for Rocket.Chat Apps to register UI action buttons. This property is used to group buttons in the UI.
## 0.36.1
### Patch Changes
- ([#33000](https://github.com/RocketChat/Rocket.Chat/pull/33000)) fix UiKit error message: Failed to resolve module: @rocket.chat/icons
## 0.36.1-rc.0
### Patch Changes
- ([#33000](https://github.com/RocketChat/Rocket.Chat/pull/33000)) fix UiKit error message: Failed to resolve module: @rocket.chat/icons
## 0.36.0
### Minor Changes
- ([#32793](https://github.com/RocketChat/Rocket.Chat/pull/32793)) New Feature: Video Conference Persistent Chat.
This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat.
## 0.36.0-rc.0
### Minor Changes
- ([#32793](https://github.com/RocketChat/Rocket.Chat/pull/32793)) New Feature: Video Conference Persistent Chat.
This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat.
## 0.35.0
### Minor Changes
- ([#31455](https://github.com/RocketChat/Rocket.Chat/pull/31455)) Introduced new elements for apps to select users
- ([#31821](https://github.com/RocketChat/Rocket.Chat/pull/31821)) New runtime for apps in the Apps-Engine based on the Deno platform
## 0.35.0-rc.0
### Minor Changes
- ([#31455](https://github.com/RocketChat/Rocket.Chat/pull/31455)) Introduced new elements for apps to select users
- ([#31821](https://github.com/RocketChat/Rocket.Chat/pull/31821)) New runtime for apps in the Apps-Engine based on the Deno platform
## 0.34.0
### Minor Changes
- ([#31918](https://github.com/RocketChat/Rocket.Chat/pull/31918)) Introduced new elements for apps to select channels
## 0.34.0-rc.0
### Minor Changes
- ([#31918](https://github.com/RocketChat/Rocket.Chat/pull/31918)) Introduced new elements for apps to select channels
## 0.33.0
### Minor Changes
- ([#31138](https://github.com/RocketChat/Rocket.Chat/pull/31138)) feat(uikit): Move `@rocket.chat/ui-kit` package to the main monorepo
## 0.33.0-rc.0
### Minor Changes
- b223cbde14: feat(uikit): Move `@rocket.chat/ui-kit` package to the main monorepo
## 0.32.1
### Patch Changes
- [`a97c7f086`](https://github.com/RocketChat/fuselage/commit/a97c7f08633e6e36a39c8933b530f9acacb83af0) Thanks [@dougfabris](https://github.com/dougfabris)! - chore: resolve-workspace-deps to publish pkg versions
## 0.32.0
### Minor Changes
- [`a04f9089d`](https://github.com/RocketChat/fuselage/commit/a04f9089d19ba32d2b2e80738d2d5f4dac0d3e0c) Thanks [@dougfabris](https://github.com/dougfabris)! - fix(ui-kit): Remove icons package dependency in ui-kit package
- [`a04f9089d`](https://github.com/RocketChat/fuselage/commit/a04f9089d19ba32d2b2e80738d2d5f4dac0d3e0c) Thanks [@dougfabris](https://github.com/dougfabris)! - refactor(ui-kit): Review some types
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [0.31.0](https://github.com/RocketChat/fuselage/compare/v0.30.1...v0.31.0) (2021-12-28)
### Bug Fixes
- **ui-kit:** Fix `SurfaceRendererPayload` ([#556](https://github.com/RocketChat/fuselage/issues/556)) ([8d76a0d](https://github.com/RocketChat/fuselage/commit/8d76a0dadab5798afba1d6ce3911aa974d33c103))
### Features
- Message Preview ([#587](https://github.com/RocketChat/fuselage/issues/587)) ([e69dad3](https://github.com/RocketChat/fuselage/commit/e69dad3a6619e98ff70bcd1cb68567a159187336))
- New hooks for element size tracking ([#413](https://github.com/RocketChat/fuselage/issues/413)) ([8ca682c](https://github.com/RocketChat/fuselage/commit/8ca682c636d2e4813f7d346cb881513382be63cf))
## [0.30.1](https://github.com/RocketChat/fuselage/compare/v0.30.0...v0.30.1) (2021-10-20)
### Bug Fixes
- **ui-kit:** Fix `SurfaceRendererPayload` ([#556](https://github.com/RocketChat/fuselage/issues/556)) ([0508689](https://github.com/RocketChat/fuselage/commit/05086895e0f2ea26c7a00856a5e0c6fcffbc4615))
# [0.30.0](https://github.com/RocketChat/fuselage/compare/v0.29.0...v0.30.0) (2021-10-06)
### Bug Fixes
- **jest:** Adjust jest and ts-jest dependencies ([#547](https://github.com/RocketChat/fuselage/issues/547)) ([91a4fa1](https://github.com/RocketChat/fuselage/commit/91a4fa1365394001afe1bd46480bda3bafed5505))
# [0.29.0](https://github.com/RocketChat/fuselage/compare/v0.28.0...v0.29.0) (2021-08-31)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.28.0](https://github.com/RocketChat/fuselage/compare/v0.27.0...v0.28.0) (2021-07-30)
### Bug Fixes
- **fuselage-ui-kit:** Implements missing url prop for buttons ([#488](https://github.com/RocketChat/fuselage/issues/488)) ([bb19344](https://github.com/RocketChat/fuselage/commit/bb193441804c9b20174e2586d22c4b2845a486c3))
### Features
- **onboarding-ui:** Administrator information form and Organization information form ([#489](https://github.com/RocketChat/fuselage/issues/489)) ([b289f68](https://github.com/RocketChat/fuselage/commit/b289f68676954b91c792d8d97680314178bf2c60))
- styled API; monorepo grooming ([#482](https://github.com/RocketChat/fuselage/issues/482)) ([1b6b70c](https://github.com/RocketChat/fuselage/commit/1b6b70cf67ec16927b1566adc2350295a8927223))
# [0.27.0](https://github.com/RocketChat/fuselage/compare/v0.26.0...v0.27.0) (2021-06-28)
### Features
- **ui-kit:** Attachment Surface ([#474](https://github.com/RocketChat/fuselage/issues/474)) ([63bb5ea](https://github.com/RocketChat/fuselage/commit/63bb5ea044afc768295a50e0155406df6091b840))
- ui-kit-unified ([#392](https://github.com/RocketChat/fuselage/issues/392)) ([ce48ca9](https://github.com/RocketChat/fuselage/commit/ce48ca9d9806283bba8be5df7c29c7aa8c1e716f))
# [0.26.0](https://github.com/RocketChat/fuselage/compare/v0.25.0...v0.26.0) (2021-05-28)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.25.0](https://github.com/RocketChat/fuselage/compare/v0.24.0...v0.25.0) (2021-05-19)
### Features
- [@rocket](https://github.com/rocket).chat/message-parser ([#443](https://github.com/RocketChat/fuselage/issues/443)) ([4722cdf](https://github.com/RocketChat/fuselage/commit/4722cdff46f5987f335d989be59649c7652bb12a))
# [0.24.0](https://github.com/RocketChat/fuselage/compare/v0.23.0...v0.24.0) (2021-04-28)
### Features
- [@rocket](https://github.com/rocket).chat/string-helpers ([#431](https://github.com/RocketChat/fuselage/issues/431)) ([2509d6a](https://github.com/RocketChat/fuselage/commit/2509d6acdbe5ec8b216e8d4430373797c5f5dfe2))
# [0.23.0](https://github.com/RocketChat/fuselage/compare/v0.22.0...v0.23.0) (2021-04-01)
### Bug Fixes
- **npm:** Wrong paths in "files" field of package.json ([6d3c811](https://github.com/RocketChat/fuselage/commit/6d3c811f6fd747de7f47aff145902d88476272ee))
# [0.22.0](https://github.com/RocketChat/fuselage/compare/v0.21.0...v0.22.0) (2021-02-26)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.21.0](https://github.com/RocketChat/fuselage/compare/v0.20.3...v0.21.0) (2021-01-31)
### Features
- Built modules for design tokens ([#356](https://github.com/RocketChat/fuselage/issues/356)) ([f9c3449](https://github.com/RocketChat/fuselage/commit/f9c344953b8161a4385cab3a3dcc8b6a7210446f))
- linear_scale element ([#365](https://github.com/RocketChat/fuselage/issues/365)) ([43a4c54](https://github.com/RocketChat/fuselage/commit/43a4c54ed10d096ef2259ddcd30c3bbd97ae866a))
## [0.20.1](https://github.com/RocketChat/fuselage/compare/v0.20.0...v0.20.1) (2020-12-22)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.20.0](https://github.com/RocketChat/fuselage/compare/v0.19.0...v0.20.0) (2020-12-21)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.19.0](https://github.com/RocketChat/fuselage/compare/v0.18.0...v0.19.0) (2020-11-28)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.18.0](https://github.com/RocketChat/fuselage/compare/v0.17.3...v0.18.0) (2020-11-16)
### Bug Fixes
- Set a conservative output.environment on Webpack bundles ([#330](https://github.com/RocketChat/fuselage/issues/330)) ([62bf728](https://github.com/RocketChat/fuselage/commit/62bf728d3541d8d7ee72420347f2351359fb5df7))
## [0.17.3](https://github.com/RocketChat/fuselage/compare/v0.17.2...v0.17.3) (2020-11-16)
### Bug Fixes
- Set a conservative output.environment on Webpack bundles ([#330](https://github.com/RocketChat/fuselage/issues/330)) ([85d4a3a](https://github.com/RocketChat/fuselage/commit/85d4a3a5fd6881b07e97fb690d31baef405cfa69))
## [0.17.2](https://github.com/RocketChat/fuselage/compare/v0.17.1...v0.17.2) (2020-10-28)
**Note:** Version bump only for package @rocket.chat/ui-kit
## [0.17.1](https://github.com/RocketChat/fuselage/compare/v0.17.0...v0.17.1) (2020-10-26)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.17.0](https://github.com/RocketChat/fuselage/compare/v0.16.0...v0.17.0) (2020-10-25)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.16.0](https://github.com/RocketChat/fuselage/compare/v0.15.1...v0.16.0) (2020-09-30)
**Note:** Version bump only for package @rocket.chat/ui-kit
## [0.15.1](https://github.com/RocketChat/fuselage/compare/v0.15.0...v0.15.1) (2020-09-22)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.15.0](https://github.com/RocketChat/fuselage/compare/v0.14.1...v0.15.0) (2020-09-17)
**Note:** Version bump only for package @rocket.chat/ui-kit
## [0.14.1](https://github.com/RocketChat/fuselage/compare/v0.14.0...v0.14.1) (2020-08-22)
### Bug Fixes
- UiKit - conditional block ([38d57ea](https://github.com/RocketChat/fuselage/commit/38d57ea9005aafc3820316beacfda43f620fa93f))
# [0.14.0](https://github.com/RocketChat/fuselage/compare/v0.13.2...v0.14.0) (2020-08-18)
### Features
- UiKit types ([#274](https://github.com/RocketChat/fuselage/issues/274)) ([e2ea79b](https://github.com/RocketChat/fuselage/commit/e2ea79b02a0fd6492c3fad06ba57cbb88d89da2b))
## [0.13.1](https://github.com/RocketChat/fuselage/compare/v0.13.0...v0.13.1) (2020-07-17)
### Bug Fixes
- Select mutations and ui-kit alerts ([#263](https://github.com/RocketChat/fuselage/issues/263)) ([661398d](https://github.com/RocketChat/fuselage/commit/661398dfdeaf827dadc46d24a7382d69f43f9742))
# [0.13.0](https://github.com/RocketChat/fuselage/compare/v0.12.0...v0.13.0) (2020-07-14)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.12.0](https://github.com/RocketChat/fuselage/compare/v0.11.0...v0.12.0) (2020-07-14)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.11.0](https://github.com/RocketChat/fuselage/compare/v0.10.0...v0.11.0) (2020-07-11)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.10.0](https://github.com/RocketChat/fuselage/compare/v0.9.0...v0.10.0) (2020-06-20)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.9.0](https://github.com/RocketChat/fuselage/compare/v0.8.0...v0.9.0) (2020-05-21)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.8.0](https://github.com/RocketChat/fuselage/compare/v0.7.1...v0.8.0) (2020-04-22)
**Note:** Version bump only for package @rocket.chat/ui-kit
## [0.7.1](https://github.com/RocketChat/fuselage/compare/v0.7.0...v0.7.1) (2020-04-01)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.7.0](https://github.com/RocketChat/fuselage/compare/v0.6.2...v0.7.0) (2020-04-01)
**Note:** Version bump only for package @rocket.chat/ui-kit
## [0.6.2](https://github.com/RocketChat/fuselage/compare/v0.6.1...v0.6.2) (2020-03-31)
**Note:** Version bump only for package @rocket.chat/ui-kit
## [0.6.1](https://github.com/RocketChat/fuselage/compare/v0.6.0...v0.6.1) (2020-03-24)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.6.0](https://github.com/RocketChat/fuselage/compare/v0.5.0...v0.6.0) (2020-03-20)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.5.0](https://github.com/RocketChat/fuselage/compare/v0.4.1...v0.5.0) (2020-03-20)
**Note:** Version bump only for package @rocket.chat/ui-kit
## [0.4.1](https://github.com/RocketChat/fuselage/compare/v0.4.0...v0.4.1) (2020-03-16)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.4.0](https://github.com/RocketChat/fuselage/compare/v0.3.0...v0.4.0) (2020-03-10)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.3.0](https://github.com/RocketChat/fuselage/compare/v0.2.0...v0.3.0) (2020-02-17)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.30...v0.2.0) (2020-02-13)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0-alpha.29](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.28...v0.2.0-alpha.29) (2020-02-10)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0-alpha.28](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.27...v0.2.0-alpha.28) (2020-02-10)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0-alpha.26](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.25...v0.2.0-alpha.26) (2020-02-10)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0-alpha.25](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.24...v0.2.0-alpha.25) (2020-02-10)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0-alpha.24](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.23...v0.2.0-alpha.24) (2020-02-09)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0-alpha.23](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.22...v0.2.0-alpha.23) (2020-02-07)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0-alpha.22](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.21...v0.2.0-alpha.22) (2020-02-07)
### Bug Fixes
- uikit using react components ([#135](https://github.com/RocketChat/fuselage/issues/135)) ([52fcedb](https://github.com/RocketChat/fuselage/commit/52fcedb0efbc33ad4240c83fd92a325fcc9f0f4c))
# [0.2.0-alpha.21](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.20...v0.2.0-alpha.21) (2020-02-05)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0-alpha.20](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.19...v0.2.0-alpha.20) (2020-02-01)
**Note:** Version bump only for package @rocket.chat/ui-kit
# [0.2.0-alpha.19](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.18...v0.2.0-alpha.19) (2020-01-13)
### Features
- Position, Modal, Options components ([#116](https://github.com/RocketChat/fuselage/issues/116)) ([af1916a](https://github.com/RocketChat/fuselage/commit/af1916a22c677939adda04fe417dafe406292762)), closes [#117](https://github.com/RocketChat/fuselage/issues/117)
# [0.2.0-alpha.18](https://github.com/RocketChat/fuselage/compare/v0.2.0-alpha.17...v0.2.0-alpha.18) (2019-12-31)
### Features
- UI Kit ([#95](https://github.com/RocketChat/fuselage/issues/95)) ([6d4162b](https://github.com/RocketChat/fuselage/commit/6d4162bb8c121b1e89f8c818e7106bce49f09c27)), closes [#94](https://github.com/RocketChat/fuselage/issues/94) [#109](https://github.com/RocketChat/fuselage/issues/109) [#108](https://github.com/RocketChat/fuselage/issues/108) [#111](https://github.com/RocketChat/fuselage/issues/111)

91
packages/ui-kit/README.md Normal file
View File

@@ -0,0 +1,91 @@
<!--header-->
<p align="center">
<a href="https://rocket.chat" title="Rocket.Chat">
<img src="https://github.com/RocketChat/Rocket.Chat.Artwork/raw/master/Logos/2020/png/logo-horizontal-red.png" alt="Rocket.Chat" />
</a>
</p>
# `@rocket.chat/ui-kit`
> Interactive UI elements for Rocket.Chat Apps
---
[![npm@latest](https://img.shields.io/npm/v/@rocket.chat/ui-kit/latest?style=flat-square)](https://www.npmjs.com/package/@rocket.chat/ui-kit/v/latest) [![npm@next](https://img.shields.io/npm/v/@rocket.chat/ui-kit/next?style=flat-square)](https://www.npmjs.com/package/@rocket.chat/ui-kit/v/next) ![npm downloads](https://img.shields.io/npm/dw/@rocket.chat/ui-kit?style=flat-square) ![License: MIT](https://img.shields.io/npm/l/@rocket.chat/ui-kit?style=flat-square)
![deps](https://img.shields.io/librariesio/release/npm/@rocket.chat/ui-kit?style=flat-square) ![npm bundle size](https://img.shields.io/bundlephobia/min/@rocket.chat/ui-kit?style=flat-square)
<!--/header-->
## Install
<!--install-->
Add `@rocket.chat/ui-kit` as a dependency:
```sh
npm i @rocket.chat/ui-kit
# or, if you are using yarn:
yarn add @rocket.chat/ui-kit
```
<!--/install-->
## Contributing
<!--contributing(msg)-->
Contributions, issues, and feature requests are welcome!<br />
Feel free to check the [issues](https://github.com/RocketChat/Rocket.Chat/issues).
<!--/contributing(msg)-->
### Building
As this package dependends on others in this monorepo, before anything run the following at the root directory:
<!--yarn(build)-->
```sh
yarn build
```
<!--/yarn(build)-->
### Linting
To ensure the source is matching our coding style, we perform [linting](<https://en.wikipedia.org/wiki/Lint_(software)>).
Before commiting, check if your code fits our style by running:
<!--yarn(lint)-->
```sh
yarn lint
```
<!--/yarn(lint)-->
Some linter warnings and errors can be automatically fixed:
<!--yarn(lint-and-fix)-->
```sh
yarn lint-and-fix
```
<!--/yarn(lint-and-fix)-->
### Running tests
Whenever possible, add tests to describe exactly what your code do. You can run them by yourself:
<!--yarn(test)-->
```sh
yarn test
```
<!--/yarn(test)-->

View File

@@ -0,0 +1,6 @@
import server from '@rocket.chat/jest-presets/server';
import type { Config } from 'jest';
export default {
preset: server.preset,
} satisfies Config;

View File

@@ -0,0 +1,56 @@
{
"name": "@rocket.chat/ui-kit",
"version": "1.1.0",
"description": "Interactive UI elements for Rocket.Chat Apps",
"homepage": "https://rocket.chat",
"bugs": {
"url": "https://github.com/RocketChat/Rocket.Chat/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/RocketChat/Rocket.Chat.git",
"directory": "packages/ui-kit"
},
"license": "MIT",
"author": {
"name": "Rocket.Chat",
"url": "https://rocket.chat/"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"/dist"
],
"scripts": {
".:build:clean": "rimraf dist",
".:build:prepare": "ts-patch install && typia patch",
".:build:tsc": "tsc -p tsconfig.build.json",
"build": "run-s .:build:prepare .:build:clean .:build:tsc",
"lint": "eslint .",
"test": "jest",
"testunit": "jest",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"typia": "patch:typia@npm%3A9.7.2#~/.yarn/patches/typia-npm-9.7.2-5c5d9c80b4.patch"
},
"devDependencies": {
"@rocket.chat/icons": "^0.49.0",
"@rocket.chat/jest-presets": "workspace:~",
"@rocket.chat/tsconfig": "workspace:*",
"@types/jest": "~30.0.0",
"eslint": "~9.39.5",
"jest": "~30.2.0",
"npm-run-all": "~4.1.5",
"rimraf": "~6.0.1",
"ts-jest": "~29.4.12",
"ts-patch": "^3.3.0",
"typescript": "~5.9.3"
},
"peerDependencies": {
"@rocket.chat/icons": "*"
},
"volta": {
"extends": "../../package.json"
}
}

View File

@@ -0,0 +1,10 @@
import type { ConfirmationDialog } from './ConfirmationDialog';
import type { InputElementDispatchAction } from './InputElementDispatchAction';
export type Actionable<Block> = Block & {
appId: string;
blockId: string;
actionId: string;
confirm?: ConfirmationDialog;
dispatchActionConfig?: InputElementDispatchAction[];
};

View File

@@ -0,0 +1,40 @@
import type { ButtonElement } from './elements/ButtonElement';
import type { ChannelsSelectElement } from './elements/ChannelsSelectElement';
import type { CheckboxElement } from './elements/CheckboxElement';
import type { ConversationsSelectElement } from './elements/ConversationsSelectElement';
import type { DatePickerElement } from './elements/DatePickerElement';
import type { ExperimentalTabElement } from './elements/ExperimentalTabElement';
import type { IconButtonElement } from './elements/IconButtonElement';
import type { LinearScaleElement } from './elements/LinearScaleElement';
import type { MultiChannelsSelectElement } from './elements/MultiChannelsSelectElement';
import type { MultiConversationsSelectElement } from './elements/MultiConversationsSelectElement';
import type { MultiStaticSelectElement } from './elements/MultiStaticSelectElement';
import type { MultiUsersSelectElement } from './elements/MultiUsersSelectElement';
import type { OverflowElement } from './elements/OverflowElement';
import type { PlainTextInputElement } from './elements/PlainTextInputElement';
import type { RadioButtonElement } from './elements/RadioButtonElement';
import type { StaticSelectElement } from './elements/StaticSelectElement';
import type { TimePickerElement } from './elements/TimePickerElement';
import type { ToggleSwitchElement } from './elements/ToggleSwitchElement';
import type { UsersSelectElement } from './elements/UsersSelectElement';
export type ActionableElement =
| ButtonElement
| ChannelsSelectElement
| ConversationsSelectElement
| DatePickerElement
| LinearScaleElement
| MultiChannelsSelectElement
| MultiConversationsSelectElement
| MultiStaticSelectElement
| MultiUsersSelectElement
| OverflowElement
| PlainTextInputElement
| StaticSelectElement
| UsersSelectElement
| ToggleSwitchElement
| RadioButtonElement
| CheckboxElement
| TimePickerElement
| ExperimentalTabElement
| IconButtonElement;

View File

@@ -0,0 +1,5 @@
import type { BlockElement } from './BlockElement';
import type { LayoutBlock } from './LayoutBlock';
import type { TextObject } from './TextObject';
export type Block = TextObject | BlockElement | LayoutBlock;

View File

@@ -0,0 +1,44 @@
import type { ButtonElement } from './elements/ButtonElement';
import type { ChannelsSelectElement } from './elements/ChannelsSelectElement';
import type { CheckboxElement } from './elements/CheckboxElement';
import type { ConversationsSelectElement } from './elements/ConversationsSelectElement';
import type { DatePickerElement } from './elements/DatePickerElement';
import type { ExperimentalTabElement } from './elements/ExperimentalTabElement';
import type { IconButtonElement } from './elements/IconButtonElement';
import type { FrameableIconElement } from './elements/IconElement';
import type { ImageElement } from './elements/ImageElement';
import type { LinearScaleElement } from './elements/LinearScaleElement';
import type { MultiChannelsSelectElement } from './elements/MultiChannelsSelectElement';
import type { MultiConversationsSelectElement } from './elements/MultiConversationsSelectElement';
import type { MultiStaticSelectElement } from './elements/MultiStaticSelectElement';
import type { MultiUsersSelectElement } from './elements/MultiUsersSelectElement';
import type { OverflowElement } from './elements/OverflowElement';
import type { PlainTextInputElement } from './elements/PlainTextInputElement';
import type { RadioButtonElement } from './elements/RadioButtonElement';
import type { StaticSelectElement } from './elements/StaticSelectElement';
import type { TimePickerElement } from './elements/TimePickerElement';
import type { ToggleSwitchElement } from './elements/ToggleSwitchElement';
import type { UsersSelectElement } from './elements/UsersSelectElement';
export type BlockElement =
| ButtonElement
| ChannelsSelectElement
| ConversationsSelectElement
| DatePickerElement
| ImageElement
| LinearScaleElement
| MultiChannelsSelectElement
| MultiConversationsSelectElement
| MultiStaticSelectElement
| MultiUsersSelectElement
| OverflowElement
| PlainTextInputElement
| StaticSelectElement
| UsersSelectElement
| ToggleSwitchElement
| RadioButtonElement
| CheckboxElement
| TimePickerElement
| ExperimentalTabElement
| FrameableIconElement
| IconButtonElement;

View File

@@ -0,0 +1,25 @@
import type { BlockElement } from './BlockElement';
export const BlockElementType = {
BUTTON: 'button',
CHANNELS_SELECT: 'channels_select',
CHECKBOX: 'checkbox',
CONVERSATIONS_SELECT: 'conversations_select',
DATEPICKER: 'datepicker',
ICON: 'icon',
ICON_BUTTON: 'icon_button',
IMAGE: 'image',
LINEAR_SCALE: 'linear_scale',
MULTI_CHANNELS_SELECT: 'multi_channels_select',
MULTI_CONVERSATIONS_SELECT: 'multi_conversations_select',
MULTI_STATIC_SELECT: 'multi_static_select',
MULTI_USERS_SELECT: 'multi_users_select',
OVERFLOW: 'overflow',
PLAIN_TEXT_INPUT: 'plain_text_input',
RADIO_BUTTON: 'radio_button',
STATIC_SELECT: 'static_select',
TAB: 'tab',
TIME_PICKER: 'time_picker',
TOGGLE_SWITCH: 'toggle_switch',
USERS_SELECT: 'users_select',
} as const satisfies Record<Uppercase<BlockElement['type']>, BlockElement['type']>;

View File

@@ -0,0 +1,10 @@
import type { TextObject } from './TextObject';
import type { PlainText } from './text/PlainText';
export type ConfirmationDialog = {
title: PlainText;
text: TextObject;
confirm: PlainText;
deny: PlainText;
style: 'primary' | 'danger';
};

View File

@@ -0,0 +1 @@
export type InputElementDispatchAction = 'on_character_entered' | 'on_item_selected';

View File

@@ -0,0 +1,6 @@
import type { LayoutBlock } from './LayoutBlock';
import type { LayoutBlockish } from './LayoutBlockish';
it('should have the same shape of `LayoutBlockish<{}>`', () => {
expect<Expect<Required<LayoutBlock> extends Required<LayoutBlockish<object>> ? true : false>>(true).toBeTruthy();
});

View File

@@ -0,0 +1,26 @@
import type { ActionsBlock } from './layout/ActionsBlock';
import type { CalloutBlock } from './layout/CalloutBlock';
import type { ConditionalBlock } from './layout/ConditionalBlock';
import type { ContextBlock } from './layout/ContextBlock';
import type { DividerBlock } from './layout/DividerBlock';
import type { ExperimentalTabNavigationBlock } from './layout/ExperimentalTabNavigationBlock';
import type { ImageBlock } from './layout/ImageBlock';
import type { InfoCardBlock } from './layout/InfoCardBlock';
import type { InputBlock } from './layout/InputBlock';
import type { PreviewBlock } from './layout/PreviewBlock';
import type { SectionBlock } from './layout/SectionBlock';
import type { VideoConferenceBlock } from './layout/VideoConferenceBlock';
export type LayoutBlock =
| PreviewBlock
| ActionsBlock
| ConditionalBlock
| ContextBlock
| DividerBlock
| ImageBlock
| InputBlock
| SectionBlock
| VideoConferenceBlock
| CalloutBlock
| ExperimentalTabNavigationBlock
| InfoCardBlock;

View File

@@ -0,0 +1,16 @@
import type { LayoutBlock } from './LayoutBlock';
export const LayoutBlockType = {
SECTION: 'section',
DIVIDER: 'divider',
IMAGE: 'image',
ACTIONS: 'actions',
CONTEXT: 'context',
INPUT: 'input',
CONDITIONAL: 'conditional',
PREVIEW: 'preview',
VIDEO_CONF: 'video_conf',
CALLOUT: 'callout',
TAB_NAVIGATION: 'tab_navigation',
INFO_CARD: 'info_card',
} as const satisfies Record<Uppercase<LayoutBlock['type']>, LayoutBlock['type']>;

View File

@@ -0,0 +1,4 @@
export type LayoutBlockish<Block> = Block & {
appId?: string;
blockId?: string;
};

View File

@@ -0,0 +1,9 @@
import type { TextObject } from './TextObject';
import type { PlainText } from './text/PlainText';
export type Option = {
text: TextObject;
value: string;
description?: PlainText;
url?: string;
};

View File

@@ -0,0 +1,7 @@
import type { Option } from './Option';
import type { PlainText } from './text/PlainText';
export type OptionGroup = {
label: PlainText;
options: Option[];
};

View File

@@ -0,0 +1,4 @@
import type { Block } from './Block';
import type { ConditionalBlock } from './layout/ConditionalBlock';
export type RenderableBlock = Exclude<Block, ConditionalBlock>;

View File

@@ -0,0 +1,4 @@
import type { LayoutBlock } from './LayoutBlock';
import type { ConditionalBlock } from './layout/ConditionalBlock';
export type RenderableLayoutBlock = Exclude<LayoutBlock, ConditionalBlock>;

View File

@@ -0,0 +1,4 @@
import type { Markdown } from './text/Markdown';
import type { PlainText } from './text/PlainText';
export type TextObject = PlainText | Markdown;

View File

@@ -0,0 +1,6 @@
import type { TextObject } from './TextObject';
export const TextObjectType = {
PLAIN_TEXT: 'plain_text',
MRKDWN: 'mrkdwn',
} as const satisfies Record<Uppercase<TextObject['type']>, TextObject['type']>;

View File

@@ -0,0 +1,6 @@
export type WithTranslations<T> = T & {
i18n?: {
key: string;
args?: { [key: string]: string | number };
};
};

View File

@@ -0,0 +1,11 @@
import type { Actionable } from '../Actionable';
import type { PlainText } from '../text/PlainText';
export type ButtonElement = Actionable<{
type: 'button';
text: PlainText;
url?: string; // TODO: The url in ButtonElement should not be an arbitrary string, it lacks validation and can lead to security issues
value?: string;
style?: 'primary' | 'secondary' | 'danger' | 'warning' | 'success';
secondary?: boolean;
}>;

View File

@@ -0,0 +1,7 @@
import type { Actionable } from '../Actionable';
import type { PlainText } from '../text/PlainText';
export type ChannelsSelectElement = Actionable<{
type: 'channels_select';
placeholder?: PlainText;
}>;

View File

@@ -0,0 +1,8 @@
import type { Actionable } from '../Actionable';
import type { Option } from '../Option';
export type CheckboxElement = Actionable<{
type: 'checkbox';
options: Option[];
initialOptions?: Option[];
}>;

View File

@@ -0,0 +1,6 @@
import type { Actionable } from '../Actionable';
/** @todo */
export type ConversationsSelectElement = Actionable<{
type: 'conversations_select';
}>;

View File

@@ -0,0 +1,8 @@
import type { Actionable } from '../Actionable';
import type { TextObject } from '../TextObject';
export type DatePickerElement = Actionable<{
type: 'datepicker';
placeholder?: TextObject;
initialDate?: string;
}>;

View File

@@ -0,0 +1,9 @@
import type { Actionable } from '../Actionable';
import type { TextObject } from '../TextObject';
export type ExperimentalTabElement = Actionable<{
type: 'tab';
title: TextObject;
disabled?: boolean;
selected?: boolean;
}>;

View File

@@ -0,0 +1,10 @@
import type { Actionable } from '../Actionable';
import type { IconElement } from './IconElement';
export type IconButtonElement = Actionable<{
type: 'icon_button';
icon: IconElement;
label?: string;
url?: string;
value?: string;
}>;

View File

@@ -0,0 +1,11 @@
type AvailableIcons = 'phone-off' | 'phone-issue' | 'clock' | 'arrow-forward' | 'info' | 'phone-question-mark';
export type IconElement = {
type: 'icon';
icon: AvailableIcons;
variant: 'default' | 'danger' | 'secondary' | 'warning';
};
export type FrameableIconElement = IconElement & {
framed?: boolean;
};

View File

@@ -0,0 +1,5 @@
export type ImageElement = {
type: 'image';
imageUrl: string;
altText: string;
};

View File

@@ -0,0 +1,11 @@
import type { Actionable } from '../Actionable';
import type { PlainText } from '../text/PlainText';
export type LinearScaleElement = Actionable<{
type: 'linear_scale';
minValue?: number;
maxValue?: number; // TODO: The maximum length of the LinearScaleElement is not specified in the specification. We should set a reasonable value for it to prevent potential issues with very large numbers.
initialValue?: number;
preLabel?: PlainText;
postLabel?: PlainText;
}>;

View File

@@ -0,0 +1,7 @@
import type { Actionable } from '../Actionable';
import type { PlainText } from '../text/PlainText';
export type MultiChannelsSelectElement = Actionable<{
type: 'multi_channels_select';
placeholder?: PlainText;
}>;

View File

@@ -0,0 +1,6 @@
import type { Actionable } from '../Actionable';
/** @todo */
export type MultiConversationsSelectElement = Actionable<{
type: 'multi_conversations_select';
}>;

View File

@@ -0,0 +1,14 @@
import type { Actionable } from '../Actionable';
import type { Option } from '../Option';
import type { OptionGroup } from '../OptionGroup';
import type { TextObject } from '../TextObject';
export type MultiStaticSelectElement = Actionable<{
type: 'multi_static_select';
placeholder: TextObject;
options: readonly Option[];
optionGroups?: readonly OptionGroup[];
maxSelectItems?: number;
initialValue?: Option['value'][];
initialOption?: Option[];
}>;

View File

@@ -0,0 +1,7 @@
import type { Actionable } from '../Actionable';
import type { PlainText } from '../text/PlainText';
export type MultiUsersSelectElement = Actionable<{
type: 'multi_users_select';
placeholder?: PlainText;
}>;

View File

@@ -0,0 +1,7 @@
import type { Actionable } from '../Actionable';
import type { Option } from '../Option';
export type OverflowElement = Actionable<{
type: 'overflow';
options: readonly Option[];
}>;

View File

@@ -0,0 +1,11 @@
import type { Actionable } from '../Actionable';
import type { PlainText } from '../text/PlainText';
export type PlainTextInputElement = Actionable<{
type: 'plain_text_input';
placeholder?: PlainText;
initialValue?: string;
multiline?: boolean;
minLength?: number;
maxLength?: number;
}>;

View File

@@ -0,0 +1,8 @@
import type { Actionable } from '../Actionable';
import type { Option } from '../Option';
export type RadioButtonElement = Actionable<{
type: 'radio_button';
options: Option[];
initialOption?: Option;
}>;

View File

@@ -0,0 +1,13 @@
import type { Actionable } from '../Actionable';
import type { Option } from '../Option';
import type { OptionGroup } from '../OptionGroup';
import type { TextObject } from '../TextObject';
export type StaticSelectElement = Actionable<{
type: 'static_select';
placeholder: TextObject;
options: readonly Option[];
optionGroups?: readonly OptionGroup[];
initialOption?: Option;
initialValue?: Option['value'];
}>;

View File

@@ -0,0 +1,8 @@
import type { Actionable } from '../Actionable';
import type { TextObject } from '../TextObject';
export type TimePickerElement = Actionable<{
type: 'time_picker';
placeholder?: TextObject;
initialTime?: string;
}>;

View File

@@ -0,0 +1,8 @@
import type { Actionable } from '../Actionable';
import type { Option } from '../Option';
export type ToggleSwitchElement = Actionable<{
type: 'toggle_switch';
options: Option[];
initialOptions?: Option[];
}>;

View File

@@ -0,0 +1,7 @@
import type { Actionable } from '../Actionable';
import type { PlainText } from '../text/PlainText';
export type UsersSelectElement = Actionable<{
type: 'users_select';
placeholder?: PlainText;
}>;

View File

@@ -0,0 +1,22 @@
import type { BlockElement } from './BlockElement';
import { BlockElementType } from './BlockElementType';
import type { ActionsBlock } from './layout/ActionsBlock';
export const isActionsBlockElement = (block: BlockElement): block is ActionsBlock['elements'][number] => {
switch (block.type) {
case BlockElementType.BUTTON:
case BlockElementType.DATEPICKER:
case BlockElementType.LINEAR_SCALE:
case BlockElementType.MULTI_STATIC_SELECT:
case BlockElementType.OVERFLOW:
case BlockElementType.STATIC_SELECT:
case BlockElementType.TOGGLE_SWITCH:
case BlockElementType.CHECKBOX:
case BlockElementType.RADIO_BUTTON:
case BlockElementType.TIME_PICKER:
return true;
default:
return false;
}
};

View File

@@ -0,0 +1,17 @@
import type { BlockElement } from './BlockElement';
import { BlockElementType } from './BlockElementType';
import type { TextObject } from './TextObject';
import { TextObjectType } from './TextObjectType';
import type { ContextBlock } from './layout/ContextBlock';
export const isContextBlockElement = (block: TextObject | BlockElement): block is ContextBlock['elements'][number] => {
switch (block.type) {
case BlockElementType.IMAGE:
case TextObjectType.PLAIN_TEXT:
case TextObjectType.MRKDWN:
return true;
default:
return false;
}
};

View File

@@ -0,0 +1,20 @@
import type { BlockElement } from './BlockElement';
import { BlockElementType } from './BlockElementType';
import type { InputBlock } from './layout/InputBlock';
export const isInputBlockElement = (block: BlockElement): block is InputBlock['element'] => {
switch (block.type) {
case BlockElementType.CHANNELS_SELECT:
case BlockElementType.CONVERSATIONS_SELECT:
case BlockElementType.DATEPICKER:
case BlockElementType.LINEAR_SCALE:
case BlockElementType.MULTI_STATIC_SELECT:
case BlockElementType.PLAIN_TEXT_INPUT:
case BlockElementType.STATIC_SELECT:
case BlockElementType.USERS_SELECT:
return true;
default:
return false;
}
};

View File

@@ -0,0 +1,18 @@
import type { BlockElement } from './BlockElement';
import { BlockElementType } from './BlockElementType';
import type { SectionBlock } from './layout/SectionBlock';
export const isSectionBlockAccessoryElement = (block: BlockElement): block is Exclude<SectionBlock['accessory'], undefined> => {
switch (block.type) {
case BlockElementType.BUTTON:
case BlockElementType.DATEPICKER:
case BlockElementType.IMAGE:
case BlockElementType.MULTI_STATIC_SELECT:
case BlockElementType.OVERFLOW:
case BlockElementType.STATIC_SELECT:
return true;
default:
return false;
}
};

View File

@@ -0,0 +1,5 @@
import type { Block } from './Block';
import type { TextObject } from './TextObject';
import { TextObjectType } from './TextObjectType';
export const isTextObject = (block: Block): block is TextObject => (Object.values(TextObjectType) as string[]).includes(block.type);

View File

@@ -0,0 +1,39 @@
import type { LayoutBlockish } from '../LayoutBlockish';
import type { ButtonElement } from '../elements/ButtonElement';
import type { ChannelsSelectElement } from '../elements/ChannelsSelectElement';
import type { CheckboxElement } from '../elements/CheckboxElement';
import type { ConversationsSelectElement } from '../elements/ConversationsSelectElement';
import type { DatePickerElement } from '../elements/DatePickerElement';
import type { LinearScaleElement } from '../elements/LinearScaleElement';
import type { MultiChannelsSelectElement } from '../elements/MultiChannelsSelectElement';
import type { MultiConversationsSelectElement } from '../elements/MultiConversationsSelectElement';
import type { MultiStaticSelectElement } from '../elements/MultiStaticSelectElement';
import type { MultiUsersSelectElement } from '../elements/MultiUsersSelectElement';
import type { OverflowElement } from '../elements/OverflowElement';
import type { RadioButtonElement } from '../elements/RadioButtonElement';
import type { StaticSelectElement } from '../elements/StaticSelectElement';
import type { TimePickerElement } from '../elements/TimePickerElement';
import type { ToggleSwitchElement } from '../elements/ToggleSwitchElement';
import type { UsersSelectElement } from '../elements/UsersSelectElement';
export type ActionsBlock = LayoutBlockish<{
type: 'actions';
elements: readonly (
| ButtonElement
| ChannelsSelectElement
| ConversationsSelectElement
| DatePickerElement
| LinearScaleElement
| MultiChannelsSelectElement
| MultiConversationsSelectElement
| MultiStaticSelectElement
| MultiUsersSelectElement
| OverflowElement
| StaticSelectElement
| UsersSelectElement
| ToggleSwitchElement
| CheckboxElement
| RadioButtonElement
| TimePickerElement
)[];
}>;

View File

@@ -0,0 +1,12 @@
import type { LayoutBlockish } from '../LayoutBlockish';
import type { TextObject } from '../TextObject';
import type { ButtonElement } from '../elements/ButtonElement';
import type { OverflowElement } from '../elements/OverflowElement';
export type CalloutBlock = LayoutBlockish<{
type: 'callout';
title?: TextObject;
text: TextObject;
variant?: 'info' | 'danger' | 'warning' | 'success';
accessory?: ButtonElement | OverflowElement;
}>;

View File

@@ -0,0 +1,11 @@
import type { Conditions } from '../../rendering/Conditions';
import type { LayoutBlockish } from '../LayoutBlockish';
import type { RenderableLayoutBlock } from '../RenderableLayoutBlock';
export type ConditionalBlock = LayoutBlockish<{
type: 'conditional';
when?: {
[K in keyof Conditions]: readonly Conditions[K][];
};
render: readonly RenderableLayoutBlock[];
}>;

View File

@@ -0,0 +1,9 @@
import type { LayoutBlockish } from '../LayoutBlockish';
import type { TextObject } from '../TextObject';
import type { ImageElement } from '../elements/ImageElement';
export type ContextBlockElements = TextObject | ImageElement;
export type ContextBlock = LayoutBlockish<{
type: 'context';
elements: readonly ContextBlockElements[];
}>;

View File

@@ -0,0 +1,5 @@
import type { LayoutBlockish } from '../LayoutBlockish';
export type DividerBlock = LayoutBlockish<{
type: 'divider';
}>;

View File

@@ -0,0 +1,7 @@
import type { LayoutBlockish } from '../LayoutBlockish';
import type { ExperimentalTabElement } from '../elements/ExperimentalTabElement';
export type ExperimentalTabNavigationBlock = LayoutBlockish<{
type: 'tab_navigation';
tabs: readonly ExperimentalTabElement[];
}>;

View File

@@ -0,0 +1,9 @@
import type { LayoutBlockish } from '../LayoutBlockish';
import type { PlainText } from '../text/PlainText';
export type ImageBlock = LayoutBlockish<{
type: 'image';
imageUrl: string;
altText: string;
title?: PlainText;
}>;

View File

@@ -0,0 +1,16 @@
import type { LayoutBlockish } from '../LayoutBlockish';
import type { IconButtonElement } from '../elements/IconButtonElement';
import type { FrameableIconElement } from '../elements/IconElement';
import type { Markdown } from '../text/Markdown';
import type { PlainText } from '../text/PlainText';
type InfoCardRow = {
background: 'default' | 'secondary';
elements: readonly (FrameableIconElement | PlainText | Markdown)[];
action?: IconButtonElement;
};
export type InfoCardBlock = LayoutBlockish<{
type: 'info_card';
rows: readonly InfoCardRow[];
}>;

View File

@@ -0,0 +1,40 @@
import type { LayoutBlockish } from '../LayoutBlockish';
import type { ChannelsSelectElement } from '../elements/ChannelsSelectElement';
import type { CheckboxElement } from '../elements/CheckboxElement';
import type { ConversationsSelectElement } from '../elements/ConversationsSelectElement';
import type { DatePickerElement } from '../elements/DatePickerElement';
import type { LinearScaleElement } from '../elements/LinearScaleElement';
import type { MultiChannelsSelectElement } from '../elements/MultiChannelsSelectElement';
import type { MultiConversationsSelectElement } from '../elements/MultiConversationsSelectElement';
import type { MultiStaticSelectElement } from '../elements/MultiStaticSelectElement';
import type { MultiUsersSelectElement } from '../elements/MultiUsersSelectElement';
import type { PlainTextInputElement } from '../elements/PlainTextInputElement';
import type { RadioButtonElement } from '../elements/RadioButtonElement';
import type { StaticSelectElement } from '../elements/StaticSelectElement';
import type { TimePickerElement } from '../elements/TimePickerElement';
import type { ToggleSwitchElement } from '../elements/ToggleSwitchElement';
import type { UsersSelectElement } from '../elements/UsersSelectElement';
import type { PlainText } from '../text/PlainText';
export type InputBlock = LayoutBlockish<{
type: 'input';
label: PlainText;
element:
| ChannelsSelectElement
| ConversationsSelectElement
| DatePickerElement
| LinearScaleElement
| MultiChannelsSelectElement
| MultiConversationsSelectElement
| MultiStaticSelectElement
| MultiUsersSelectElement
| PlainTextInputElement
| StaticSelectElement
| UsersSelectElement
| CheckboxElement
| RadioButtonElement
| TimePickerElement
| ToggleSwitchElement;
hint?: PlainText;
optional?: boolean;
}>;

View File

@@ -0,0 +1,36 @@
import type { LayoutBlockish } from '../LayoutBlockish';
import type { TextObject } from '../TextObject';
import type { ContextBlock } from './ContextBlock';
type Image = {
url: string;
dimensions?: {
width: number;
height: number;
};
};
export type PreviewBlockBase = LayoutBlockish<{
type: 'preview';
title: TextObject[];
description: TextObject[];
footer?: ContextBlock;
}>;
export type PreviewBlockWithThumb = PreviewBlockBase & {
thumb: Image;
};
export type PreviewBlockWithPreview = PreviewBlockBase & {
preview: Image;
externalUrl?: string;
oembedUrl?: string;
thumb: undefined;
};
export type PreviewBlock = PreviewBlockBase | PreviewBlockWithThumb | PreviewBlockWithPreview;
export const isPreviewBlockWithThumb = (previewBlock: PreviewBlock): previewBlock is PreviewBlockWithThumb => 'thumb' in previewBlock;
export const isPreviewBlockWithPreview = (previewBlock: PreviewBlock): previewBlock is PreviewBlockWithPreview =>
'externalUrl' in previewBlock || 'oembedUrl' in previewBlock;

View File

@@ -0,0 +1,15 @@
import type { LayoutBlockish } from '../LayoutBlockish';
import type { TextObject } from '../TextObject';
import type { ButtonElement } from '../elements/ButtonElement';
import type { DatePickerElement } from '../elements/DatePickerElement';
import type { ImageElement } from '../elements/ImageElement';
import type { MultiStaticSelectElement } from '../elements/MultiStaticSelectElement';
import type { OverflowElement } from '../elements/OverflowElement';
import type { StaticSelectElement } from '../elements/StaticSelectElement';
export type SectionBlock = LayoutBlockish<{
type: 'section';
text?: TextObject;
fields?: readonly TextObject[];
accessory?: ButtonElement | DatePickerElement | ImageElement | MultiStaticSelectElement | OverflowElement | StaticSelectElement;
}>;

View File

@@ -0,0 +1,6 @@
import type { LayoutBlockish } from '../LayoutBlockish';
export type VideoConferenceBlock = LayoutBlockish<{
type: 'video_conf';
callId: string;
}>;

View File

@@ -0,0 +1,7 @@
import type { WithTranslations } from '../WithTranslations';
export type Markdown = WithTranslations<{
type: 'mrkdwn';
text: string;
verbatim?: boolean;
}>;

View File

@@ -0,0 +1,7 @@
import type { WithTranslations } from '../WithTranslations';
export type PlainText = WithTranslations<{
type: 'plain_text';
text: string;
emoji?: boolean;
}>;

View File

@@ -0,0 +1,74 @@
export type { ConfirmationDialog } from './blocks/ConfirmationDialog';
export type { Option } from './blocks/Option';
export type { OptionGroup } from './blocks/OptionGroup';
export type { PlainText } from './blocks/text/PlainText';
export type { Markdown } from './blocks/text/Markdown';
export type { TextObject } from './blocks/TextObject';
export type { Conditions } from './rendering/Conditions';
export type { ButtonElement } from './blocks/elements/ButtonElement';
export type { ChannelsSelectElement } from './blocks/elements/ChannelsSelectElement';
export type { ConversationsSelectElement } from './blocks/elements/ConversationsSelectElement';
export type { DatePickerElement } from './blocks/elements/DatePickerElement';
export type { ImageElement } from './blocks/elements/ImageElement';
export type { LinearScaleElement } from './blocks/elements/LinearScaleElement';
export type { MultiStaticSelectElement } from './blocks/elements/MultiStaticSelectElement';
export type { MultiChannelsSelectElement } from './blocks/elements/MultiChannelsSelectElement';
export type { MultiConversationsSelectElement } from './blocks/elements/MultiConversationsSelectElement';
export type { MultiUsersSelectElement } from './blocks/elements/MultiUsersSelectElement';
export type { OverflowElement } from './blocks/elements/OverflowElement';
export type { PlainTextInputElement } from './blocks/elements/PlainTextInputElement';
export type { StaticSelectElement } from './blocks/elements/StaticSelectElement';
export type { UsersSelectElement } from './blocks/elements/UsersSelectElement';
export type { ToggleSwitchElement } from './blocks/elements/ToggleSwitchElement';
export type { CheckboxElement } from './blocks/elements/CheckboxElement';
export type { RadioButtonElement } from './blocks/elements/RadioButtonElement';
export type { TimePickerElement } from './blocks/elements/TimePickerElement';
export type { BlockElement } from './blocks/BlockElement';
export type { ExperimentalTabElement } from './blocks/elements/ExperimentalTabElement';
export type { IconElement, FrameableIconElement } from './blocks/elements/IconElement';
export type { IconButtonElement } from './blocks/elements/IconButtonElement';
export type { ActionsBlock } from './blocks/layout/ActionsBlock';
export type { ConditionalBlock } from './blocks/layout/ConditionalBlock';
export type { ContextBlock } from './blocks/layout/ContextBlock';
export * from './blocks/layout/PreviewBlock';
export type { DividerBlock } from './blocks/layout/DividerBlock';
export type { ImageBlock } from './blocks/layout/ImageBlock';
export type { InputBlock } from './blocks/layout/InputBlock';
export type { SectionBlock } from './blocks/layout/SectionBlock';
export type { VideoConferenceBlock } from './blocks/layout/VideoConferenceBlock';
export type { LayoutBlock } from './blocks/LayoutBlock';
export type { CalloutBlock } from './blocks/layout/CalloutBlock';
export type { ExperimentalTabNavigationBlock } from './blocks/layout/ExperimentalTabNavigationBlock';
export type { InfoCardBlock } from './blocks/layout/InfoCardBlock';
export type { InputElementDispatchAction } from './blocks/InputElementDispatchAction';
export type { Block } from './blocks/Block';
export type { RenderableBlock } from './blocks/RenderableBlock';
export { LayoutBlockType } from './blocks/LayoutBlockType';
export { BlockElementType } from './blocks/BlockElementType';
export { TextObjectType } from './blocks/TextObjectType';
export { BlockContext } from './rendering/BlockContext';
export type { BlockRenderers } from './rendering/BlockRenderers';
export type { RenderableLayoutBlock } from './blocks/RenderableLayoutBlock';
export { SurfaceRenderer } from './rendering/SurfaceRenderer';
export type { ActionableElement } from './blocks/ActionableElement';
export type { ActionOf } from './rendering/ActionOf';
export * from './surfaces/View';
export * from './surfaces/attachment';
export * from './surfaces/banner';
export * from './surfaces/contextualBar';
export * from './surfaces/message';
export * from './surfaces/modal';
export * from './interactions/UserInteraction';
export * from './interactions/ServerInteraction';

View File

@@ -0,0 +1,110 @@
import typia from 'typia';
import type { BannerView } from '../surfaces/banner';
import type { ContextualBarView } from '../surfaces/contextualBar';
import type { ModalView } from '../surfaces/modal';
type OpenModalServerInteraction = {
type: 'modal.open';
triggerId: string;
appId: string;
view: ModalView;
};
type UpdateModalServerInteraction = {
type: 'modal.update';
triggerId: string;
appId: string;
view: ModalView;
};
type CloseModalServerInteraction = {
type: 'modal.close';
triggerId: string;
appId: string;
};
type OpenBannerServerInteraction = {
type: 'banner.open';
triggerId: string;
appId: string;
} & BannerView;
type UpdateBannerServerInteraction = {
type: 'banner.update';
triggerId: string;
appId: string;
view: BannerView;
};
type CloseBannerServerInteraction = {
type: 'banner.close';
triggerId: string;
appId: string;
viewId: BannerView['viewId'];
};
type OpenContextualBarServerInteraction = {
type: 'contextual_bar.open';
triggerId: string;
appId: string;
view: ContextualBarView;
};
type UpdateContextualBarServerInteraction = {
type: 'contextual_bar.update';
triggerId: string;
appId: string;
view: ContextualBarView;
};
type CloseContextualBarServerInteraction = {
type: 'contextual_bar.close';
triggerId: string;
appId: string;
view: ContextualBarView;
};
type ReportErrorsServerInteraction = {
type: 'errors';
triggerId: string;
appId: string;
// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
viewId: ModalView['id'] | BannerView['viewId'] | ContextualBarView['id'];
errors: { [field: string]: string }[];
};
export type ServerInteraction =
| OpenModalServerInteraction
| UpdateModalServerInteraction
| CloseModalServerInteraction
| OpenBannerServerInteraction
| UpdateBannerServerInteraction
| CloseBannerServerInteraction
| OpenContextualBarServerInteraction
| UpdateContextualBarServerInteraction
| CloseContextualBarServerInteraction
| ReportErrorsServerInteraction;
export const isOpenModalServerInteraction = typia.createIs<OpenModalServerInteraction>();
export const isUpdateModalServerInteraction = typia.createIs<UpdateModalServerInteraction>();
export const isCloseModalServerInteraction = typia.createIs<CloseModalServerInteraction>();
export const isOpenBannerServerInteraction = typia.createIs<OpenBannerServerInteraction>();
export const isUpdateBannerServerInteraction = typia.createIs<UpdateBannerServerInteraction>();
export const isCloseBannerServerInteraction = typia.createIs<CloseBannerServerInteraction>();
export const isOpenContextualBarServerInteraction = typia.createIs<OpenContextualBarServerInteraction>();
export const isUpdateContextualBarServerInteraction = typia.createIs<UpdateContextualBarServerInteraction>();
export const isCloseContextualBarServerInteraction = typia.createIs<CloseContextualBarServerInteraction>();
export const isReportErrorsServerInteraction = typia.createIs<ReportErrorsServerInteraction>();
export const isServerInteraction = (input: unknown): input is ServerInteraction =>
isOpenModalServerInteraction(input) ||
isUpdateModalServerInteraction(input) ||
isCloseModalServerInteraction(input) ||
isOpenBannerServerInteraction(input) ||
isUpdateBannerServerInteraction(input) ||
isCloseBannerServerInteraction(input) ||
isOpenContextualBarServerInteraction(input) ||
isUpdateContextualBarServerInteraction(input) ||
isCloseContextualBarServerInteraction(input) ||
isReportErrorsServerInteraction(input);

View File

@@ -0,0 +1,134 @@
import typia from 'typia';
import type { View } from '../surfaces/View';
export type MessageBlockActionUserInteraction = {
type: 'blockAction';
actionId: string;
payload: {
blockId: string;
value: unknown;
};
container: {
type: 'message';
id: string;
};
mid: string;
tmid?: string;
rid: string;
triggerId: string;
};
export type ViewBlockActionUserInteraction = {
type: 'blockAction';
actionId: string;
payload: {
blockId: string;
value: unknown;
};
container: {
type: 'view';
id: string;
};
rid?: string;
triggerId: string;
};
export type ViewClosedUserInteraction = {
type: 'viewClosed';
payload: {
viewId: string;
view: View & {
id: string;
state: { [blockId: string]: { [key: string]: unknown } };
};
isCleared?: boolean;
};
rid?: string;
triggerId: string;
};
export type ViewSubmitUserInteraction = {
type: 'viewSubmit';
actionId?: undefined;
payload: {
view: View & {
id: string;
state: { [blockId: string]: { [key: string]: unknown } };
};
};
rid?: string;
triggerId: string;
viewId: string;
};
export type MessageBoxActionButtonUserInteraction = {
type: 'actionButton';
actionId: string;
payload: {
context: 'messageBoxAction';
message: string;
};
mid?: undefined;
tmid?: string;
rid: string;
triggerId: string;
};
export type UserDropdownActionButtonUserInteraction = {
type: 'actionButton';
actionId: string;
payload: {
context: 'userDropdownAction';
message?: undefined;
};
mid?: undefined;
tmid?: undefined;
rid?: undefined;
triggerId: string;
};
export type MesssageActionButtonUserInteraction = {
type: 'actionButton';
actionId: string;
payload: {
context: 'messageAction';
message?: undefined;
};
mid: string;
tmid?: string;
rid: string;
triggerId: string;
};
export type RoomActionButtonUserInteraction = {
type: 'actionButton';
actionId: string;
payload: {
context: 'roomAction';
message?: undefined;
};
mid?: undefined;
tmid?: undefined;
rid: string;
triggerId: string;
};
export type UserInteraction =
| MessageBlockActionUserInteraction
| ViewBlockActionUserInteraction
| ViewClosedUserInteraction
| ViewSubmitUserInteraction
| MessageBoxActionButtonUserInteraction
| UserDropdownActionButtonUserInteraction
| MesssageActionButtonUserInteraction
| RoomActionButtonUserInteraction;
export const isMessageBlockActionUserInteraction = typia.createIs<MessageBlockActionUserInteraction>();
export const isViewBlockActionUserInteraction = typia.createIs<ViewBlockActionUserInteraction>();
export const isViewClosedUserInteraction = typia.createIs<ViewClosedUserInteraction>();
export const isViewSubmitUserInteraction = typia.createIs<ViewSubmitUserInteraction>();
export const isMessageBoxActionButtonUserInteraction = typia.createIs<MessageBoxActionButtonUserInteraction>();
export const isUserDropdownActionButtonUserInteraction = typia.createIs<UserDropdownActionButtonUserInteraction>();
export const isMesssageActionButtonUserInteraction = typia.createIs<MesssageActionButtonUserInteraction>();
export const isRoomActionButtonUserInteraction = typia.createIs<RoomActionButtonUserInteraction>();

View File

@@ -0,0 +1,14 @@
declare global {
type Expect<T extends true> = T;
type EnumToMatchTaggedUnionTags<
TEnum extends Record<Uppercase<TTaggedUnion[TTagField]>, any>,
TTaggedUnion extends Record<string, any>,
TTagField extends keyof TTaggedUnion,
> = TEnum extends {
[K in TTaggedUnion as Uppercase<K[TTagField]>]: TEnum[Uppercase<K[TTagField]>];
}
? true
: false;
}
export {};

View File

@@ -0,0 +1,55 @@
import type { ActionableElement } from '../blocks/ActionableElement';
import type { Option } from '../blocks/Option';
import type { ButtonElement } from '../blocks/elements/ButtonElement';
import type { ChannelsSelectElement } from '../blocks/elements/ChannelsSelectElement';
import type { CheckboxElement } from '../blocks/elements/CheckboxElement';
import type { ConversationsSelectElement } from '../blocks/elements/ConversationsSelectElement';
import type { DatePickerElement } from '../blocks/elements/DatePickerElement';
import type { LinearScaleElement } from '../blocks/elements/LinearScaleElement';
import type { MultiChannelsSelectElement } from '../blocks/elements/MultiChannelsSelectElement';
import type { MultiConversationsSelectElement } from '../blocks/elements/MultiConversationsSelectElement';
import type { MultiStaticSelectElement } from '../blocks/elements/MultiStaticSelectElement';
import type { MultiUsersSelectElement } from '../blocks/elements/MultiUsersSelectElement';
import type { OverflowElement } from '../blocks/elements/OverflowElement';
import type { PlainTextInputElement } from '../blocks/elements/PlainTextInputElement';
import type { RadioButtonElement } from '../blocks/elements/RadioButtonElement';
import type { StaticSelectElement } from '../blocks/elements/StaticSelectElement';
import type { TimePickerElement } from '../blocks/elements/TimePickerElement';
import type { ToggleSwitchElement } from '../blocks/elements/ToggleSwitchElement';
import type { UsersSelectElement } from '../blocks/elements/UsersSelectElement';
export type ActionOf<TElement extends ActionableElement> = TElement extends ButtonElement
? ButtonElement['value']
: TElement extends ChannelsSelectElement
? Option['value']
: TElement extends ConversationsSelectElement
? unknown
: TElement extends DatePickerElement
? DatePickerElement['initialDate']
: TElement extends LinearScaleElement
? LinearScaleElement['initialValue']
: TElement extends MultiChannelsSelectElement
? Option['value'][]
: TElement extends MultiConversationsSelectElement
? unknown
: TElement extends MultiStaticSelectElement
? MultiStaticSelectElement['initialValue']
: TElement extends MultiUsersSelectElement
? Option['value'][]
: TElement extends OverflowElement
? OverflowElement['options'][number]['value']
: TElement extends PlainTextInputElement
? PlainTextInputElement['initialValue']
: TElement extends StaticSelectElement
? StaticSelectElement['initialValue']
: TElement extends UsersSelectElement
? Option['value']
: TElement extends ToggleSwitchElement
? Option['value'][]
: TElement extends RadioButtonElement
? Option['value'] | undefined
: TElement extends CheckboxElement
? Option['value'][] | undefined
: TElement extends TimePickerElement
? TimePickerElement['initialTime']
: never;

View File

@@ -0,0 +1,18 @@
const NONE = Symbol('none');
const BLOCK = Symbol('block');
const SECTION = Symbol('section');
const ACTION = Symbol('action');
const FORM = Symbol('form');
const CONTEXT = Symbol('context');
export const BlockContext = {
NONE,
BLOCK,
SECTION,
ACTION,
FORM,
CONTEXT,
} as const;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type BlockContext = (typeof BlockContext)[keyof typeof BlockContext];

View File

@@ -0,0 +1,8 @@
import type { BlockElement } from '..';
import type { BlockContext } from './BlockContext';
export type BlockElementRenderer<OutputElement, Block extends BlockElement = BlockElement> = (
blockElement: Block,
context: BlockContext,
index: number,
) => OutputElement | null;

View File

@@ -0,0 +1,14 @@
import type { BlockElementRenderer } from './BlockElementRenderer';
import type { LayoutBlockRenderer } from './LayoutBlockRenderer';
import type { TextObjectRenderer } from './TextObjectRenderer';
import type { BlockElement } from '../blocks/BlockElement';
import type { RenderableLayoutBlock } from '../blocks/RenderableLayoutBlock';
import type { TextObject } from '../blocks/TextObject';
export type BlockRenderers<T> = {
[B in RenderableLayoutBlock as B['type']]?: LayoutBlockRenderer<T, B>;
} & {
[B in TextObject as B['type']]: TextObjectRenderer<T, B>;
} & {
[B in BlockElement as B['type']]?: BlockElementRenderer<T, B>;
};

View File

@@ -0,0 +1,3 @@
export type Conditions = {
engine?: 'rocket.chat' | 'livechat';
};

View File

@@ -0,0 +1,8 @@
import type { BlockContext } from './BlockContext';
import type { RenderableLayoutBlock } from '../blocks/RenderableLayoutBlock';
export type LayoutBlockRenderer<T, B extends RenderableLayoutBlock = RenderableLayoutBlock> = (
layoutBlock: B,
context: typeof BlockContext.BLOCK,
index: number,
) => T | null;

View File

@@ -0,0 +1,89 @@
import { BlockContext } from './BlockContext';
import type { BlockRenderers } from './BlockRenderers';
import type { Conditions } from './Conditions';
import { renderBlockElement } from './renderBlockElement';
import { renderLayoutBlock } from './renderLayoutBlock';
import { renderTextObject } from './renderTextObject';
import { resolveConditionalBlocks } from './resolveConditionalBlocks';
import type { Block } from '../blocks/Block';
import type { BlockElement } from '../blocks/BlockElement';
import { LayoutBlockType } from '../blocks/LayoutBlockType';
import type { RenderableLayoutBlock } from '../blocks/RenderableLayoutBlock';
import type { TextObject } from '../blocks/TextObject';
import { isActionsBlockElement } from '../blocks/isActionsBlockElement';
import { isContextBlockElement } from '../blocks/isContextBlockElement';
import { isInputBlockElement } from '../blocks/isInputBlockElement';
import { isSectionBlockAccessoryElement } from '../blocks/isSectionBlockAccessoryElement';
import { isTextObject } from '../blocks/isTextObject';
import type { Markdown } from '../blocks/text/Markdown';
import type { PlainText } from '../blocks/text/PlainText';
export abstract class SurfaceRenderer<
TOutputObject,
TAllowedLayoutBlock extends RenderableLayoutBlock = RenderableLayoutBlock,
> implements BlockRenderers<TOutputObject> {
protected readonly allowedLayoutBlockTypes: Set<TAllowedLayoutBlock['type']>;
public constructor(allowedLayoutBlockTypes: TAllowedLayoutBlock['type'][]) {
this.allowedLayoutBlockTypes = new Set(allowedLayoutBlockTypes);
}
private isAllowedLayoutBlock = (block: Block): block is TAllowedLayoutBlock =>
this.allowedLayoutBlockTypes.has(block.type as TAllowedLayoutBlock['type']);
public render(blocks: readonly Block[], conditions?: Conditions): TOutputObject[] {
if (!Array.isArray(blocks)) {
return [];
}
return blocks
.flatMap(resolveConditionalBlocks(conditions))
.filter(this.isAllowedLayoutBlock)
.map(renderLayoutBlock(this))
.filter((value): value is TOutputObject => value !== null);
}
public renderTextObject(textObject: TextObject, index: number, context: BlockContext = BlockContext.NONE): TOutputObject | null {
return renderTextObject(this, context)(textObject, index);
}
public renderActionsBlockElement(block: BlockElement, index: number): TOutputObject | null {
if (this.allowedLayoutBlockTypes.has(LayoutBlockType.ACTIONS) === false && !isActionsBlockElement(block)) {
return null;
}
return renderBlockElement(this, BlockContext.ACTION)(block, index);
}
public renderContextBlockElement(block: TextObject | BlockElement, index: number): TOutputObject | null {
if (this.allowedLayoutBlockTypes.has(LayoutBlockType.CONTEXT) === false && !isContextBlockElement(block)) {
return null;
}
if (isTextObject(block)) {
return renderTextObject(this, BlockContext.CONTEXT)(block, index);
}
return renderBlockElement(this, BlockContext.CONTEXT)(block, index);
}
public renderInputBlockElement(block: BlockElement, index: number): TOutputObject | null {
if (this.allowedLayoutBlockTypes.has(LayoutBlockType.INPUT) === false && !isInputBlockElement(block)) {
return null;
}
return renderBlockElement(this, BlockContext.FORM)(block, index);
}
public renderSectionAccessoryBlockElement(block: BlockElement, index: number): TOutputObject | null {
if (this.allowedLayoutBlockTypes.has(LayoutBlockType.SECTION) === false && !isSectionBlockAccessoryElement(block)) {
return null;
}
return renderBlockElement(this, BlockContext.SECTION)(block, index);
}
public abstract plain_text(textObject: PlainText, context: BlockContext, index: number): TOutputObject | null;
public abstract mrkdwn(textObject: Markdown, context: BlockContext, index: number): TOutputObject | null;
}

View File

@@ -0,0 +1,8 @@
import type { BlockContext } from './BlockContext';
import type { TextObject } from '../blocks/TextObject';
export type TextObjectRenderer<OutputElement, Block extends TextObject = TextObject> = (
textObject: Block,
context: BlockContext,
index: number,
) => OutputElement | null;

View File

@@ -0,0 +1,10 @@
import type { Conditions } from './Conditions';
import type { SurfaceRenderer } from './SurfaceRenderer';
import type { Block } from '../blocks/Block';
import type { RenderableLayoutBlock } from '../blocks/RenderableLayoutBlock';
export const createSurfaceRenderer =
<TAllowedLayoutBlock extends RenderableLayoutBlock>() =>
<TOutputObject>(surfaceRenderer: SurfaceRenderer<TOutputObject, TAllowedLayoutBlock>, conditions?: Conditions) =>
(blocks: readonly { type: string }[]): TOutputObject[] =>
surfaceRenderer.render(blocks as Block[], conditions);

View File

@@ -0,0 +1,15 @@
import type { BlockContext } from './BlockContext';
import type { BlockElementRenderer } from './BlockElementRenderer';
import type { BlockRenderers } from './BlockRenderers';
import type { BlockElement } from '../blocks/BlockElement';
const getBlockElementRenderer = <T>(renderers: BlockRenderers<T>, type: BlockElement['type']): BlockElementRenderer<T> | undefined =>
renderers[type] as BlockElementRenderer<T> | undefined;
export const renderBlockElement =
<T>(renderers: BlockRenderers<T>, context: BlockContext) =>
(blockElement: BlockElement, index: number): T | null => {
const renderer = getBlockElementRenderer(renderers, blockElement.type);
return renderer?.call(renderers, blockElement, context, index) ?? null;
};

View File

@@ -0,0 +1,22 @@
import { BlockContext } from './BlockContext';
import type { BlockRenderers } from './BlockRenderers';
import type { LayoutBlockRenderer } from './LayoutBlockRenderer';
import type { LayoutBlock } from '../blocks/LayoutBlock';
import type { ConditionalBlock } from '../blocks/layout/ConditionalBlock';
const getLayoutBlockRenderer = <T>(
renderers: BlockRenderers<T>,
type: Exclude<LayoutBlock, ConditionalBlock>['type'],
): LayoutBlockRenderer<T> | undefined => renderers[type] as LayoutBlockRenderer<T> | undefined;
export const renderLayoutBlock =
<T>(renderers: BlockRenderers<T>) =>
(layoutBlock: Exclude<LayoutBlock, ConditionalBlock>, index: number): T | null => {
const renderer = getLayoutBlockRenderer(renderers, layoutBlock.type);
if (!renderer) {
return null;
}
return renderer.call(renderers, layoutBlock, BlockContext.BLOCK, index);
};

View File

@@ -0,0 +1,15 @@
import type { BlockContext } from './BlockContext';
import type { BlockRenderers } from './BlockRenderers';
import type { TextObjectRenderer } from './TextObjectRenderer';
import type { TextObject } from '../blocks/TextObject';
const getTextObjectRenderer = <T>(renderers: BlockRenderers<T>, type: TextObject['type']): TextObjectRenderer<T> | undefined =>
renderers[type] as TextObjectRenderer<T> | undefined;
export const renderTextObject =
<T>(renderers: BlockRenderers<T>, context: BlockContext) =>
(textObject: TextObject, index: number): T | null => {
const renderer = getTextObjectRenderer(renderers, textObject.type);
return renderer?.call(renderers, textObject, context, index) ?? null;
};

View File

@@ -0,0 +1,30 @@
import type { Conditions } from './Conditions';
import type { Block } from '../blocks/Block';
import { LayoutBlockType } from '../blocks/LayoutBlockType';
import type { ConditionalBlock } from '../blocks/layout/ConditionalBlock';
const conditionsMatch = (conditions: Conditions | undefined = undefined, filters: ConditionalBlock['when'] = {}): boolean => {
if (!conditions) {
return true;
}
if (Array.isArray(filters.engine) && !filters.engine.includes(conditions.engine)) {
return false;
}
return true;
};
export const resolveConditionalBlocks =
(conditions?: Conditions) =>
(block: Block): readonly Block[] => {
if (block.type !== LayoutBlockType.CONDITIONAL) {
return [block];
}
if (conditionsMatch(conditions, block.when)) {
return block.render;
}
return [];
};

View File

@@ -0,0 +1,15 @@
import typia from 'typia';
import type { LayoutBlock } from '../blocks/LayoutBlock';
/**
* An instance of a UiKit surface and its metadata.
*/
export type View = {
/** The ID of the app that created this view. */
appId: string;
/** The blocks that make up the content of the view. */
blocks: LayoutBlock[];
};
export const isView = typia.createIs<View>();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
import type { ActionsBlock } from '../../blocks/layout/ActionsBlock';
import type { CalloutBlock } from '../../blocks/layout/CalloutBlock';
import type { ContextBlock } from '../../blocks/layout/ContextBlock';
import type { DividerBlock } from '../../blocks/layout/DividerBlock';
import type { ImageBlock } from '../../blocks/layout/ImageBlock';
import type { SectionBlock } from '../../blocks/layout/SectionBlock';
import { SurfaceRenderer } from '../../rendering/SurfaceRenderer';
type AttachmentSurfaceLayoutBlock = ActionsBlock | ContextBlock | DividerBlock | ImageBlock | SectionBlock | CalloutBlock;
export abstract class UiKitParserAttachment<T> extends SurfaceRenderer<T, AttachmentSurfaceLayoutBlock> {
public constructor() {
super(['actions', 'context', 'divider', 'image', 'section', 'callout']);
}
}
export type AttachmentSurfaceLayout = AttachmentSurfaceLayoutBlock[];

View File

@@ -0,0 +1,3 @@
export type { AttachmentSurfaceLayout } from './UiKitParserAttachment';
export { UiKitParserAttachment } from './UiKitParserAttachment';
export { uiKitAttachment } from './uiKitAttachment';

View File

@@ -0,0 +1,4 @@
import type { AttachmentSurfaceLayout } from './UiKitParserAttachment';
import { createSurfaceRenderer } from '../../rendering/createSurfaceRenderer';
export const uiKitAttachment = createSurfaceRenderer<AttachmentSurfaceLayout[number]>();

View File

@@ -0,0 +1,22 @@
import type { Keys as IconName } from '@rocket.chat/icons';
import typia from 'typia';
import type { TextObject } from '../../blocks/TextObject';
import type { View } from '../View';
import type { BannerSurfaceLayout } from './UiKitParserBanner';
/**
* A view that is displayed as a banner.
*/
// Omitting `blocks` from `View` because array intersections are weird
export type BannerView = Omit<View, 'blocks'> & {
viewId: string;
inline?: boolean;
variant?: 'neutral' | 'info' | 'success' | 'warning' | 'danger';
icon?: IconName;
/** Title as plain string (legacy) or UiKit text object (e.g. { type: 'mrkdwn', text: '...' }). */
title?: string | TextObject;
blocks: BannerSurfaceLayout;
};
export const isBannerView = typia.createIs<BannerView>();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
import type { ActionsBlock } from '../../blocks/layout/ActionsBlock';
import type { CalloutBlock } from '../../blocks/layout/CalloutBlock';
import type { ContextBlock } from '../../blocks/layout/ContextBlock';
import type { DividerBlock } from '../../blocks/layout/DividerBlock';
import type { ImageBlock } from '../../blocks/layout/ImageBlock';
import type { InputBlock } from '../../blocks/layout/InputBlock';
import type { SectionBlock } from '../../blocks/layout/SectionBlock';
import { SurfaceRenderer } from '../../rendering/SurfaceRenderer';
type BannerSurfaceLayoutBlock = ActionsBlock | ContextBlock | DividerBlock | ImageBlock | InputBlock | SectionBlock | CalloutBlock;
export abstract class UiKitParserBanner<T> extends SurfaceRenderer<T, BannerSurfaceLayoutBlock> {
public constructor() {
super(['actions', 'context', 'divider', 'image', 'input', 'section']);
}
}
export type BannerSurfaceLayout = BannerSurfaceLayoutBlock[];

View File

@@ -0,0 +1,3 @@
export { type BannerView, isBannerView } from './BannerView';
export { type BannerSurfaceLayout, UiKitParserBanner } from './UiKitParserBanner';
export { uiKitBanner } from './uiKitBanner';

View File

@@ -0,0 +1,4 @@
import type { BannerSurfaceLayout } from './UiKitParserBanner';
import { createSurfaceRenderer } from '../../rendering/createSurfaceRenderer';
export const uiKitBanner = createSurfaceRenderer<BannerSurfaceLayout[number]>();

View File

@@ -0,0 +1,20 @@
import typia from 'typia';
import type { TextObject } from '../../blocks/TextObject';
import type { ButtonElement } from '../../blocks/elements/ButtonElement';
import type { View } from '../View';
import type { ContextualBarSurfaceLayout } from './UiKitParserContextualBar';
/**
* A view that is displayed as a contextual bar.
*/
// Omitting `blocks` from `View` because array intersections are weird
export type ContextualBarView = Omit<View, 'blocks'> & {
id: string;
title: TextObject;
close?: ButtonElement;
submit?: ButtonElement;
blocks: ContextualBarSurfaceLayout;
};
export const isContextualBarView = typia.createIs<ContextualBarView>();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
import type { ActionsBlock } from '../../blocks/layout/ActionsBlock';
import type { ContextBlock } from '../../blocks/layout/ContextBlock';
import type { DividerBlock } from '../../blocks/layout/DividerBlock';
import type { ImageBlock } from '../../blocks/layout/ImageBlock';
import type { InputBlock } from '../../blocks/layout/InputBlock';
import type { SectionBlock } from '../../blocks/layout/SectionBlock';
import { SurfaceRenderer } from '../../rendering/SurfaceRenderer';
type ContextualBarSurfaceLayoutBlock = ActionsBlock | ContextBlock | DividerBlock | ImageBlock | InputBlock | SectionBlock;
export abstract class UiKitParserContextualBar<OutputElement> extends SurfaceRenderer<OutputElement, ContextualBarSurfaceLayoutBlock> {
public constructor() {
super(['actions', 'context', 'divider', 'image', 'input', 'section']);
}
}
export type ContextualBarSurfaceLayout = ContextualBarSurfaceLayoutBlock[];

View File

@@ -0,0 +1,3 @@
export { type ContextualBarView, isContextualBarView } from './ContextualBarView';
export { UiKitParserContextualBar, type ContextualBarSurfaceLayout } from './UiKitParserContextualBar';
export { uiKitContextualBar } from './uiKitContextualBar';

View File

@@ -0,0 +1,4 @@
import type { ContextualBarSurfaceLayout } from './UiKitParserContextualBar';
import { createSurfaceRenderer } from '../../rendering/createSurfaceRenderer';
export const uiKitContextualBar = createSurfaceRenderer<ContextualBarSurfaceLayout[number]>();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
import type { ActionsBlock } from '../../blocks/layout/ActionsBlock';
import type { CalloutBlock } from '../../blocks/layout/CalloutBlock';
import type { ContextBlock } from '../../blocks/layout/ContextBlock';
import type { DividerBlock } from '../../blocks/layout/DividerBlock';
import type { ImageBlock } from '../../blocks/layout/ImageBlock';
import type { InfoCardBlock } from '../../blocks/layout/InfoCardBlock';
import type { PreviewBlock } from '../../blocks/layout/PreviewBlock';
import type { SectionBlock } from '../../blocks/layout/SectionBlock';
import type { VideoConferenceBlock } from '../../blocks/layout/VideoConferenceBlock';
import { SurfaceRenderer } from '../../rendering/SurfaceRenderer';
type MessageSurfaceLayoutBlock =
| ActionsBlock
| ContextBlock
| DividerBlock
| ImageBlock
| SectionBlock
| VideoConferenceBlock
| PreviewBlock
| CalloutBlock
| InfoCardBlock;
export abstract class UiKitParserMessage<OutputElement> extends SurfaceRenderer<OutputElement, MessageSurfaceLayoutBlock> {
public constructor() {
super(['actions', 'context', 'divider', 'image', 'section', 'preview', 'video_conf', 'callout']);
}
}
export type MessageSurfaceLayout = MessageSurfaceLayoutBlock[];

View File

@@ -0,0 +1,2 @@
export { UiKitParserMessage, type MessageSurfaceLayout } from './UiKitParserMessage';
export { uiKitMessage } from './uiKitMessage';

View File

@@ -0,0 +1,4 @@
import type { MessageSurfaceLayout } from './UiKitParserMessage';
import { createSurfaceRenderer } from '../../rendering/createSurfaceRenderer';
export const uiKitMessage = createSurfaceRenderer<MessageSurfaceLayout[number]>();

View File

@@ -0,0 +1,21 @@
import typia from 'typia';
import type { TextObject } from '../../blocks/TextObject';
import type { ButtonElement } from '../../blocks/elements/ButtonElement';
import type { View } from '../View';
import type { ModalSurfaceLayout } from './UiKitParserModal';
/**
* A view that is displayed as a modal dialog.
*/
// Omitting `blocks` from `View` because array intersections are weird
export type ModalView = Omit<View, 'blocks'> & {
id: string;
showIcon?: boolean;
title: TextObject;
close?: ButtonElement;
submit?: ButtonElement;
blocks: ModalSurfaceLayout;
};
export const isModalView = typia.createIs<ModalView>();

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More