Merge branch 'frontend-core' of github.com:Budibase/budibase into experimental-hbs-caching
This commit is contained in:
commit
0e59be9375
58 changed files with 2091 additions and 244 deletions
214
i18n/README.jp.md
Normal file
214
i18n/README.jp.md
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://www.budibase.com">
|
||||||
|
<img alt="Budibase" src="https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg" width="60" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<h1 align="center">
|
||||||
|
Budibase
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
使って楽しいローコードプラットフォーム
|
||||||
|
</h3>
|
||||||
|
<p align="center">
|
||||||
|
Budibaseはオープンソースのローコードプラットフォームで、生産性を向上させるツールを簡単に構築することができます。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
🤖 🎨 🚀
|
||||||
|
</h3>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase design ui" src="https://res.cloudinary.com/daog6scxm/image/upload/v1633524049/ui/design-ui-wide-mobile_gdaveq.jpg">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/Budibase/budibase/releases">
|
||||||
|
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/Budibase/budibase/total">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/Budibase/budibase/releases">
|
||||||
|
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/Budibase/budibase">
|
||||||
|
</a>
|
||||||
|
<a href="https://twitter.com/intent/follow?screen_name=budibase">
|
||||||
|
<img src="https://img.shields.io/twitter/follow/budibase?style=social" alt="Follow @budibase" />
|
||||||
|
</a>
|
||||||
|
<img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Code of conduct" />
|
||||||
|
<a href="https://codecov.io/gh/Budibase/budibase">
|
||||||
|
<img src="https://codecov.io/gh/Budibase/budibase/graph/badge.svg?token=E8W2ZFXQOH"/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
<a href="https://docs.budibase.com/getting-started">はじめに</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://docs.budibase.com">ドキュメント</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas">機能リクエスト</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://github.com/Budibase/budibase/issues">バグ報告</a>
|
||||||
|
<span> · </span>
|
||||||
|
サポート: <a href="https://github.com/Budibase/budibase/discussions">ディスカッション</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
## ✨ 特徴
|
||||||
|
|
||||||
|
### "本物"のソフトウェアを構築できます
|
||||||
|
ほかのプラットフォームとは違い、Budibaseだけでシングルページのアプリケーションを制作し完成させることができます。Budibaseで作られたアプリケーションは素晴らしいパフォーマンスを持っており、レスポンシブデザインにも対応しています。ユーザー達にいい印象を与えること間違いなしでしょう!
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 拡張性が高くオープンソース
|
||||||
|
Budibaseはオープンソースで、GPL v3ライセンスの下に公開されています。このことは、Budibaseが常にあなたのそばにいるという安心感を与えてくれることでしょう。そして、私たちは開発者に優しい環境を提供しているので、あなたは好きなだけにソースコードをフォークして改造、もしくは直接Budibaseにコントリビュートすることができます。
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 既存のデータ、もしくは一から始める
|
||||||
|
Budibaseはいろんなツールから既存のデータを使用できます。たとえばMongoDB、CouchDB、 PostgreSQL、MySQL、Airtable、S3、DynamoDB、REST APIなど。ほかのプラットフォームにない特徴として、Budibaseはデータなしの状態でビジネスアプリケーションの構築を一から始めることができます。 [新しいデータリソースをリクエスト](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas)。
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase data" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/data_n1tlhf.png">
|
||||||
|
</p>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### パワフルな内蔵コンポーネントでアプリケーションを設計し構築
|
||||||
|
|
||||||
|
Budibaseには、美しくデザインされた強力なコンポーネントが付属しており、それら使用しUIを簡単に構築することができます。また、CSSによるスタイリングオプションも豊富に用意されているので、よりクリエイティブな表現もも可能です。
|
||||||
|
[Request new component](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas)。
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase design" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970243/Out%20of%20beta%20launch/design-like-a-pro_qhlfeu.gif">
|
||||||
|
</p>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### プロセスを自動化し、ほかのツールと連携し、Webhookをでつながる!
|
||||||
|
定型化した作業を自動化して時間を節約しましょう。Webhookに接続、Eメールの自動送信など、すべてBudibaseに任せましょう。 こちらで簡単に [新しいオートメーションを作る](https://github.com/Budibase/automations)または[新しいオートメーションをリクエストすることができます](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas)。
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase automations" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970486/Out%20of%20beta%20launch/automation_riro7u.png">
|
||||||
|
</p>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 使い親しんだツールとの統合
|
||||||
|
Budibaseは多くの人気ツールと統合されており、あなたのニーズに合わせたパーフェクトなアプリケーションを構築することができます。
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase integrations" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/integrations_kc7dqt.png">
|
||||||
|
</p>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 管理者のパラダイス
|
||||||
|
Budibaseはどんな規模のプロジェクトにも柔軟に対応できます。Budibaseを使えば、個人または組織のサーバーでセルフホスティングし、ユーザー、オンボーディング、SMTP、アプリ、グループ、テーマなどをひとまとめに管理することが可能です。また、ユーザーやグループにアプリポータルを提供し、グループ管理者にユーザー管理を委ねることも可能です。
|
||||||
|
- プロモーションビデオを視聴する: https://youtu.be/xoljVpty_Kw
|
||||||
|
|
||||||
|
<br /><br /><br />
|
||||||
|
|
||||||
|
## 🏁 始めましょう
|
||||||
|
|
||||||
|
<a href="https://docs.budibase.com/self-hosting/self-host"><img src="https://res.cloudinary.com/daog6scxm/image/upload/v1634808888/logo/deploy_npl9za.png" /></a>
|
||||||
|
|
||||||
|
Docker、KubernetesもしくはDegital Oceanを使用しセルフホスティングするか、セルフホスティングに困難がある、もしくは今すぐ開始したい場合はBudibase Cloudを使用しすぐに始めましょう。
|
||||||
|
|
||||||
|
### [Budibaseをセルフホスティングする](https://docs.budibase.com/self-hosting/self-host)
|
||||||
|
|
||||||
|
### [Budibase Cloudを使用する](https://budibase.com)
|
||||||
|
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
## 🎓 Budibaseを学ぶ
|
||||||
|
|
||||||
|
Budibaseのドキュメント[はここです](https://docs.budibase.com)。
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
## 💬 コミュニティ
|
||||||
|
|
||||||
|
もし何か問題がある、もしくはBudibaseコミュニティのほかのユーザーと交流したいのであれば私たちの[Github discussions](https://github.com/Budibase/budibase/discussions)までお越しください。
|
||||||
|
|
||||||
|
<br /><br /><br />
|
||||||
|
|
||||||
|
|
||||||
|
## ❗ 行動規範
|
||||||
|
|
||||||
|
Budibase は、すべての人を歓迎し、多様で、ハラスメントのない環境を提供することに尽力しています。Budibase コミュニティに参加するすべての人たちが私たちの[**行動規範**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md)を遵守していただくことお願いします。必ず読んでください。
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
|
||||||
|
## 🙌 Budibaseにコントリビュート
|
||||||
|
|
||||||
|
|
||||||
|
バグレポートからプルリクエストの作成まで、すべての貢献は感謝、そして歓迎されております。新しい新機能の実装やAPIの変更を計画している場合は、まずIssueを作成してください。これであなたの貴重な考えは私たちにも伝わり、無駄とはなりません。
|
||||||
|
|
||||||
|
### どこから始めるか混乱していますか?
|
||||||
|
ここはコントリビュートをはじめるための最適な場所です! [First time issues project](https://github.com/Budibase/budibase/projects/22).
|
||||||
|
|
||||||
|
### リポジトリの構成
|
||||||
|
Budibaseは、lernaによってmonorepo方式で管理されています。budibase パッケージのビルドと公開はlernaによって管理されています。Budibaseを構成するパッケージは以下の通り:
|
||||||
|
|
||||||
|
- [packages/builder](https://github.com/Budibase/budibase/tree/HEAD/packages/builder) - budibase builder クライアントサイドのsvelteアプリケーションのコードが含まれています。
|
||||||
|
|
||||||
|
- [packages/client](https://github.com/Budibase/budibase/tree/HEAD/packages/client) - ブラウザ上で動作するモジュールで、JSONの定義を読み取り、そこから"生きている"Webアプリケーションを作成します。
|
||||||
|
|
||||||
|
- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - budibaseのサーバーです。この Koa アプリは、builder アプリと budibase アプリの JS を提供し、データベースとファイル システムと対話するための API を提供する役割を担っています。
|
||||||
|
|
||||||
|
詳しくは[CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md)をご覧ください。
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
|
||||||
|
## 📝 ライセンス
|
||||||
|
|
||||||
|
Budibase はオープンソースであり、[GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html)ライセンスの下に公開されています。クライアントとコンポーネントライブラリは [MPL](https://directory.fsf.org/wiki/License:MPL-2.0)で公開されています - ですから、あなたが制作したアプリケーションはどのようなライセンスでも公開することができます。
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
## ⭐ スター数の履歴
|
||||||
|
|
||||||
|
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
|
||||||
|
|
||||||
|
ビルダーのアップデートの間に問題が発生する場合は[ここ](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting)を参考に環境をクリアにしてください。
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
## Contributors ✨
|
||||||
|
|
||||||
|
すばらしい皆さまに感謝しかありません。([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
<!-- markdownlint-disable -->
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="http://martinmck.com"><img src="https://avatars1.githubusercontent.com/u/11256663?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin McKeaveney</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Tests">⚠️</a> <a href="#infra-shogunpurple" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||||
|
<td align="center"><a href="http://www.michaeldrury.co.uk/"><img src="https://avatars2.githubusercontent.com/u/4407001?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Drury</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Tests">⚠️</a> <a href="#infra-mike12345567" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/aptkingston"><img src="https://avatars3.githubusercontent.com/u/9075550?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Kingston</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Tests">⚠️</a> <a href="#design-aptkingston" title="Design">🎨</a></td>
|
||||||
|
<td align="center"><a href="https://budibase.com/"><img src="https://avatars3.githubusercontent.com/u/3524181?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Shanks</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/kevmodrome"><img src="https://avatars3.githubusercontent.com/u/534488?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Åberg Kultalahti</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://www.budibase.com/"><img src="https://avatars2.githubusercontent.com/u/49767913?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=joebudi" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=joebudi" title="Code">💻</a> <a href="#content-joebudi" title="Content">🖋</a> <a href="#design-joebudi" title="Design">🎨</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Rory-Powell"><img src="https://avatars.githubusercontent.com/u/8755148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rory Powell</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Tests">⚠️</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/PClmnt"><img src="https://avatars.githubusercontent.com/u/5665926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peter Clement</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Conor-Mack"><img src="https://avatars1.githubusercontent.com/u/36074859?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Conor_Mack</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Conor-Mack" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=Conor-Mack" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/pngwn"><img src="https://avatars1.githubusercontent.com/u/12937446?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pngwn</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=pngwn" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=pngwn" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/HugoLd"><img src="https://avatars0.githubusercontent.com/u/26521848?v=4?s=100" width="100px;" alt=""/><br /><sub><b>HugoLd</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=HugoLd" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/victoriasloan"><img src="https://avatars.githubusercontent.com/u/9913651?v=4?s=100" width="100px;" alt=""/><br /><sub><b>victoriasloan</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=victoriasloan" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/yashank09"><img src="https://avatars.githubusercontent.com/u/37672190?v=4?s=100" width="100px;" alt=""/><br /><sub><b>yashank09</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=yashank09" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/SOVLOOKUP"><img src="https://avatars.githubusercontent.com/u/53158137?v=4?s=100" width="100px;" alt=""/><br /><sub><b>SOVLOOKUP</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=SOVLOOKUP" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/seoulaja"><img src="https://avatars.githubusercontent.com/u/15101654?v=4?s=100" width="100px;" alt=""/><br /><sub><b>seoulaja</b></sub></a><br /><a href="#translation-seoulaja" title="Translation">🌍</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/mslourens"><img src="https://avatars.githubusercontent.com/u/1907152?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Maurits Lourens</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mslourens" title="Tests">⚠️</a> <a href="https://github.com/Budibase/budibase/commits?author=mslourens" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- markdownlint-restore -->
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
|
このプロジェクトは、[all-contributors](https://github.com/all-contributors/all-contributors)仕様に準拠しています。どのような貢献でも歓迎します。
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/backend-core",
|
"name": "@budibase/backend-core",
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"description": "Budibase backend core libraries used in server and worker",
|
"description": "Budibase backend core libraries used in server and worker",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/bbui",
|
"name": "@budibase/bbui",
|
||||||
"description": "A UI solution used in the different Budibase projects.",
|
"description": "A UI solution used in the different Budibase projects.",
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"module": "dist/bbui.es.js",
|
"module": "dist/bbui.es.js",
|
||||||
|
@ -79,6 +79,7 @@
|
||||||
"@spectrum-css/underlay": "^2.0.9",
|
"@spectrum-css/underlay": "^2.0.9",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.4",
|
||||||
|
"easymde": "^2.16.1",
|
||||||
"svelte-flatpickr": "^3.2.3",
|
"svelte-flatpickr": "^3.2.3",
|
||||||
"svelte-portal": "^1.0.0"
|
"svelte-portal": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import "@spectrum-css/button/dist/index-vars.css"
|
import "@spectrum-css/button/dist/index-vars.css"
|
||||||
|
import Tooltip from "../Tooltip/Tooltip.svelte"
|
||||||
|
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let size = "M"
|
export let size = "M"
|
||||||
|
@ -11,36 +12,67 @@
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let icon = undefined
|
export let icon = undefined
|
||||||
export let active = false
|
export let active = false
|
||||||
|
export let tooltip = undefined
|
||||||
|
|
||||||
|
let showTooltip = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<div class:container={!!tooltip}>
|
||||||
class:spectrum-Button--cta={cta}
|
<button
|
||||||
class:spectrum-Button--primary={primary}
|
class:spectrum-Button--cta={cta}
|
||||||
class:spectrum-Button--secondary={secondary}
|
class:spectrum-Button--primary={primary}
|
||||||
class:spectrum-Button--warning={warning}
|
class:spectrum-Button--secondary={secondary}
|
||||||
class:spectrum-Button--overBackground={overBackground}
|
class:spectrum-Button--warning={warning}
|
||||||
class:spectrum-Button--quiet={quiet}
|
class:spectrum-Button--overBackground={overBackground}
|
||||||
class:active
|
class:spectrum-Button--quiet={quiet}
|
||||||
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
|
class:active
|
||||||
{disabled}
|
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
|
||||||
on:click|preventDefault
|
{disabled}
|
||||||
>
|
on:click|preventDefault
|
||||||
{#if icon}
|
on:mouseover={() => (showTooltip = true)}
|
||||||
<svg
|
on:mouseleave={() => (showTooltip = false)}
|
||||||
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
|
>
|
||||||
focusable="false"
|
{#if icon}
|
||||||
aria-hidden="true"
|
<svg
|
||||||
aria-label={icon}
|
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
|
||||||
>
|
focusable="false"
|
||||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
aria-hidden="true"
|
||||||
</svg>
|
aria-label={icon}
|
||||||
|
>
|
||||||
|
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
{#if $$slots}
|
||||||
|
<span class="spectrum-Button-label"><slot /></span>
|
||||||
|
{/if}
|
||||||
|
{#if !disabled && tooltip}
|
||||||
|
<div class="tooltip-icon">
|
||||||
|
<svg
|
||||||
|
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
|
||||||
|
focusable="false"
|
||||||
|
aria-hidden="true"
|
||||||
|
aria-label="Info"
|
||||||
|
>
|
||||||
|
<use xlink:href="#spectrum-icon-18-InfoOutline" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
{#if showTooltip && tooltip}
|
||||||
|
<div class="position">
|
||||||
|
<div class="tooltip">
|
||||||
|
<Tooltip textWrapping={true} direction={"bottom"} text={tooltip} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $$slots}
|
</div>
|
||||||
<span class="spectrum-Button-label"><slot /></span>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
.spectrum-Button-label {
|
.spectrum-Button-label {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -49,4 +81,23 @@
|
||||||
.active {
|
.active {
|
||||||
color: var(--spectrum-global-color-blue-600) !important;
|
color: var(--spectrum-global-color-blue-600) !important;
|
||||||
}
|
}
|
||||||
|
.tooltip {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 100;
|
||||||
|
width: 160px;
|
||||||
|
text-align: center;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
top: -5px;
|
||||||
|
}
|
||||||
|
.position {
|
||||||
|
position: relative;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
.tooltip-icon {
|
||||||
|
padding-left: var(--spacing-m);
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,16 +14,20 @@
|
||||||
export let value = null
|
export let value = null
|
||||||
export let placeholder = null
|
export let placeholder = null
|
||||||
export let appendTo = undefined
|
export let appendTo = undefined
|
||||||
|
export let timeOnly = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const flatpickrId = `${uuid()}-wrapper`
|
const flatpickrId = `${uuid()}-wrapper`
|
||||||
let open = false
|
let open = false
|
||||||
let flatpickr
|
let flatpickr, flatpickrOptions, isTimeOnly
|
||||||
|
|
||||||
|
$: isTimeOnly = !timeOnly && value ? !isNaN(new Date(`0-${value}`)) : timeOnly
|
||||||
$: flatpickrOptions = {
|
$: flatpickrOptions = {
|
||||||
element: `#${flatpickrId}`,
|
element: `#${flatpickrId}`,
|
||||||
enableTime: enableTime || false,
|
enableTime: isTimeOnly || enableTime || false,
|
||||||
|
noCalendar: isTimeOnly || false,
|
||||||
altInput: true,
|
altInput: true,
|
||||||
altFormat: enableTime ? "F j Y, H:i" : "F j, Y",
|
altFormat: isTimeOnly ? "H:i" : enableTime ? "F j Y, H:i" : "F j, Y",
|
||||||
wrap: true,
|
wrap: true,
|
||||||
appendTo,
|
appendTo,
|
||||||
disableMobile: "true",
|
disableMobile: "true",
|
||||||
|
@ -35,6 +39,11 @@
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
newValue = newValue.toISOString()
|
newValue = newValue.toISOString()
|
||||||
}
|
}
|
||||||
|
// if time only set date component to today
|
||||||
|
if (timeOnly) {
|
||||||
|
const todayDate = new Date().toISOString().split("T")[0]
|
||||||
|
newValue = `${todayDate}T${newValue.split("T")[1]}`
|
||||||
|
}
|
||||||
dispatch("change", newValue)
|
dispatch("change", newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +76,11 @@
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
let date
|
let date
|
||||||
if (val instanceof Date) {
|
let time = new Date(`0-${val}`)
|
||||||
|
// it is a string like 00:00:00, just time
|
||||||
|
if (timeOnly || (typeof val === "string" && !isNaN(time))) {
|
||||||
|
date = time
|
||||||
|
} else if (val instanceof Date) {
|
||||||
// Use real date obj if already parsed
|
// Use real date obj if already parsed
|
||||||
date = val
|
date = val
|
||||||
} else if (isNaN(val)) {
|
} else if (isNaN(val)) {
|
||||||
|
@ -77,7 +90,7 @@
|
||||||
// Treat as numerical timestamp
|
// Treat as numerical timestamp
|
||||||
date = new Date(parseInt(val))
|
date = new Date(parseInt(val))
|
||||||
}
|
}
|
||||||
const time = date.getTime()
|
time = date.getTime()
|
||||||
if (isNaN(time)) {
|
if (isNaN(time)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -88,69 +101,71 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Flatpickr
|
{#key isTimeOnly}
|
||||||
bind:flatpickr
|
<Flatpickr
|
||||||
value={parseDate(value)}
|
bind:flatpickr
|
||||||
on:open={onOpen}
|
value={parseDate(value)}
|
||||||
on:close={onClose}
|
on:open={onOpen}
|
||||||
options={flatpickrOptions}
|
on:close={onClose}
|
||||||
on:change={handleChange}
|
options={flatpickrOptions}
|
||||||
element={`#${flatpickrId}`}
|
on:change={handleChange}
|
||||||
>
|
element={`#${flatpickrId}`}
|
||||||
<div
|
|
||||||
id={flatpickrId}
|
|
||||||
class:is-disabled={disabled}
|
|
||||||
class:is-invalid={!!error}
|
|
||||||
class="flatpickr spectrum-InputGroup spectrum-Datepicker"
|
|
||||||
class:is-focused={open}
|
|
||||||
aria-readonly="false"
|
|
||||||
aria-required="false"
|
|
||||||
aria-haspopup="true"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
on:click={flatpickr?.open}
|
id={flatpickrId}
|
||||||
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
|
||||||
class:is-disabled={disabled}
|
class:is-disabled={disabled}
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
|
class="flatpickr spectrum-InputGroup spectrum-Datepicker"
|
||||||
|
class:is-focused={open}
|
||||||
|
aria-readonly="false"
|
||||||
|
aria-required="false"
|
||||||
|
aria-haspopup="true"
|
||||||
>
|
>
|
||||||
{#if !!error}
|
<div
|
||||||
|
on:click={flatpickr?.open}
|
||||||
|
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
||||||
|
class:is-disabled={disabled}
|
||||||
|
class:is-invalid={!!error}
|
||||||
|
>
|
||||||
|
{#if !!error}
|
||||||
|
<svg
|
||||||
|
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
||||||
|
focusable="false"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<use xlink:href="#spectrum-icon-18-Alert" />
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
<input
|
||||||
|
data-input
|
||||||
|
type="text"
|
||||||
|
{disabled}
|
||||||
|
class="spectrum-Textfield-input spectrum-InputGroup-input"
|
||||||
|
{placeholder}
|
||||||
|
{id}
|
||||||
|
{value}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button"
|
||||||
|
tabindex="-1"
|
||||||
|
{disabled}
|
||||||
|
class:is-invalid={!!error}
|
||||||
|
on:click={flatpickr?.open}
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
class="spectrum-Icon spectrum-Icon--sizeM"
|
||||||
focusable="false"
|
focusable="false"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
aria-label="Calendar"
|
||||||
>
|
>
|
||||||
<use xlink:href="#spectrum-icon-18-Alert" />
|
<use xlink:href="#spectrum-icon-18-Calendar" />
|
||||||
</svg>
|
</svg>
|
||||||
{/if}
|
</button>
|
||||||
<input
|
|
||||||
data-input
|
|
||||||
type="text"
|
|
||||||
{disabled}
|
|
||||||
class="spectrum-Textfield-input spectrum-InputGroup-input"
|
|
||||||
{placeholder}
|
|
||||||
{id}
|
|
||||||
{value}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
</Flatpickr>
|
||||||
type="button"
|
{/key}
|
||||||
class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button"
|
|
||||||
tabindex="-1"
|
|
||||||
{disabled}
|
|
||||||
class:is-invalid={!!error}
|
|
||||||
on:click={flatpickr?.open}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="spectrum-Icon spectrum-Icon--sizeM"
|
|
||||||
focusable="false"
|
|
||||||
aria-hidden="true"
|
|
||||||
aria-label="Calendar"
|
|
||||||
>
|
|
||||||
<use xlink:href="#spectrum-icon-18-Calendar" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Flatpickr>
|
|
||||||
{#if open}
|
{#if open}
|
||||||
<div class="overlay" on:mousedown|self={flatpickr?.close} />
|
<div class="overlay" on:mousedown|self={flatpickr?.close} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
42
packages/bbui/src/Form/Core/RichTextField.svelte
Normal file
42
packages/bbui/src/Form/Core/RichTextField.svelte
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<script>
|
||||||
|
import MarkdownEditor from "../../Markdown/MarkdownEditor.svelte"
|
||||||
|
|
||||||
|
export let value = ""
|
||||||
|
export let placeholder = null
|
||||||
|
export let disabled = false
|
||||||
|
export let error = null
|
||||||
|
export let height = null
|
||||||
|
export let id = null
|
||||||
|
export let fullScreenOffset = null
|
||||||
|
export let easyMDEOptions = null
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class:error>
|
||||||
|
<MarkdownEditor
|
||||||
|
{value}
|
||||||
|
{placeholder}
|
||||||
|
{height}
|
||||||
|
{id}
|
||||||
|
{fullScreenOffset}
|
||||||
|
{disabled}
|
||||||
|
{easyMDEOptions}
|
||||||
|
on:change
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.error :global(.EasyMDEContainer .editor-toolbar) {
|
||||||
|
border-top-color: var(--spectrum-semantic-negative-color-default);
|
||||||
|
border-left-color: var(--spectrum-semantic-negative-color-default);
|
||||||
|
border-right-color: var(--spectrum-semantic-negative-color-default);
|
||||||
|
}
|
||||||
|
.error :global(.EasyMDEContainer .CodeMirror) {
|
||||||
|
border-bottom-color: var(--spectrum-semantic-negative-color-default);
|
||||||
|
border-left-color: var(--spectrum-semantic-negative-color-default);
|
||||||
|
border-right-color: var(--spectrum-semantic-negative-color-default);
|
||||||
|
}
|
||||||
|
.error :global(.EasyMDEContainer .editor-preview-side) {
|
||||||
|
border-bottom-color: var(--spectrum-semantic-negative-color-default);
|
||||||
|
border-right-color: var(--spectrum-semantic-negative-color-default);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -22,11 +22,23 @@
|
||||||
dispatch("change", event.target.value)
|
dispatch("change", event.target.value)
|
||||||
focus = false
|
focus = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getStyleString = (attribute, value) => {
|
||||||
|
if (!attribute || value == null) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if (isNaN(value)) {
|
||||||
|
return `${attribute}:${value};`
|
||||||
|
}
|
||||||
|
return `${attribute}:${value}px;`
|
||||||
|
}
|
||||||
|
|
||||||
|
$: heightString = getStyleString("height", height)
|
||||||
|
$: minHeightString = getStyleString("min-height", minHeight)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={(height ? `height: ${height}px;` : "") +
|
style={`${heightString}${minHeightString}`}
|
||||||
(minHeight ? `min-height: ${minHeight}px` : "")}
|
|
||||||
class="spectrum-Textfield spectrum-Textfield--multiline"
|
class="spectrum-Textfield spectrum-Textfield--multiline"
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
class:is-disabled={disabled}
|
class:is-disabled={disabled}
|
||||||
|
|
|
@ -10,3 +10,4 @@ export { default as CoreSearch } from "./Search.svelte"
|
||||||
export { default as CoreDatePicker } from "./DatePicker.svelte"
|
export { default as CoreDatePicker } from "./DatePicker.svelte"
|
||||||
export { default as CoreDropzone } from "./Dropzone.svelte"
|
export { default as CoreDropzone } from "./Dropzone.svelte"
|
||||||
export { default as CoreStepper } from "./Stepper.svelte"
|
export { default as CoreStepper } from "./Stepper.svelte"
|
||||||
|
export { default as CoreRichTextField } from "./RichTextField.svelte"
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let error = null
|
export let error = null
|
||||||
export let enableTime = true
|
export let enableTime = true
|
||||||
|
export let timeOnly = false
|
||||||
export let placeholder = null
|
export let placeholder = null
|
||||||
export let appendTo = undefined
|
export let appendTo = undefined
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
{value}
|
{value}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{enableTime}
|
{enableTime}
|
||||||
|
{timeOnly}
|
||||||
{appendTo}
|
{appendTo}
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
/>
|
/>
|
||||||
|
|
36
packages/bbui/src/Form/RichTextField.svelte
Normal file
36
packages/bbui/src/Form/RichTextField.svelte
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<script>
|
||||||
|
import Field from "./Field.svelte"
|
||||||
|
import RichTextField from "./Core/RichTextField.svelte"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
export let value = null
|
||||||
|
export let label = null
|
||||||
|
export let labelPosition = "above"
|
||||||
|
export let placeholder = null
|
||||||
|
export let disabled = false
|
||||||
|
export let error = null
|
||||||
|
export let height = null
|
||||||
|
export let id = null
|
||||||
|
export let fullScreenOffset = null
|
||||||
|
export let easyMDEOptions = null
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
const onChange = e => {
|
||||||
|
value = e.detail
|
||||||
|
dispatch("change", e.detail)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Field {label} {labelPosition} {error}>
|
||||||
|
<RichTextField
|
||||||
|
{error}
|
||||||
|
{disabled}
|
||||||
|
{value}
|
||||||
|
{placeholder}
|
||||||
|
{height}
|
||||||
|
{id}
|
||||||
|
{fullScreenOffset}
|
||||||
|
{easyMDEOptions}
|
||||||
|
on:change={onChange}
|
||||||
|
/>
|
||||||
|
</Field>
|
60
packages/bbui/src/Markdown/MarkdownEditor.svelte
Normal file
60
packages/bbui/src/Markdown/MarkdownEditor.svelte
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<script>
|
||||||
|
import SpectrumMDE from "./SpectrumMDE.svelte"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
export let value = null
|
||||||
|
export let height = null
|
||||||
|
export let placeholder = null
|
||||||
|
export let id = null
|
||||||
|
export let fullScreenOffset = 0
|
||||||
|
export let disabled = false
|
||||||
|
export let easyMDEOptions
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let latestValue
|
||||||
|
let mde
|
||||||
|
|
||||||
|
// Ensure the value is updated if the value prop changes outside the editor's
|
||||||
|
// control
|
||||||
|
$: checkValue(value)
|
||||||
|
$: mde?.codemirror.on("change", debouncedUpdate)
|
||||||
|
|
||||||
|
const checkValue = val => {
|
||||||
|
if (mde && val !== latestValue) {
|
||||||
|
mde.value(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const debounce = (fn, interval) => {
|
||||||
|
let timeout
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = setTimeout(fn, interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
latestValue = mde.value()
|
||||||
|
dispatch("change", latestValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debounce the update function to avoid spamming it constantly
|
||||||
|
const debouncedUpdate = debounce(update, 250)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#key height}
|
||||||
|
<SpectrumMDE
|
||||||
|
bind:mde
|
||||||
|
scroll={true}
|
||||||
|
{height}
|
||||||
|
{id}
|
||||||
|
{fullScreenOffset}
|
||||||
|
{disabled}
|
||||||
|
easyMDEOptions={{
|
||||||
|
initialValue: value,
|
||||||
|
placeholder,
|
||||||
|
...easyMDEOptions,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/key}
|
70
packages/bbui/src/Markdown/MarkdownViewer.svelte
Normal file
70
packages/bbui/src/Markdown/MarkdownViewer.svelte
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<script>
|
||||||
|
import SpectrumMDE from "./SpectrumMDE.svelte"
|
||||||
|
|
||||||
|
export let value
|
||||||
|
export let height
|
||||||
|
|
||||||
|
let mde
|
||||||
|
|
||||||
|
// Keep the value up to date
|
||||||
|
$: mde && mde.value(value || "")
|
||||||
|
$: {
|
||||||
|
if (mde && !mde.isPreviewActive()) {
|
||||||
|
mde.togglePreview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="markdown-viewer" style="height:{height};">
|
||||||
|
<SpectrumMDE
|
||||||
|
bind:mde
|
||||||
|
scroll={false}
|
||||||
|
easyMDEOptions={{
|
||||||
|
initialValue: value,
|
||||||
|
toolbar: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.markdown-viewer {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
/* Remove padding, borders and background colors */
|
||||||
|
.markdown-viewer :global(.editor-preview) {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.markdown-viewer :global(.CodeMirror) {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.markdown-viewer :global(.EasyMDEContainer) {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
/* Hide the actual code editor */
|
||||||
|
.markdown-viewer :global(.CodeMirror-scroll) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/*Hide the scrollbar*/
|
||||||
|
.markdown-viewer :global(.CodeMirror-vscrollbar) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
/*Position relatively so we only consume whatever space we need */
|
||||||
|
.markdown-viewer :global(.editor-preview-full) {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
/* Remove margin on the first and last components to fully trim the preview */
|
||||||
|
.markdown-viewer :global(.editor-preview-full > :first-child) {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.markdown-viewer :global(.editor-preview-full > :last-child) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
/* Code blocks in preview */
|
||||||
|
.markdown-viewer :global(.editor-preview-full pre) {
|
||||||
|
background: var(--spectrum-global-color-gray-200);
|
||||||
|
}
|
||||||
|
</style>
|
184
packages/bbui/src/Markdown/SpectrumMDE.svelte
Normal file
184
packages/bbui/src/Markdown/SpectrumMDE.svelte
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
<script>
|
||||||
|
import EasyMDE from "easymde"
|
||||||
|
import "easymde/dist/easymde.min.css"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
export let height = null
|
||||||
|
export let scroll = true
|
||||||
|
export let easyMDEOptions = null
|
||||||
|
export let mde = null
|
||||||
|
export let id = null
|
||||||
|
export let fullScreenOffset = null
|
||||||
|
export let disabled = false
|
||||||
|
|
||||||
|
let element
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
height = height || "200px"
|
||||||
|
mde = new EasyMDE({
|
||||||
|
element,
|
||||||
|
spellChecker: false,
|
||||||
|
status: false,
|
||||||
|
unorderedListStyle: "-",
|
||||||
|
maxHeight: scroll ? height : undefined,
|
||||||
|
minHeight: scroll ? undefined : height,
|
||||||
|
...easyMDEOptions,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Revert the editor when we unmount
|
||||||
|
return () => {
|
||||||
|
mde.toTextArea()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$: styleString = getStyleString(fullScreenOffset)
|
||||||
|
|
||||||
|
const getStyleString = offset => {
|
||||||
|
let string = ""
|
||||||
|
string += `--fullscreen-offset-x:${offset?.x || "0px"};`
|
||||||
|
string += `--fullscreen-offset-y:${offset?.y || "0px"};`
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class:disabled style={styleString}>
|
||||||
|
<textarea disabled {id} bind:this={element} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Disabled styles */
|
||||||
|
.disabled :global(textarea) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.disabled :global(.CodeMirror-cursor) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.disabled :global(.EasyMDEContainer) {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.disabled :global(.editor-toolbar button i) {
|
||||||
|
color: var(--spectrum-global-color-gray-400);
|
||||||
|
}
|
||||||
|
.disabled :global(.CodeMirror) {
|
||||||
|
color: var(--spectrum-global-color-gray-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toolbar container */
|
||||||
|
:global(.EasyMDEContainer .editor-toolbar) {
|
||||||
|
background: var(--spectrum-global-color-gray-50);
|
||||||
|
border-top: 1px solid var(--spectrum-alias-border-color);
|
||||||
|
border-left: 1px solid var(--spectrum-alias-border-color);
|
||||||
|
border-right: 1px solid var(--spectrum-alias-border-color);
|
||||||
|
}
|
||||||
|
/* Main code mirror instance and default color */
|
||||||
|
:global(.EasyMDEContainer .CodeMirror) {
|
||||||
|
border: 1px solid var(--spectrum-alias-border-color);
|
||||||
|
background: var(--spectrum-global-color-gray-50);
|
||||||
|
color: var(--spectrum-alias-text-color);
|
||||||
|
}
|
||||||
|
/* Toolbar button active state */
|
||||||
|
:global(.EasyMDEContainer .editor-toolbar button.active) {
|
||||||
|
background: var(--spectrum-global-color-gray-200);
|
||||||
|
border-color: var(--spectrum-global-color-gray-400);
|
||||||
|
}
|
||||||
|
/* Toolbar button hover state */
|
||||||
|
:global(.EasyMDEContainer .editor-toolbar button:hover) {
|
||||||
|
background: var(--spectrum-global-color-gray-200);
|
||||||
|
border-color: var(--spectrum-global-color-gray-400);
|
||||||
|
}
|
||||||
|
/* Toolbar button color */
|
||||||
|
:global(.EasyMDEContainer .editor-toolbar button i) {
|
||||||
|
color: var(--spectrum-global-color-gray-800);
|
||||||
|
}
|
||||||
|
/* Separator between toolbar buttons*/
|
||||||
|
:global(.EasyMDEContainer .editor-toolbar i.separator) {
|
||||||
|
border-color: var(--spectrum-global-color-gray-300);
|
||||||
|
}
|
||||||
|
/* Cursor */
|
||||||
|
:global(.EasyMDEContainer .CodeMirror-cursor) {
|
||||||
|
border-color: var(--spectrum-alias-text-color);
|
||||||
|
}
|
||||||
|
/* Text selections */
|
||||||
|
:global(.EasyMDEContainer .CodeMirror-selectedtext) {
|
||||||
|
background: var(--spectrum-global-color-gray-400) !important;
|
||||||
|
}
|
||||||
|
/* Background of lines containing selected text */
|
||||||
|
:global(.EasyMDEContainer .CodeMirror-selected) {
|
||||||
|
background: var(--spectrum-global-color-gray-400) !important;
|
||||||
|
}
|
||||||
|
/* Color of text for images and links */
|
||||||
|
:global(.EasyMDEContainer .cm-s-easymde .cm-link) {
|
||||||
|
color: var(--spectrum-global-color-gray-600);
|
||||||
|
}
|
||||||
|
/* Color of URL for images and links */
|
||||||
|
:global(.EasyMDEContainer .cm-s-easymde .cm-url) {
|
||||||
|
color: var(--spectrum-global-color-gray-500);
|
||||||
|
}
|
||||||
|
/* Full preview window */
|
||||||
|
:global(.EasyMDEContainer .editor-preview) {
|
||||||
|
background: var(--spectrum-global-color-gray-50);
|
||||||
|
}
|
||||||
|
/* Side by side preview window */
|
||||||
|
:global(.EasyMDEContainer .editor-preview) {
|
||||||
|
border: 1px solid var(--spectrum-alias-border-color);
|
||||||
|
}
|
||||||
|
/* Code blocks in editor */
|
||||||
|
:global(.EasyMDEContainer .cm-s-easymde .cm-comment) {
|
||||||
|
background: var(--spectrum-global-color-gray-100);
|
||||||
|
}
|
||||||
|
/* Code blocks in preview */
|
||||||
|
:global(.EasyMDEContainer pre) {
|
||||||
|
background: var(--spectrum-global-color-gray-100);
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
:global(.EasyMDEContainer code) {
|
||||||
|
color: #e83e8c;
|
||||||
|
}
|
||||||
|
:global(.EasyMDEContainer pre code) {
|
||||||
|
color: var(--spectrum-alias-text-color);
|
||||||
|
}
|
||||||
|
/* Block quotes */
|
||||||
|
:global(.EasyMDEContainer blockquote) {
|
||||||
|
border-left: 4px solid var(--spectrum-global-color-gray-400);
|
||||||
|
color: var(--spectrum-global-color-gray-700);
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
/* HR's */
|
||||||
|
:global(.EasyMDEContainer hr) {
|
||||||
|
background-color: var(--spectrum-global-color-gray-300);
|
||||||
|
border: none;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
/* Tables */
|
||||||
|
:global(.EasyMDEContainer td, .EasyMDEContainer th) {
|
||||||
|
border-color: var(--spectrum-alias-border-color) !important;
|
||||||
|
}
|
||||||
|
/* Links */
|
||||||
|
:global(.EasyMDEContainer a) {
|
||||||
|
color: var(--primaryColor);
|
||||||
|
}
|
||||||
|
:global(.EasyMDEContainer a:hover) {
|
||||||
|
color: var(--primaryColorHover);
|
||||||
|
}
|
||||||
|
/* Allow full screen offset */
|
||||||
|
:global(.EasyMDEContainer .editor-toolbar.fullscreen) {
|
||||||
|
left: var(--fullscreen-offset-x);
|
||||||
|
top: var(--fullscreen-offset-y);
|
||||||
|
}
|
||||||
|
:global(.EasyMDEContainer .CodeMirror-fullscreen) {
|
||||||
|
left: var(--fullscreen-offset-x);
|
||||||
|
top: calc(50px + var(--fullscreen-offset-y));
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.EasyMDEContainer .CodeMirror-fullscreen.CodeMirror-sided) {
|
||||||
|
width: calc((100% - var(--fullscreen-offset-x)) / 2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.EasyMDEContainer .editor-preview-side) {
|
||||||
|
left: calc(50% + (var(--fullscreen-offset-x) / 2));
|
||||||
|
top: calc(50px + var(--fullscreen-offset-y));
|
||||||
|
width: calc((100% - var(--fullscreen-offset-x)) / 2) !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,7 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
export let type = "info"
|
export let type = "info"
|
||||||
export let icon = "Info"
|
export let icon = "Info"
|
||||||
export let message = ""
|
export let message = ""
|
||||||
|
export let dismissable = false
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="spectrum-Toast spectrum-Toast--{type}">
|
<div class="spectrum-Toast spectrum-Toast--{type}">
|
||||||
|
@ -17,4 +22,28 @@
|
||||||
<div class="spectrum-Toast-body">
|
<div class="spectrum-Toast-body">
|
||||||
<div class="spectrum-Toast-content">{message || ""}</div>
|
<div class="spectrum-Toast-content">{message || ""}</div>
|
||||||
</div>
|
</div>
|
||||||
|
{#if dismissable}
|
||||||
|
<div class="spectrum-Toast-buttons">
|
||||||
|
<button
|
||||||
|
class="spectrum-ClearButton spectrum-ClearButton--overBackground spectrum-ClearButton--sizeM"
|
||||||
|
on:click={() => dispatch("dismiss")}
|
||||||
|
>
|
||||||
|
<div class="spectrum-ClearButton-fill">
|
||||||
|
<svg
|
||||||
|
class="spectrum-ClearButton-icon spectrum-Icon spectrum-UIIcon-Cross100"
|
||||||
|
focusable="false"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<use xlink:href="#spectrum-css-icon-Cross100" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.spectrum-Toast {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import "@spectrum-css/toast/dist/index-vars.css"
|
import "@spectrum-css/toast/dist/index-vars.css"
|
||||||
import Portal from "svelte-portal"
|
import Portal from "svelte-portal"
|
||||||
import { flip } from "svelte/animate"
|
|
||||||
import { notifications } from "../Stores/notifications"
|
import { notifications } from "../Stores/notifications"
|
||||||
import Notification from "./Notification.svelte"
|
import Notification from "./Notification.svelte"
|
||||||
import { fly } from "svelte/transition"
|
import { fly } from "svelte/transition"
|
||||||
|
@ -9,9 +8,15 @@
|
||||||
|
|
||||||
<Portal target=".modal-container">
|
<Portal target=".modal-container">
|
||||||
<div class="notifications">
|
<div class="notifications">
|
||||||
{#each $notifications as { type, icon, message, id } (id)}
|
{#each $notifications as { type, icon, message, id, dismissable } (id)}
|
||||||
<div animate:flip transition:fly={{ y: -30 }}>
|
<div transition:fly={{ y: -30 }}>
|
||||||
<Notification {type} {icon} {message} />
|
<Notification
|
||||||
|
{type}
|
||||||
|
{icon}
|
||||||
|
{message}
|
||||||
|
{dismissable}
|
||||||
|
on:dismiss={() => notifications.dismiss(id)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,20 +20,29 @@ export const createNotificationStore = () => {
|
||||||
setTimeout(() => (block = false), timeout)
|
setTimeout(() => (block = false), timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
const send = (message, type = "default", icon = "") => {
|
const send = (message, type = "default", icon = "", autoDismiss = true) => {
|
||||||
if (block) {
|
if (block) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _id = id()
|
let _id = id()
|
||||||
_notifications.update(state => {
|
_notifications.update(state => {
|
||||||
return [...state, { id: _id, type, message, icon }]
|
return [
|
||||||
|
...state,
|
||||||
|
{ id: _id, type, message, icon, dismissable: !autoDismiss },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
if (autoDismiss) {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
dismissNotification(_id)
|
||||||
|
}, NOTIFICATION_TIMEOUT)
|
||||||
|
timeoutIds.add(timeoutId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dismissNotification = id => {
|
||||||
|
_notifications.update(state => {
|
||||||
|
return state.filter(n => n.id !== id)
|
||||||
})
|
})
|
||||||
const timeoutId = setTimeout(() => {
|
|
||||||
_notifications.update(state => {
|
|
||||||
return state.filter(({ id }) => id !== _id)
|
|
||||||
})
|
|
||||||
}, NOTIFICATION_TIMEOUT)
|
|
||||||
timeoutIds.add(timeoutId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { subscribe } = _notifications
|
const { subscribe } = _notifications
|
||||||
|
@ -42,10 +51,11 @@ export const createNotificationStore = () => {
|
||||||
subscribe,
|
subscribe,
|
||||||
send,
|
send,
|
||||||
info: msg => send(msg, "info", "Info"),
|
info: msg => send(msg, "info", "Info"),
|
||||||
error: msg => send(msg, "error", "Alert"),
|
error: msg => send(msg, "error", "Alert", false),
|
||||||
warning: msg => send(msg, "warning", "Alert"),
|
warning: msg => send(msg, "warning", "Alert"),
|
||||||
success: msg => send(msg, "success", "CheckmarkCircle"),
|
success: msg => send(msg, "success", "CheckmarkCircle"),
|
||||||
blockNotifications,
|
blockNotifications,
|
||||||
|
dismiss: dismissNotification,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,18 @@
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
|
|
||||||
|
// adding the 0- will turn a string like 00:00:00 into a valid ISO
|
||||||
|
// date, but will make actual ISO dates invalid
|
||||||
|
$: time = new Date(`0-${value}`)
|
||||||
|
$: isTime = !isNaN(time)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>{dayjs(value).format("MMMM D YYYY, HH:mm")}</div>
|
<div>
|
||||||
|
{dayjs(isTime ? time : value).format(
|
||||||
|
isTime ? "HH:mm:ss" : "MMMM D YYYY, HH:mm"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
class:icon-small={size === "M" || size === "S"}
|
class:icon-small={size === "M" || size === "S"}
|
||||||
on:mouseover={() => (showTooltip = true)}
|
on:mouseover={() => (showTooltip = true)}
|
||||||
on:mouseleave={() => (showTooltip = false)}
|
on:mouseleave={() => (showTooltip = false)}
|
||||||
|
on:focus
|
||||||
>
|
>
|
||||||
<Icon name="InfoOutline" size="S" disabled={true} />
|
<Icon name="InfoOutline" size="S" disabled={true} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,7 +48,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
top: 15px;
|
top: 15px;
|
||||||
z-index: 1;
|
z-index: 100;
|
||||||
width: 160px;
|
width: 160px;
|
||||||
}
|
}
|
||||||
.icon {
|
.icon {
|
||||||
|
|
|
@ -60,6 +60,9 @@ export { default as StatusLight } from "./StatusLight/StatusLight.svelte"
|
||||||
export { default as ColorPicker } from "./ColorPicker/ColorPicker.svelte"
|
export { default as ColorPicker } from "./ColorPicker/ColorPicker.svelte"
|
||||||
export { default as InlineAlert } from "./InlineAlert/InlineAlert.svelte"
|
export { default as InlineAlert } from "./InlineAlert/InlineAlert.svelte"
|
||||||
export { default as Banner } from "./Banner/Banner.svelte"
|
export { default as Banner } from "./Banner/Banner.svelte"
|
||||||
|
export { default as MarkdownEditor } from "./Markdown/MarkdownEditor.svelte"
|
||||||
|
export { default as MarkdownViewer } from "./Markdown/MarkdownViewer.svelte"
|
||||||
|
export { default as RichTextField } from "./Form/RichTextField.svelte"
|
||||||
|
|
||||||
// Renderers
|
// Renderers
|
||||||
export { default as BoldRenderer } from "./Table/BoldRenderer.svelte"
|
export { default as BoldRenderer } from "./Table/BoldRenderer.svelte"
|
||||||
|
|
|
@ -271,6 +271,13 @@
|
||||||
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.2.tgz#ea9062c3c98dfc6ba59e5df14a03025ad8969999"
|
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.2.tgz#ea9062c3c98dfc6ba59e5df14a03025ad8969999"
|
||||||
integrity sha512-vzS9KqYXot4J3AEER/u618MXWAS+IoMvYMNrOoscKiLLKYQWenaueakUWulFonToPd/9vIpqtdbwxznqrK5qDw==
|
integrity sha512-vzS9KqYXot4J3AEER/u618MXWAS+IoMvYMNrOoscKiLLKYQWenaueakUWulFonToPd/9vIpqtdbwxznqrK5qDw==
|
||||||
|
|
||||||
|
"@types/codemirror@^5.60.4":
|
||||||
|
version "5.60.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-5.60.5.tgz#5b989a3b4bbe657458cf372c92b6bfda6061a2b7"
|
||||||
|
integrity sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg==
|
||||||
|
dependencies:
|
||||||
|
"@types/tern" "*"
|
||||||
|
|
||||||
"@types/estree@*":
|
"@types/estree@*":
|
||||||
version "0.0.47"
|
version "0.0.47"
|
||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4"
|
||||||
|
@ -281,6 +288,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||||
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
||||||
|
|
||||||
|
"@types/marked@^4.0.1":
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/marked/-/marked-4.0.2.tgz#cb2dbf10da2f41cf20bd91fb5f89b67540c282f7"
|
||||||
|
integrity sha512-auNrZ/c0w6wsM9DccwVxWHssrMDezHUAXNesdp2RQrCVCyrQbOiSq7yqdJKrUQQpw9VTm7CGYJH2A/YG7jjrjQ==
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "14.14.41"
|
version "14.14.41"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615"
|
||||||
|
@ -303,6 +315,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/tern@*":
|
||||||
|
version "0.23.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.4.tgz#03926eb13dbeaf3ae0d390caf706b2643a0127fb"
|
||||||
|
integrity sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==
|
||||||
|
dependencies:
|
||||||
|
"@types/estree" "*"
|
||||||
|
|
||||||
accepts@~1.3.7:
|
accepts@~1.3.7:
|
||||||
version "1.3.7"
|
version "1.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
||||||
|
@ -525,6 +544,18 @@ coa@^2.0.2:
|
||||||
chalk "^2.4.1"
|
chalk "^2.4.1"
|
||||||
q "^1.1.2"
|
q "^1.1.2"
|
||||||
|
|
||||||
|
codemirror-spell-checker@1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz#1c660f9089483ccb5113b9ba9ca19c3f4993371e"
|
||||||
|
integrity sha1-HGYPkIlIPMtRE7m6nKGcP0mTNx4=
|
||||||
|
dependencies:
|
||||||
|
typo-js "*"
|
||||||
|
|
||||||
|
codemirror@^5.63.1:
|
||||||
|
version "5.65.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.1.tgz#5988a812c974c467f964bcc1a00c944e373de502"
|
||||||
|
integrity sha512-s6aac+DD+4O2u1aBmdxhB7yz2XU7tG3snOyQ05Kxifahz7hoxnfxIRHxiCSEv3TUC38dIVH8G+lZH9UWSfGQxA==
|
||||||
|
|
||||||
color-convert@^1.9.0, color-convert@^1.9.1:
|
color-convert@^1.9.0, color-convert@^1.9.1:
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||||
|
@ -861,6 +892,17 @@ dot-prop@^5.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-obj "^2.0.0"
|
is-obj "^2.0.0"
|
||||||
|
|
||||||
|
easymde@^2.16.1:
|
||||||
|
version "2.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/easymde/-/easymde-2.16.1.tgz#f4c2380312615cb33826f1a1fecfaa4022ff551a"
|
||||||
|
integrity sha512-FihYgjRsKfhGNk89SHSqxKLC4aJ1kfybPWW6iAmtb5GnXu+tnFPSzSaGBmk1RRlCuhFSjhF0SnIMGVPjEzkr6g==
|
||||||
|
dependencies:
|
||||||
|
"@types/codemirror" "^5.60.4"
|
||||||
|
"@types/marked" "^4.0.1"
|
||||||
|
codemirror "^5.63.1"
|
||||||
|
codemirror-spell-checker "1.1.2"
|
||||||
|
marked "^4.0.10"
|
||||||
|
|
||||||
ee-first@1.1.1:
|
ee-first@1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||||
|
@ -1472,6 +1514,11 @@ magic-string@^0.25.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
sourcemap-codec "^1.4.4"
|
sourcemap-codec "^1.4.4"
|
||||||
|
|
||||||
|
marked@^4.0.10:
|
||||||
|
version "4.0.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.12.tgz#2262a4e6fd1afd2f13557726238b69a48b982f7d"
|
||||||
|
integrity sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==
|
||||||
|
|
||||||
mdn-data@2.0.14:
|
mdn-data@2.0.14:
|
||||||
version "2.0.14"
|
version "2.0.14"
|
||||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||||
|
@ -2490,6 +2537,11 @@ type-is@~1.6.17, type-is@~1.6.18:
|
||||||
media-typer "0.3.0"
|
media-typer "0.3.0"
|
||||||
mime-types "~2.1.24"
|
mime-types "~2.1.24"
|
||||||
|
|
||||||
|
typo-js@*:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.2.1.tgz#334a0d8c3f6c56f2f1e15fdf6c31677793cbbe9b"
|
||||||
|
integrity sha512-bTGLjbD3WqZDR3CgEFkyi9Q/SS2oM29ipXrWfDb4M74ea69QwKAECVceYpaBu0GfdnASMg9Qfl67ttB23nePHg==
|
||||||
|
|
||||||
unbox-primitive@^1.0.0:
|
unbox-primitive@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
|
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -64,10 +64,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.49-alpha.14",
|
"@budibase/bbui": "^1.0.50-alpha.5",
|
||||||
"@budibase/client": "^1.0.49-alpha.14",
|
"@budibase/client": "^1.0.50-alpha.5",
|
||||||
"@budibase/frontend-core": "^1.0.49-alpha.14",
|
"@budibase/frontend-core": "^1.0.50-alpha.5",
|
||||||
"@budibase/string-templates": "^1.0.49-alpha.14",
|
"@budibase/string-templates": "^1.0.50-alpha.5",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
|
|
|
@ -169,6 +169,11 @@ export function makeDatasourceFormComponents(datasource) {
|
||||||
optionsSource: "schema",
|
optionsSource: "schema",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (fieldType === "longform") {
|
||||||
|
component.customProps({
|
||||||
|
format: "auto",
|
||||||
|
})
|
||||||
|
}
|
||||||
if (fieldType === "array") {
|
if (fieldType === "array") {
|
||||||
component.customProps({
|
component.customProps({
|
||||||
placeholder: "Choose an option",
|
placeholder: "Choose an option",
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
Select,
|
Select,
|
||||||
DatePicker,
|
DatePicker,
|
||||||
Toggle,
|
Toggle,
|
||||||
TextArea,
|
|
||||||
Multiselect,
|
Multiselect,
|
||||||
Label,
|
Label,
|
||||||
|
RichTextField,
|
||||||
|
TextArea,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import Dropzone from "components/common/Dropzone.svelte"
|
import Dropzone from "components/common/Dropzone.svelte"
|
||||||
import { capitalise } from "helpers"
|
import { capitalise } from "helpers"
|
||||||
|
@ -43,7 +44,11 @@
|
||||||
{:else if type === "link"}
|
{:else if type === "link"}
|
||||||
<LinkedRowSelector bind:linkedRows={value} schema={meta} />
|
<LinkedRowSelector bind:linkedRows={value} schema={meta} />
|
||||||
{:else if type === "longform"}
|
{:else if type === "longform"}
|
||||||
<TextArea {label} bind:value />
|
{#if meta.useRichText}
|
||||||
|
<RichTextField {label} height="150px" bind:value />
|
||||||
|
{:else}
|
||||||
|
<TextArea {label} height="150px" bind:value />
|
||||||
|
{/if}
|
||||||
{:else if type === "json"}
|
{:else if type === "json"}
|
||||||
<Label>{label}</Label>
|
<Label>{label}</Label>
|
||||||
<Editor
|
<Editor
|
||||||
|
|
|
@ -371,7 +371,7 @@
|
||||||
|
|
||||||
{#if canBeSearched && !external}
|
{#if canBeSearched && !external}
|
||||||
<div>
|
<div>
|
||||||
<Label grey small>Search Indexes</Label>
|
<Label>Search Indexes</Label>
|
||||||
<Toggle
|
<Toggle
|
||||||
value={indexes[0] === field.name}
|
value={indexes[0] === field.name}
|
||||||
disabled={indexes[1] === field.name}
|
disabled={indexes[1] === field.name}
|
||||||
|
@ -398,6 +398,19 @@
|
||||||
label="Options (one per line)"
|
label="Options (one per line)"
|
||||||
bind:values={field.constraints.inclusion}
|
bind:values={field.constraints.inclusion}
|
||||||
/>
|
/>
|
||||||
|
{:else if field.type === "longform"}
|
||||||
|
<div>
|
||||||
|
<Label
|
||||||
|
size="M"
|
||||||
|
tooltip="Rich text includes support for images, links, tables, lists and more"
|
||||||
|
>
|
||||||
|
Formatting
|
||||||
|
</Label>
|
||||||
|
<Toggle
|
||||||
|
bind:value={field.useRichText}
|
||||||
|
text="Enable rich text support (markdown)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{:else if field.type === "array"}
|
{:else if field.type === "array"}
|
||||||
<ValuesList
|
<ValuesList
|
||||||
label="Options (one per line)"
|
label="Options (one per line)"
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
dispatch("change")
|
dispatch("change")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
dispatch("save", value)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="parent">
|
<div class="parent">
|
||||||
|
@ -39,7 +43,10 @@
|
||||||
name="SaveFloppy"
|
name="SaveFloppy"
|
||||||
hoverable
|
hoverable
|
||||||
size="S"
|
size="S"
|
||||||
on:click={() => setEditing(false)}
|
on:click={() => {
|
||||||
|
setEditing(false)
|
||||||
|
save()
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -81,7 +81,8 @@
|
||||||
"backgroundimage",
|
"backgroundimage",
|
||||||
"link",
|
"link",
|
||||||
"icon",
|
"icon",
|
||||||
"embed"
|
"embed",
|
||||||
|
"markdownviewer"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if query?.parameters?.length > 0}
|
{#if query?.parameters?.length > 0}
|
||||||
<div>
|
<div class="params">
|
||||||
<BindingBuilder
|
<BindingBuilder
|
||||||
bind:customParams={parameters.queryParams}
|
bind:customParams={parameters.queryParams}
|
||||||
queryBindings={query.parameters}
|
queryBindings={query.parameters}
|
||||||
|
@ -70,3 +70,11 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.params {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-l);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -59,6 +59,9 @@
|
||||||
$: schemaReadOnly = !responseSuccess
|
$: schemaReadOnly = !responseSuccess
|
||||||
$: variablesReadOnly = !responseSuccess
|
$: variablesReadOnly = !responseSuccess
|
||||||
$: showVariablesTab = shouldShowVariables(dynamicVariables, variablesReadOnly)
|
$: showVariablesTab = shouldShowVariables(dynamicVariables, variablesReadOnly)
|
||||||
|
$: hasSchema =
|
||||||
|
Object.keys(schema || {}).length !== 0 ||
|
||||||
|
Object.keys(query?.schema || {}).length !== 0
|
||||||
|
|
||||||
function getSelectedQuery() {
|
function getSelectedQuery() {
|
||||||
return cloneDeep(
|
return cloneDeep(
|
||||||
|
@ -307,6 +310,7 @@
|
||||||
bind:value={query.name}
|
bind:value={query.name}
|
||||||
defaultValue="Untitled"
|
defaultValue="Untitled"
|
||||||
on:change={() => (query.flags.urlName = false)}
|
on:change={() => (query.flags.urlName = false)}
|
||||||
|
on:save={saveQuery}
|
||||||
/>
|
/>
|
||||||
<div class="access">
|
<div class="access">
|
||||||
<Label>Access level</Label>
|
<Label>Access level</Label>
|
||||||
|
@ -326,7 +330,15 @@
|
||||||
<div class="url">
|
<div class="url">
|
||||||
<Input bind:value={url} placeholder="http://www.api.com/endpoint" />
|
<Input bind:value={url} placeholder="http://www.api.com/endpoint" />
|
||||||
</div>
|
</div>
|
||||||
<Button cta disabled={!url} on:click={runQuery}>Send</Button>
|
<Button primary disabled={!url} on:click={runQuery}>Send</Button>
|
||||||
|
<Button
|
||||||
|
disabled={!query.name}
|
||||||
|
cta
|
||||||
|
on:click={saveQuery}
|
||||||
|
tooltip={!hasSchema
|
||||||
|
? "Saving a query before sending will mean no schema is generated"
|
||||||
|
: null}>Save</Button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<Tabs selected="Bindings" quiet noPadding noHorizPadding onTop>
|
<Tabs selected="Bindings" quiet noPadding noHorizPadding onTop>
|
||||||
<Tab title="Bindings">
|
<Tab title="Bindings">
|
||||||
|
@ -539,9 +551,6 @@
|
||||||
>{response?.info.size}</span
|
>{response?.info.size}</span
|
||||||
>
|
>
|
||||||
</Label>
|
</Label>
|
||||||
<Button disabled={!responseSuccess} cta on:click={saveQuery}
|
|
||||||
>Save query</Button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -2365,11 +2365,10 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"longformfield": {
|
"longformfield": {
|
||||||
"name": "Rich Text",
|
"name": "Long Form Field",
|
||||||
"icon": "TextParagraph",
|
"icon": "TextParagraph",
|
||||||
"styles": ["size"],
|
"styles": ["size"],
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"illegalChildren": ["section"],
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "field/longform",
|
"type": "field/longform",
|
||||||
|
@ -2392,6 +2391,27 @@
|
||||||
"label": "Default value",
|
"label": "Default value",
|
||||||
"key": "defaultValue"
|
"key": "defaultValue"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Formatting",
|
||||||
|
"key": "format",
|
||||||
|
"placeholder": null,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Auto",
|
||||||
|
"value": "auto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Plain text",
|
||||||
|
"value": "plain"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Rich text (markdown)",
|
||||||
|
"value": "rich"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"defaultValue": "auto"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
|
@ -2402,35 +2422,6 @@
|
||||||
"type": "validation/string",
|
"type": "validation/string",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "select",
|
|
||||||
"label": "Alignment",
|
|
||||||
"key": "align",
|
|
||||||
"defaultValue": "left",
|
|
||||||
"showInBar": true,
|
|
||||||
"barStyle": "buttons",
|
|
||||||
"options": [{
|
|
||||||
"label": "Left",
|
|
||||||
"value": "left",
|
|
||||||
"barIcon": "TextAlignLeft",
|
|
||||||
"barTitle": "Align left"
|
|
||||||
}, {
|
|
||||||
"label": "Center",
|
|
||||||
"value": "center",
|
|
||||||
"barIcon": "TextAlignCenter",
|
|
||||||
"barTitle": "Align center"
|
|
||||||
}, {
|
|
||||||
"label": "Right",
|
|
||||||
"value": "right",
|
|
||||||
"barIcon": "TextAlignRight",
|
|
||||||
"barTitle": "Align right"
|
|
||||||
}, {
|
|
||||||
"label": "Justify",
|
|
||||||
"value": "justify",
|
|
||||||
"barIcon": "TextAlignJustify",
|
|
||||||
"barTitle": "Justify text"
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2462,6 +2453,12 @@
|
||||||
"key": "enableTime",
|
"key": "enableTime",
|
||||||
"defaultValue": true
|
"defaultValue": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Time Only",
|
||||||
|
"key": "timeOnly",
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Default value",
|
"label": "Default value",
|
||||||
|
@ -3448,5 +3445,18 @@
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"markdownviewer": {
|
||||||
|
"name": "Markdown Viewer",
|
||||||
|
"icon": "TaskList",
|
||||||
|
"styles": ["size"],
|
||||||
|
"editable": true,
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Markdown",
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
|
@ -19,9 +19,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.49-alpha.14",
|
"@budibase/bbui": "^1.0.50-alpha.5",
|
||||||
"@budibase/frontend-core": "^1.0.49-alpha.14",
|
"@budibase/frontend-core": "^1.0.50-alpha.5",
|
||||||
"@budibase/string-templates": "^1.0.49-alpha.14",
|
"@budibase/string-templates": "^1.0.50-alpha.5",
|
||||||
"@spectrum-css/button": "^3.0.3",
|
"@spectrum-css/button": "^3.0.3",
|
||||||
"@spectrum-css/card": "^3.0.3",
|
"@spectrum-css/card": "^3.0.3",
|
||||||
"@spectrum-css/divider": "^1.0.3",
|
"@spectrum-css/divider": "^1.0.3",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext, setContext } from "svelte"
|
||||||
|
import { writable } from "svelte/store"
|
||||||
import { Heading, Icon } from "@budibase/bbui"
|
import { Heading, Icon } from "@budibase/bbui"
|
||||||
import { FieldTypes } from "../../constants"
|
import { FieldTypes } from "../../constants"
|
||||||
import active from "svelte-spa-router/active"
|
import active from "svelte-spa-router/active"
|
||||||
|
@ -29,6 +30,16 @@
|
||||||
Small: "s",
|
Small: "s",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set some layout context. This isn't used in bindings but can be used
|
||||||
|
// determine things about the current app layout.
|
||||||
|
$: mobile = $context.device.mobile
|
||||||
|
const store = writable({ headerHeight: 0 })
|
||||||
|
$: store.set({
|
||||||
|
screenXOffset: getScreenXOffset(navigation, mobile),
|
||||||
|
screenYOffset: getScreenYOffset(navigation, mobile),
|
||||||
|
})
|
||||||
|
setContext("layout", store)
|
||||||
|
|
||||||
// Permanently go into peek mode if we ever get the peek flag
|
// Permanently go into peek mode if we ever get the peek flag
|
||||||
let isPeeking = false
|
let isPeeking = false
|
||||||
$: {
|
$: {
|
||||||
|
@ -58,13 +69,27 @@
|
||||||
if ($builderStore.inBuilder) return
|
if ($builderStore.inBuilder) return
|
||||||
window.location.href = "/builder/apps"
|
window.location.href = "/builder/apps"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getScreenXOffset = (navigation, mobile) => {
|
||||||
|
if (navigation !== "Left") {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return mobile ? "0px" : "250px"
|
||||||
|
}
|
||||||
|
const getScreenYOffset = (navigation, mobile) => {
|
||||||
|
if (mobile) {
|
||||||
|
return !navigation || navigation === "None" ? 0 : "61px"
|
||||||
|
} else {
|
||||||
|
return navigation === "Top" ? "137px" : "0px"
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="layout layout--{typeClass}"
|
class="layout layout--{typeClass}"
|
||||||
use:styleable={$component.styles}
|
use:styleable={$component.styles}
|
||||||
class:desktop={!$context.device.mobile}
|
class:desktop={!mobile}
|
||||||
class:mobile={!!$context.device.mobile}
|
class:mobile={!!mobile}
|
||||||
>
|
>
|
||||||
{#if typeClass !== "none"}
|
{#if typeClass !== "none"}
|
||||||
<div
|
<div
|
||||||
|
|
19
packages/client/src/components/app/MarkdownViewer.svelte
Normal file
19
packages/client/src/components/app/MarkdownViewer.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script>
|
||||||
|
import { MarkdownViewer } from "@budibase/bbui"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
import Placeholder from "./Placeholder.svelte"
|
||||||
|
|
||||||
|
export let value
|
||||||
|
|
||||||
|
const component = getContext("component")
|
||||||
|
const { builderStore, styleable } = getContext("sdk")
|
||||||
|
const height = $component.styles?.normal?.height
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div use:styleable={$component.styles}>
|
||||||
|
{#if value}
|
||||||
|
<MarkdownViewer {value} {height} />
|
||||||
|
{:else if $builderStore.inBuilder}
|
||||||
|
<Placeholder />
|
||||||
|
{/if}
|
||||||
|
</div>
|
|
@ -7,6 +7,7 @@
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let enableTime = false
|
export let enableTime = false
|
||||||
|
export let timeOnly = false
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
appendTo={document.getElementById("flatpickr-root")}
|
appendTo={document.getElementById("flatpickr-root")}
|
||||||
{enableTime}
|
{enableTime}
|
||||||
|
{timeOnly}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { CoreTextArea } from "@budibase/bbui"
|
import { getContext, setContext } from "svelte"
|
||||||
|
import { writable } from "svelte/store"
|
||||||
|
import { CoreRichTextField, CoreTextArea } from "@budibase/bbui"
|
||||||
import Field from "./Field.svelte"
|
import Field from "./Field.svelte"
|
||||||
import { getContext } from "svelte"
|
|
||||||
|
|
||||||
export let field
|
export let field
|
||||||
export let label
|
export let label
|
||||||
|
@ -9,13 +10,40 @@
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue = ""
|
export let defaultValue = ""
|
||||||
export let align
|
export let format = "auto"
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
let fieldSchema
|
||||||
|
|
||||||
|
const context = getContext("context")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
$: height = $component.styles?.normal?.height || "124px"
|
const layout = getContext("layout")
|
||||||
|
const newContext = writable($component)
|
||||||
|
setContext("component", newContext)
|
||||||
|
|
||||||
|
// Determine whether we should use a rich or plain text component.
|
||||||
|
// We treat undefined as "auto".
|
||||||
|
$: useRichText =
|
||||||
|
format === "rich" || (format !== "plain" && fieldSchema?.useRichText)
|
||||||
|
|
||||||
|
// Extract the settings height so we can pass it on to the rich text field.
|
||||||
|
// We then wipe the height style so that the field will automatically size
|
||||||
|
// itself based on the height of the rich text field.
|
||||||
|
let height
|
||||||
|
$: {
|
||||||
|
height = $component.styles?.normal?.height || "150px"
|
||||||
|
newContext.set({
|
||||||
|
...$component,
|
||||||
|
styles: {
|
||||||
|
...$component.styles,
|
||||||
|
normal: {
|
||||||
|
...$component.styles.normal,
|
||||||
|
height: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
|
@ -27,29 +55,36 @@
|
||||||
type="longform"
|
type="longform"
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
|
bind:fieldSchema
|
||||||
>
|
>
|
||||||
{#if fieldState}
|
{#if fieldState}
|
||||||
<div style="--height: {height};">
|
{#if useRichText}
|
||||||
|
<CoreRichTextField
|
||||||
|
value={fieldState.value}
|
||||||
|
on:change={e => fieldApi.setValue(e.detail)}
|
||||||
|
disabled={fieldState.disabled}
|
||||||
|
error={fieldState.error}
|
||||||
|
id={fieldState.fieldId}
|
||||||
|
{placeholder}
|
||||||
|
{height}
|
||||||
|
fullScreenOffset={{
|
||||||
|
x: $layout.screenXOffset,
|
||||||
|
y: $layout.screenYOffset,
|
||||||
|
}}
|
||||||
|
easyMDEOptions={{
|
||||||
|
hideIcons: $context.device.mobile ? ["side-by-side", "guide"] : [],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
<CoreTextArea
|
<CoreTextArea
|
||||||
value={fieldState.value}
|
value={fieldState.value}
|
||||||
on:change={e => fieldApi.setValue(e.detail)}
|
on:change={e => fieldApi.setValue(e.detail)}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
{align}
|
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
minHeight={height}
|
||||||
/>
|
/>
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<style>
|
|
||||||
:global(.spectrum-Form-itemField .spectrum-Textfield--multiline) {
|
|
||||||
min-height: calc(var(--height) - 24px);
|
|
||||||
}
|
|
||||||
:global(.spectrum-Form--labelsAbove
|
|
||||||
.spectrum-Form-itemField
|
|
||||||
.spectrum-Textfield--multiline) {
|
|
||||||
min-height: calc(var(--height) - 24px);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ export { default as daterangepicker } from "./DateRangePicker.svelte"
|
||||||
export { default as cardstat } from "./CardStat.svelte"
|
export { default as cardstat } from "./CardStat.svelte"
|
||||||
export { default as spectrumcard } from "./SpectrumCard.svelte"
|
export { default as spectrumcard } from "./SpectrumCard.svelte"
|
||||||
export { default as tag } from "./Tag.svelte"
|
export { default as tag } from "./Tag.svelte"
|
||||||
|
export { default as markdownviewer } from "./MarkdownViewer.svelte"
|
||||||
export * from "./charts"
|
export * from "./charts"
|
||||||
export * from "./forms"
|
export * from "./forms"
|
||||||
export * from "./table"
|
export * from "./table"
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
type={$notificationStore.type}
|
type={$notificationStore.type}
|
||||||
message={$notificationStore.message}
|
message={$notificationStore.message}
|
||||||
icon={$notificationStore.icon}
|
icon={$notificationStore.icon}
|
||||||
|
dismissable={$notificationStore.dismissable}
|
||||||
|
on:dismiss={notificationStore.actions.dismiss}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/key}
|
{/key}
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const proxyNotification = event => {
|
const proxyNotification = event => {
|
||||||
const { message, type, icon } = event.detail
|
const { message, type, icon, autoDismiss } = event.detail
|
||||||
notificationStore.actions.send(message, type, icon)
|
notificationStore.actions.send(message, type, icon, autoDismiss)
|
||||||
}
|
}
|
||||||
|
|
||||||
const proxyStateUpdate = event => {
|
const proxyStateUpdate = event => {
|
||||||
|
|
|
@ -19,7 +19,7 @@ const createNotificationStore = () => {
|
||||||
setTimeout(() => (block = false), timeout)
|
setTimeout(() => (block = false), timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
const send = (message, type = "info", icon) => {
|
const send = (message, type = "info", icon, autoDismiss = true) => {
|
||||||
if (block) {
|
if (block) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ const createNotificationStore = () => {
|
||||||
message,
|
message,
|
||||||
type,
|
type,
|
||||||
icon,
|
icon,
|
||||||
|
autoDismiss,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -42,12 +43,20 @@ const createNotificationStore = () => {
|
||||||
type,
|
type,
|
||||||
message,
|
message,
|
||||||
icon,
|
icon,
|
||||||
|
dismissable: !autoDismiss,
|
||||||
delay: get(store) != null,
|
delay: get(store) != null,
|
||||||
})
|
})
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
timeout = setTimeout(() => {
|
if (autoDismiss) {
|
||||||
store.set(null)
|
timeout = setTimeout(() => {
|
||||||
}, NOTIFICATION_TIMEOUT)
|
store.set(null)
|
||||||
|
}, NOTIFICATION_TIMEOUT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dismiss = () => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
store.set(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -57,8 +66,9 @@ const createNotificationStore = () => {
|
||||||
info: msg => send(msg, "info", "Info"),
|
info: msg => send(msg, "info", "Info"),
|
||||||
success: msg => send(msg, "success", "CheckmarkCircle"),
|
success: msg => send(msg, "success", "CheckmarkCircle"),
|
||||||
warning: msg => send(msg, "warning", "Alert"),
|
warning: msg => send(msg, "warning", "Alert"),
|
||||||
error: msg => send(msg, "error", "Alert"),
|
error: msg => send(msg, "error", "Alert", false),
|
||||||
blockNotifications,
|
blockNotifications,
|
||||||
|
dismiss,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/frontend-core",
|
"name": "@budibase/frontend-core",
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"description": "Budibase frontend core libraries used in builder and client",
|
"description": "Budibase frontend core libraries used in builder and client",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.49-alpha.14",
|
"@budibase/bbui": "^1.0.50-alpha.4",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"svelte": "^3.46.2"
|
"svelte": "^3.46.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -70,9 +70,9 @@
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "^10.0.3",
|
"@apidevtools/swagger-parser": "^10.0.3",
|
||||||
"@budibase/backend-core": "^1.0.49-alpha.14",
|
"@budibase/backend-core": "^1.0.50-alpha.5",
|
||||||
"@budibase/client": "^1.0.49-alpha.14",
|
"@budibase/client": "^1.0.50-alpha.5",
|
||||||
"@budibase/string-templates": "^1.0.49-alpha.14",
|
"@budibase/string-templates": "^1.0.50-alpha.5",
|
||||||
"@bull-board/api": "^3.7.0",
|
"@bull-board/api": "^3.7.0",
|
||||||
"@bull-board/koa": "^3.7.0",
|
"@bull-board/koa": "^3.7.0",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
|
|
|
@ -36,13 +36,9 @@ CREATE TABLE people
|
||||||
INSERT products
|
INSERT products
|
||||||
(name, description)
|
(name, description)
|
||||||
VALUES
|
VALUES
|
||||||
('Bananas', 'Fruit thing');
|
('Bananas', 'Fruit thing'),
|
||||||
|
|
||||||
INSERT products
|
|
||||||
(name, description)
|
|
||||||
VALUES
|
|
||||||
('Meat', 'Animal thing');
|
('Meat', 'Animal thing');
|
||||||
|
|
||||||
INSERT tasks
|
INSERT tasks
|
||||||
(taskname, productid)
|
(taskname, productid)
|
||||||
VALUES
|
VALUES
|
||||||
|
|
|
@ -19,6 +19,12 @@ CREATE TABLE Tasks (
|
||||||
FOREIGN KEY(PersonID)
|
FOREIGN KEY(PersonID)
|
||||||
REFERENCES Persons(PersonID)
|
REFERENCES Persons(PersonID)
|
||||||
);
|
);
|
||||||
|
CREATE TABLE Products (
|
||||||
|
id serial primary key,
|
||||||
|
name text,
|
||||||
|
updated time
|
||||||
|
);
|
||||||
INSERT INTO Persons (FirstName, LastName, Age, Address, City, CreatedAt) VALUES ('Mike', 'Hughes', 28.2, '123 Fake Street', 'Belfast', '2021-01-19 03:14:07');
|
INSERT INTO Persons (FirstName, LastName, Age, Address, City, CreatedAt) VALUES ('Mike', 'Hughes', 28.2, '123 Fake Street', 'Belfast', '2021-01-19 03:14:07');
|
||||||
INSERT INTO Tasks (PersonID, TaskName) VALUES (1, 'assembling');
|
INSERT INTO Tasks (PersonID, TaskName) VALUES (1, 'assembling');
|
||||||
INSERT INTO Tasks (PersonID, TaskName) VALUES (1, 'processing');
|
INSERT INTO Tasks (PersonID, TaskName) VALUES (1, 'processing');
|
||||||
|
INSERT INTO Products (name, updated) VALUES ('Meat', '11:00:22'), ('Fruit', '10:00:00');
|
||||||
|
|
|
@ -5,8 +5,11 @@ exports.csv = function (headers, rows) {
|
||||||
csv = `${csv}\n${headers
|
csv = `${csv}\n${headers
|
||||||
.map(header => {
|
.map(header => {
|
||||||
let val = row[header]
|
let val = row[header]
|
||||||
val = typeof val === "object" ? JSON.stringify(val) : val
|
val =
|
||||||
return `"${val}"`.trim()
|
typeof val === "object"
|
||||||
|
? `"${JSON.stringify(val).replace(/"/g, "'")}"`
|
||||||
|
: `"${val}"`
|
||||||
|
return val.trim()
|
||||||
})
|
})
|
||||||
.join(",")}`
|
.join(",")}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,7 +230,6 @@ describe("/queries", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("variables", () => {
|
describe("variables", () => {
|
||||||
|
|
||||||
async function preview(datasource, fields) {
|
async function preview(datasource, fields) {
|
||||||
return config.previewQuery(request, config, datasource, fields)
|
return config.previewQuery(request, config, datasource, fields)
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,10 +161,16 @@ class QueryRunner {
|
||||||
const responses = await Promise.all(dynamics)
|
const responses = await Promise.all(dynamics)
|
||||||
for (let i = 0; i < foundVars.length; i++) {
|
for (let i = 0; i < foundVars.length; i++) {
|
||||||
const variable = foundVars[i]
|
const variable = foundVars[i]
|
||||||
parameters[variable.name] = processStringSync(variable.value, {
|
parameters[variable.name] = processStringSync(
|
||||||
data: responses[i].rows,
|
variable.value,
|
||||||
info: responses[i].extra,
|
{
|
||||||
})
|
data: responses[i].rows,
|
||||||
|
info: responses[i].extra,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
escapeNewlines: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
// make sure its known that this uses dynamic variables in case it fails
|
// make sure its known that this uses dynamic variables in case it fails
|
||||||
this.hasDynamicVariables = true
|
this.hasDynamicVariables = true
|
||||||
}
|
}
|
||||||
|
@ -188,6 +194,7 @@ class QueryRunner {
|
||||||
enrichedQuery[key] = processStringSync(fields[key], parameters, {
|
enrichedQuery[key] = processStringSync(fields[key], parameters, {
|
||||||
noEscaping: true,
|
noEscaping: true,
|
||||||
noHelpers: true,
|
noHelpers: true,
|
||||||
|
escapeNewlines: true,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
enrichedQuery[key] = fields[key]
|
enrichedQuery[key] = fields[key]
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"incremental": true
|
"incremental": true,
|
||||||
|
"types": [ "node", "jest"],
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src/**/*",
|
"./src/**/*",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"description": "Handlebars wrapper for Budibase templating.",
|
"description": "Handlebars wrapper for Budibase templating.",
|
||||||
"main": "src/index.cjs",
|
"main": "src/index.cjs",
|
||||||
"module": "dist/bundle.mjs",
|
"module": "dist/bundle.mjs",
|
||||||
|
@ -13,6 +13,11 @@
|
||||||
},
|
},
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src",
|
||||||
|
"manifest.json"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && rollup -c",
|
"build": "tsc && rollup -c",
|
||||||
"dev:builder": "tsc && rollup -cw",
|
"dev:builder": "tsc && rollup -cw",
|
||||||
|
|
|
@ -21,7 +21,7 @@ const HELPERS = [
|
||||||
// javascript helper
|
// javascript helper
|
||||||
new Helper(HelperFunctionNames.JS, processJS, false),
|
new Helper(HelperFunctionNames.JS, processJS, false),
|
||||||
// this help is applied to all statements
|
// this help is applied to all statements
|
||||||
new Helper(HelperFunctionNames.ALL, value => {
|
new Helper(HelperFunctionNames.ALL, (value, { __opts }) => {
|
||||||
if (
|
if (
|
||||||
value != null &&
|
value != null &&
|
||||||
typeof value === "object" &&
|
typeof value === "object" &&
|
||||||
|
@ -36,7 +36,11 @@ const HELPERS = [
|
||||||
if (value && value.string) {
|
if (value && value.string) {
|
||||||
value = value.string
|
value = value.string
|
||||||
}
|
}
|
||||||
let text = new SafeString(value.replace(/&/g, "&"))
|
let text = value
|
||||||
|
if (__opts && __opts.escapeNewlines) {
|
||||||
|
text = value.replace(/\n/g, "\\n")
|
||||||
|
}
|
||||||
|
text = new SafeString(text.replace(/&/g, "&"))
|
||||||
if (text == null || typeof text !== "string") {
|
if (text == null || typeof text !== "string") {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
@ -62,10 +66,14 @@ module.exports.HelperNames = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.registerAll = handlebars => {
|
module.exports.registerMinimum = handlebars => {
|
||||||
for (let helper of HELPERS) {
|
for (let helper of HELPERS) {
|
||||||
helper.register(handlebars)
|
helper.register(handlebars)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.registerAll = handlebars => {
|
||||||
|
module.exports.registerMinimum(handlebars)
|
||||||
// register imported helpers
|
// register imported helpers
|
||||||
externalHandlebars.registerAll(handlebars)
|
externalHandlebars.registerAll(handlebars)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const handlebars = require("handlebars")
|
const handlebars = require("handlebars")
|
||||||
const { registerAll } = require("./helpers/index")
|
const { registerAll, registerMinimum } = require("./helpers/index")
|
||||||
const processors = require("./processors")
|
const processors = require("./processors")
|
||||||
const { atob, btoa } = require("./utilities")
|
const { atob, btoa } = require("./utilities")
|
||||||
const manifest = require("../manifest.json")
|
const manifest = require("../manifest.json")
|
||||||
|
@ -8,10 +8,13 @@ const { FIND_HBS_REGEX, FIND_DOUBLE_HBS_REGEX } = require("./utilities")
|
||||||
const hbsInstance = handlebars.create()
|
const hbsInstance = handlebars.create()
|
||||||
registerAll(hbsInstance)
|
registerAll(hbsInstance)
|
||||||
const hbsInstanceNoHelpers = handlebars.create()
|
const hbsInstanceNoHelpers = handlebars.create()
|
||||||
|
registerMinimum(hbsInstanceNoHelpers)
|
||||||
const defaultOpts = {
|
const defaultOpts = {
|
||||||
noHelpers: false,
|
noHelpers: false,
|
||||||
cacheTemplates: false,
|
cacheTemplates: false,
|
||||||
noEscaping: false,
|
noEscaping: false,
|
||||||
|
escapeNewlines: false,
|
||||||
|
noFinalise: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,15 +37,14 @@ function createTemplate(string, opts) {
|
||||||
opts = { ...defaultOpts, ...opts }
|
opts = { ...defaultOpts, ...opts }
|
||||||
|
|
||||||
// Finalising adds a helper, can't do this with no helpers
|
// Finalising adds a helper, can't do this with no helpers
|
||||||
const shouldFinalise = !opts.noHelpers
|
const key = `${string}-${JSON.stringify(opts)}`
|
||||||
const key = `${string}${shouldFinalise}${opts.noEscaping}`
|
|
||||||
|
|
||||||
// Reuse the cached template is possible
|
// Reuse the cached template is possible
|
||||||
if (opts.cacheTemplates && templateCache[key]) {
|
if (opts.cacheTemplates && templateCache[key]) {
|
||||||
return templateCache[key]
|
return templateCache[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
string = processors.preprocess(string, shouldFinalise)
|
string = processors.preprocess(string, opts)
|
||||||
|
|
||||||
// Optionally disable built in HBS escaping
|
// Optionally disable built in HBS escaping
|
||||||
if (opts.noEscaping) {
|
if (opts.noEscaping) {
|
||||||
|
@ -145,6 +147,7 @@ module.exports.processStringSync = (string, context, opts) => {
|
||||||
return processors.postprocess(
|
return processors.postprocess(
|
||||||
template({
|
template({
|
||||||
now: new Date(now).toISOString(),
|
now: new Date(now).toISOString(),
|
||||||
|
__opts: opts,
|
||||||
...context,
|
...context,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -200,7 +203,10 @@ module.exports.isValid = (string, opts) => {
|
||||||
// don't really need a real context to check if its valid
|
// don't really need a real context to check if its valid
|
||||||
const context = {}
|
const context = {}
|
||||||
try {
|
try {
|
||||||
const template = createTemplate(string, opts)
|
const template = createTemplate(string, {
|
||||||
|
...opts,
|
||||||
|
noFinalise: true,
|
||||||
|
})
|
||||||
template(context)
|
template(context)
|
||||||
return true
|
return true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ const { FIND_HBS_REGEX } = require("../utilities")
|
||||||
const preprocessor = require("./preprocessor")
|
const preprocessor = require("./preprocessor")
|
||||||
const postprocessor = require("./postprocessor")
|
const postprocessor = require("./postprocessor")
|
||||||
|
|
||||||
function process(output, processors) {
|
function process(output, processors, opts) {
|
||||||
for (let processor of processors) {
|
for (let processor of processors) {
|
||||||
// if a literal statement has occurred stop
|
// if a literal statement has occurred stop
|
||||||
if (typeof output !== "string") {
|
if (typeof output !== "string") {
|
||||||
|
@ -15,24 +15,22 @@ function process(output, processors) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (let match of matches) {
|
for (let match of matches) {
|
||||||
output = processor.process(output, match)
|
output = processor.process(output, match, opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.preprocess = (string, finalise = true) => {
|
module.exports.preprocess = (string, opts) => {
|
||||||
let processors = preprocessor.processors
|
let processors = preprocessor.processors
|
||||||
// the pre-processor finalisation stops handlebars from ever throwing an error
|
if (opts.noFinalise) {
|
||||||
// might want to pre-process for other benefits but still want to see errors
|
|
||||||
if (!finalise) {
|
|
||||||
processors = processors.filter(
|
processors = processors.filter(
|
||||||
processor => processor.name !== preprocessor.PreprocessorNames.FINALISE
|
processor => processor.name !== preprocessor.PreprocessorNames.FINALISE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
return process(string, processors, opts)
|
||||||
|
}
|
||||||
|
module.exports.postprocess = string => {
|
||||||
|
let processors = postprocessor.processors
|
||||||
return process(string, processors)
|
return process(string, processors)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.postprocess = string => {
|
|
||||||
return process(string, postprocessor.processors)
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,6 +16,8 @@ class Postprocessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.PostProcessorNames = PostProcessorNames
|
||||||
|
|
||||||
module.exports.processors = [
|
module.exports.processors = [
|
||||||
new Postprocessor(PostProcessorNames.CONVERT_LITERALS, statement => {
|
new Postprocessor(PostProcessorNames.CONVERT_LITERALS, statement => {
|
||||||
if (typeof statement !== "string" || !statement.includes(LITERAL_MARKER)) {
|
if (typeof statement !== "string" || !statement.includes(LITERAL_MARKER)) {
|
||||||
|
|
|
@ -16,8 +16,8 @@ class Preprocessor {
|
||||||
this.fn = fn
|
this.fn = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
process(fullString, statement) {
|
process(fullString, statement, opts) {
|
||||||
const output = this.fn(statement)
|
const output = this.fn(statement, opts)
|
||||||
const idx = fullString.indexOf(statement)
|
const idx = fullString.indexOf(statement)
|
||||||
return swapStrings(fullString, idx, statement.length, output)
|
return swapStrings(fullString, idx, statement.length, output)
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,8 @@ module.exports.processors = [
|
||||||
return statement
|
return statement
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new Preprocessor(PreprocessorNames.FINALISE, statement => {
|
new Preprocessor(PreprocessorNames.FINALISE, (statement, opts) => {
|
||||||
|
const noHelpers = opts && opts.noHelpers
|
||||||
let insideStatement = statement.slice(2, statement.length - 2)
|
let insideStatement = statement.slice(2, statement.length - 2)
|
||||||
if (insideStatement.charAt(0) === " ") {
|
if (insideStatement.charAt(0) === " ") {
|
||||||
insideStatement = insideStatement.slice(1)
|
insideStatement = insideStatement.slice(1)
|
||||||
|
@ -63,7 +64,10 @@ module.exports.processors = [
|
||||||
return statement
|
return statement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (HelperNames().some(option => option.includes(possibleHelper))) {
|
if (
|
||||||
|
!noHelpers &&
|
||||||
|
HelperNames().some(option => option.includes(possibleHelper))
|
||||||
|
) {
|
||||||
insideStatement = `(${insideStatement})`
|
insideStatement = `(${insideStatement})`
|
||||||
}
|
}
|
||||||
return `{{ all ${insideStatement} }}`
|
return `{{ all ${insideStatement} }}`
|
||||||
|
|
|
@ -59,3 +59,33 @@ describe("attempt some complex problems", () => {
|
||||||
expect(output).toBe("nulltest")
|
expect(output).toBe("nulltest")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("check behaviour with newlines", () => {
|
||||||
|
const context = {
|
||||||
|
binding: `Hello
|
||||||
|
there`
|
||||||
|
}
|
||||||
|
it("should escape new line to \\n with double brace", async () => {
|
||||||
|
const hbs = JSON.stringify({
|
||||||
|
body: "{{ binding }}"
|
||||||
|
})
|
||||||
|
const output = await processString(hbs, context, { escapeNewlines: true })
|
||||||
|
expect(JSON.parse(output).body).toBe(context.binding)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should work the same with triple brace", async () => {
|
||||||
|
const hbs = JSON.stringify({
|
||||||
|
body: "{{{ binding }}}"
|
||||||
|
})
|
||||||
|
const output = await processString(hbs, context, { escapeNewlines: true })
|
||||||
|
expect(JSON.parse(output).body).toBe(context.binding)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should still work with helpers disabled", async () => {
|
||||||
|
const hbs = JSON.stringify({
|
||||||
|
body: "{{ binding }}"
|
||||||
|
})
|
||||||
|
const output = await processString(hbs, context, { escapeNewlines: true, noHelpers: true })
|
||||||
|
expect(JSON.parse(output).body).toBe(context.binding)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -20,7 +20,7 @@ describe("test that it can run without helpers", () => {
|
||||||
)
|
)
|
||||||
const valid = await processString("{{ avg 1 1 1 }}", {})
|
const valid = await processString("{{ avg 1 1 1 }}", {})
|
||||||
expect(valid).toBe("1")
|
expect(valid).toBe("1")
|
||||||
expect(output).toBe("{{ avg 1 1 1 }}")
|
expect(output).toBe("")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.49-alpha.14",
|
"version": "1.0.50-alpha.5",
|
||||||
"description": "Budibase background service",
|
"description": "Budibase background service",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -34,8 +34,8 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/backend-core": "^1.0.49-alpha.14",
|
"@budibase/backend-core": "^1.0.50-alpha.5",
|
||||||
"@budibase/string-templates": "^1.0.49-alpha.14",
|
"@budibase/string-templates": "^1.0.50-alpha.5",
|
||||||
"@koa/router": "^8.0.0",
|
"@koa/router": "^8.0.0",
|
||||||
"@sentry/node": "^6.0.0",
|
"@sentry/node": "^6.0.0",
|
||||||
"@techpass/passport-openidconnect": "^0.3.0",
|
"@techpass/passport-openidconnect": "^0.3.0",
|
||||||
|
|
Loading…
Reference in a new issue