mirror of
https://github.com/chartdb/chartdb.git
synced 2025-10-24 08:33:44 +00:00
Compare commits
130 Commits
v1.13.0
...
jf/add_edi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7382626b92 | ||
|
|
6f6b59c74f | ||
|
|
4f1a378762 | ||
|
|
1a6688e85e | ||
|
|
5e81c1848a | ||
|
|
2bd9ca25b2 | ||
|
|
b016a70691 | ||
|
|
a0fb1ed08b | ||
|
|
ffddcdcc98 | ||
|
|
fe9ef275b8 | ||
|
|
df89f0b6b9 | ||
|
|
534d2858af | ||
|
|
2a64deebb8 | ||
|
|
e5e1d59327 | ||
|
|
aa290615ca | ||
|
|
ec6e46fe81 | ||
|
|
ac128d67de | ||
|
|
07937a2f51 | ||
|
|
d8e0bc7db8 | ||
|
|
1ce265781b | ||
|
|
60c5675cbf | ||
|
|
66b086378c | ||
|
|
abd2a6ccbe | ||
|
|
459c5f1ce3 | ||
|
|
44be48ff3a | ||
|
|
ad8e34483f | ||
|
|
215d57979d | ||
|
|
ec3719ebce | ||
|
|
0a5874a69b | ||
|
|
7e0fdd1595 | ||
|
|
2531a7023f | ||
|
|
73daf0df21 | ||
|
|
c77c983989 | ||
|
|
0aaa451479 | ||
|
|
b697e26170 | ||
|
|
04d91c67b1 | ||
|
|
d0dee84970 | ||
|
|
b4ccfcdcde | ||
|
|
1759b0b9f2 | ||
|
|
ab4845c772 | ||
|
|
0545b41140 | ||
|
|
4520f8b1f7 | ||
|
|
712bdf5b95 | ||
|
|
d7c9536272 | ||
|
|
815a52f192 | ||
|
|
f1a4298362 | ||
|
|
b8f2141bd2 | ||
|
|
eaebe34768 | ||
|
|
0d623a86b1 | ||
|
|
19fd94c6bd | ||
|
|
0da3caeeac | ||
|
|
cb2ba66233 | ||
|
|
8a2267281b | ||
|
|
41ba251377 | ||
|
|
e9c5442d9d | ||
|
|
4f1d3295c0 | ||
|
|
5936500ca0 | ||
|
|
43fc1d7fc2 | ||
|
|
8dfa7cc62e | ||
|
|
23e93bfd01 | ||
|
|
16f9f4671e | ||
|
|
0c300e5e72 | ||
|
|
b9a1e78b53 | ||
|
|
337f7cdab4 | ||
|
|
1b0390f0b7 | ||
|
|
bc52933b58 | ||
|
|
2fdad2344c | ||
|
|
0c7eaa2df2 | ||
|
|
a5f8e56b3c | ||
|
|
8ffde62c1a | ||
|
|
39247b77a2 | ||
|
|
984b2aeee2 | ||
|
|
eed104be5b | ||
|
|
00bd535b3c | ||
|
|
18e914242f | ||
|
|
e68837a34a | ||
|
|
b30162d98b | ||
|
|
dba372d25a | ||
|
|
2eb48e75d3 | ||
|
|
867903cd5f | ||
|
|
8aeb1df0ad | ||
|
|
6bea827293 | ||
|
|
a119854da7 | ||
|
|
bfbfd7b843 | ||
|
|
0ca7008735 | ||
|
|
4bc71c52ff | ||
|
|
8f27f10dec | ||
|
|
a93ec2cab9 | ||
|
|
386e40a0bf | ||
|
|
bda150d4b6 | ||
|
|
87836e53d1 | ||
|
|
7e0483f1a5 | ||
|
|
309ee9cb0f | ||
|
|
79b885502e | ||
|
|
745bdee86d | ||
|
|
08eb9cc55f | ||
|
|
778f85d492 | ||
|
|
fb92be7d3e | ||
|
|
6df588f40e | ||
|
|
b46ed58dff | ||
|
|
0d9f57a9c9 | ||
|
|
b7dbe54c83 | ||
|
|
43d1dfff71 | ||
|
|
9949a46ee3 | ||
|
|
dfbcf05b2f | ||
|
|
f56fab9876 | ||
|
|
c9ea7da092 | ||
|
|
22d46e1e90 | ||
|
|
6af94afc56 | ||
|
|
f7f92903de | ||
|
|
b35e17526b | ||
|
|
bf32c08d37 | ||
|
|
5d337409d6 | ||
|
|
67f5ac303e | ||
|
|
578546a171 | ||
|
|
aa0b629a3e | ||
|
|
69beaa0a83 | ||
|
|
4fcc49d49a | ||
|
|
d15985e399 | ||
|
|
d429128e65 | ||
|
|
2fce8326b6 | ||
|
|
433c68a33d | ||
|
|
58acb65f12 | ||
|
|
7978955819 | ||
|
|
c6118e0cdb | ||
|
|
7d063b905f | ||
|
|
e0ff198c3f | ||
|
|
8b86e1c229 | ||
|
|
24be28a662 | ||
|
|
c6788b4917 |
5
.github/workflows/ci.yaml
vendored
5
.github/workflows/ci.yaml
vendored
@@ -24,4 +24,7 @@ jobs:
|
||||
run: npm run lint
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
run: npm run build
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test:ci
|
||||
2
.github/workflows/cla.yaml
vendored
2
.github/workflows/cla.yaml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write # this can be 'read' if the signatures are in remote repository
|
||||
contents: read
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
|
||||
|
||||
136
CHANGELOG.md
136
CHANGELOG.md
@@ -1,5 +1,141 @@
|
||||
# Changelog
|
||||
|
||||
## [1.15.1](https://github.com/chartdb/chartdb/compare/v1.15.0...v1.15.1) (2025-08-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add actions menu to diagram list + add duplicate diagram ([#876](https://github.com/chartdb/chartdb/issues/876)) ([abd2a6c](https://github.com/chartdb/chartdb/commit/abd2a6ccbe1aa63db44ec28b3eff525cc5d3f8b0))
|
||||
* **custom-types:** Make schema optional ([#866](https://github.com/chartdb/chartdb/issues/866)) ([60c5675](https://github.com/chartdb/chartdb/commit/60c5675cbfe205859d2d0c9848d8345a0a854671))
|
||||
* handle quoted identifiers with special characters in SQL import/export and DBML generation ([#877](https://github.com/chartdb/chartdb/issues/877)) ([66b0863](https://github.com/chartdb/chartdb/commit/66b086378cd63347acab5fc7f13db7db4feaa872))
|
||||
|
||||
## [1.15.0](https://github.com/chartdb/chartdb/compare/v1.14.0...v1.15.0) (2025-08-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add auto increment support for fields with database-specific export ([#851](https://github.com/chartdb/chartdb/issues/851)) ([c77c983](https://github.com/chartdb/chartdb/commit/c77c983989ae38a6b1139dd9015f4f3178d4e103))
|
||||
* **filter:** filter tables by areas ([#836](https://github.com/chartdb/chartdb/issues/836)) ([e9c5442](https://github.com/chartdb/chartdb/commit/e9c5442d9df2beadad78187da3363bb6406636c4))
|
||||
* include foreign keys inline in SQLite CREATE TABLE statements ([#833](https://github.com/chartdb/chartdb/issues/833)) ([43fc1d7](https://github.com/chartdb/chartdb/commit/43fc1d7fc26876b22c61405f6c3df89fc66b7992))
|
||||
* **postgres:** add support hash index types ([#812](https://github.com/chartdb/chartdb/issues/812)) ([0d623a8](https://github.com/chartdb/chartdb/commit/0d623a86b1cb7cbd223e10ad23d09fc0e106c006))
|
||||
* support create views ([#868](https://github.com/chartdb/chartdb/issues/868)) ([0a5874a](https://github.com/chartdb/chartdb/commit/0a5874a69b6323145430c1fb4e3482ac7da4916c))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* area filter logic ([#861](https://github.com/chartdb/chartdb/issues/861)) ([73daf0d](https://github.com/chartdb/chartdb/commit/73daf0df2142a29c2eeebe60b43198bcca869026))
|
||||
* **area filter:** fix dragging tables over filtered areas ([#842](https://github.com/chartdb/chartdb/issues/842)) ([19fd94c](https://github.com/chartdb/chartdb/commit/19fd94c6bde3a9ec749cd1ccacbedb6abc96d037))
|
||||
* **canvas:** delete table + area together bug ([#859](https://github.com/chartdb/chartdb/issues/859)) ([b697e26](https://github.com/chartdb/chartdb/commit/b697e26170da95dcb427ff6907b6f663c98ba59f))
|
||||
* **cla:** Harden action ([#867](https://github.com/chartdb/chartdb/issues/867)) ([ad8e344](https://github.com/chartdb/chartdb/commit/ad8e34483fdf4226de76c9e7768bc2ba9bf154de))
|
||||
* DBML export error with multi-line table comments for SQL Server ([#852](https://github.com/chartdb/chartdb/issues/852)) ([0545b41](https://github.com/chartdb/chartdb/commit/0545b411407b2449220d10981a04c3e368a90ca3))
|
||||
* filter to default schema on load new diagram ([#849](https://github.com/chartdb/chartdb/issues/849)) ([712bdf5](https://github.com/chartdb/chartdb/commit/712bdf5b958919d940c4f2a1c3b7c7e969990f02))
|
||||
* **filter:** filter toggle issues with no schemas dbs ([#856](https://github.com/chartdb/chartdb/issues/856)) ([d0dee84](https://github.com/chartdb/chartdb/commit/d0dee849702161d979b4f589a7e6579fbaade22d))
|
||||
* **filters:** refactor diagram filters - remove schema filter ([#832](https://github.com/chartdb/chartdb/issues/832)) ([4f1d329](https://github.com/chartdb/chartdb/commit/4f1d3295c09782ab46d82ce21b662032aa094f22))
|
||||
* for sqlite import - add more types & include type parameters ([#834](https://github.com/chartdb/chartdb/issues/834)) ([5936500](https://github.com/chartdb/chartdb/commit/5936500ca00a57b3f161616264c26152a13c36d2))
|
||||
* improve creating view to table dependency ([#874](https://github.com/chartdb/chartdb/issues/874)) ([44be48f](https://github.com/chartdb/chartdb/commit/44be48ff3ad1361279331c17364090b13af471a1))
|
||||
* initially show filter when filter active ([#853](https://github.com/chartdb/chartdb/issues/853)) ([ab4845c](https://github.com/chartdb/chartdb/commit/ab4845c7728e6e0b2d852f8005921fd90630eef9))
|
||||
* **menu:** clear file menu ([#843](https://github.com/chartdb/chartdb/issues/843)) ([eaebe34](https://github.com/chartdb/chartdb/commit/eaebe3476824af779214a354b3e991923a22f195))
|
||||
* merge relationship & dependency sections to ref section ([#870](https://github.com/chartdb/chartdb/issues/870)) ([ec3719e](https://github.com/chartdb/chartdb/commit/ec3719ebce4664b2aa6e3322fb3337e72bc21015))
|
||||
* move dbml into sections menu ([#862](https://github.com/chartdb/chartdb/issues/862)) ([2531a70](https://github.com/chartdb/chartdb/commit/2531a7023f36ef29e67c0da6bca4fd0346b18a51))
|
||||
* open filter by default ([#863](https://github.com/chartdb/chartdb/issues/863)) ([7e0fdd1](https://github.com/chartdb/chartdb/commit/7e0fdd1595bffe29e769d29602d04f42edfe417e))
|
||||
* preserve composite primary key constraint names across import/export workflows ([#869](https://github.com/chartdb/chartdb/issues/869)) ([215d579](https://github.com/chartdb/chartdb/commit/215d57979df2e91fa61988acff590daad2f4e771))
|
||||
* prevent false change detection in DBML editor by stripping public schema on import ([#858](https://github.com/chartdb/chartdb/issues/858)) ([0aaa451](https://github.com/chartdb/chartdb/commit/0aaa451479911d047e4cc83f063afa68a122ba9b))
|
||||
* remove unnecessary space ([#845](https://github.com/chartdb/chartdb/issues/845)) ([f1a4298](https://github.com/chartdb/chartdb/commit/f1a429836221aacdda73b91665bf33ffb011164c))
|
||||
* reorder with areas ([#846](https://github.com/chartdb/chartdb/issues/846)) ([d7c9536](https://github.com/chartdb/chartdb/commit/d7c9536272cf1d42104b7064ea448d128d091a20))
|
||||
* **select-box:** fix select box issue in dialog ([#840](https://github.com/chartdb/chartdb/issues/840)) ([cb2ba66](https://github.com/chartdb/chartdb/commit/cb2ba66233c8c04e2d963cf2d210499d8512a268))
|
||||
* set default filter only if has more than 1 schemas ([#855](https://github.com/chartdb/chartdb/issues/855)) ([b4ccfcd](https://github.com/chartdb/chartdb/commit/b4ccfcdcde2f3565b0d3bbc46fa1715feb6cd925))
|
||||
* show default schema first ([#854](https://github.com/chartdb/chartdb/issues/854)) ([1759b0b](https://github.com/chartdb/chartdb/commit/1759b0b9f271ed25f7c71f26c344e3f1d97bc5fb))
|
||||
* **sidebar:** add titles to sidebar ([#844](https://github.com/chartdb/chartdb/issues/844)) ([b8f2141](https://github.com/chartdb/chartdb/commit/b8f2141bd2e67272030896fb4009a7925f9f09e4))
|
||||
* **sql-import:** fix SQL Server foreign key parsing for tables without schema prefix ([#857](https://github.com/chartdb/chartdb/issues/857)) ([04d91c6](https://github.com/chartdb/chartdb/commit/04d91c67b1075e94948f75186878e633df7abbca))
|
||||
* **table colors:** switch to default table color ([#841](https://github.com/chartdb/chartdb/issues/841)) ([0da3cae](https://github.com/chartdb/chartdb/commit/0da3caeeac37926dd22f38d98423611f39c0412a))
|
||||
* update filter on adding table ([#838](https://github.com/chartdb/chartdb/issues/838)) ([41ba251](https://github.com/chartdb/chartdb/commit/41ba25137789dda25266178cd7c96ecbb37e62a4))
|
||||
|
||||
## [1.14.0](https://github.com/chartdb/chartdb/compare/v1.13.2...v1.14.0) (2025-08-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add floating "Show All" button when tables are out of view ([#787](https://github.com/chartdb/chartdb/issues/787)) ([bda150d](https://github.com/chartdb/chartdb/commit/bda150d4b6d6fb90beb423efba69349d21a037a5))
|
||||
* add table selection for large database imports ([#776](https://github.com/chartdb/chartdb/issues/776)) ([0d9f57a](https://github.com/chartdb/chartdb/commit/0d9f57a9c969a67e350d6bf25e07c3a9ef5bba39))
|
||||
* **canvas:** Add filter tables on canvas ([#774](https://github.com/chartdb/chartdb/issues/774)) ([dfbcf05](https://github.com/chartdb/chartdb/commit/dfbcf05b2f595f5b7b77dd61abf77e6e07acaf8f))
|
||||
* **custom-types:** add highlight fields option for custom types ([#726](https://github.com/chartdb/chartdb/issues/726)) ([7e0483f](https://github.com/chartdb/chartdb/commit/7e0483f1a5512a6a737baf61caf7513e043f2e96))
|
||||
* **datatypes:** Add decimal / numeric attribute support + organize field row ([#715](https://github.com/chartdb/chartdb/issues/715)) ([778f85d](https://github.com/chartdb/chartdb/commit/778f85d49214232a39710e47bb5d4ec41b75d427))
|
||||
* **dbml:** Edit Diagram Directly from DBML ([#819](https://github.com/chartdb/chartdb/issues/819)) ([1b0390f](https://github.com/chartdb/chartdb/commit/1b0390f0b7652fe415540b7942cf53ec87143f08))
|
||||
* **default value:** add default value option to table field settings ([#770](https://github.com/chartdb/chartdb/issues/770)) ([c9ea7da](https://github.com/chartdb/chartdb/commit/c9ea7da0923ff991cb936235674d9a52b8186137))
|
||||
* enhance primary key and unique field handling logic ([#817](https://github.com/chartdb/chartdb/issues/817)) ([39247b7](https://github.com/chartdb/chartdb/commit/39247b77a299caa4f29ea434af3028155c6d37ed))
|
||||
* implement area grouping with parent-child relationships ([#762](https://github.com/chartdb/chartdb/issues/762)) ([b35e175](https://github.com/chartdb/chartdb/commit/b35e17526b3c9b918928ae5f3f89711ea7b2529c))
|
||||
* **schema:** support create new schema ([#801](https://github.com/chartdb/chartdb/issues/801)) ([867903c](https://github.com/chartdb/chartdb/commit/867903cd5f24d96ce1fe718dc9b562e2f2b75276))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add open and create diagram to side menu ([#757](https://github.com/chartdb/chartdb/issues/757)) ([67f5ac3](https://github.com/chartdb/chartdb/commit/67f5ac303ebf5ada97d5c80fb08a2815ca205a91))
|
||||
* add PostgreSQL tests and fix parsing SQL ([#760](https://github.com/chartdb/chartdb/issues/760)) ([5d33740](https://github.com/chartdb/chartdb/commit/5d337409d64d1078b538350016982a98e684c06c))
|
||||
* area resizers size ([#830](https://github.com/chartdb/chartdb/issues/830)) ([23e93bf](https://github.com/chartdb/chartdb/commit/23e93bfd01d741dd3d11aa5c479cef97e1a86fa6))
|
||||
* **area:** redo/undo after dragging an area with tables ([#767](https://github.com/chartdb/chartdb/issues/767)) ([6af94af](https://github.com/chartdb/chartdb/commit/6af94afc56cf8987b8fc9e3f0a9bfa966de35408))
|
||||
* **canvas filter:** improve scroller on canvas filter ([#799](https://github.com/chartdb/chartdb/issues/799)) ([6bea827](https://github.com/chartdb/chartdb/commit/6bea82729362a8c7b73dc089ddd9e52bae176aa2))
|
||||
* **canvas:** fix filter eye button ([#780](https://github.com/chartdb/chartdb/issues/780)) ([b7dbe54](https://github.com/chartdb/chartdb/commit/b7dbe54c83c75cfe3c556f7a162055dcfe2de23d))
|
||||
* clone of custom types ([#804](https://github.com/chartdb/chartdb/issues/804)) ([b30162d](https://github.com/chartdb/chartdb/commit/b30162d98bc659a61aae023cdeaead4ce25c7ae9))
|
||||
* **cockroachdb:** support schema creation for cockroachdb ([#803](https://github.com/chartdb/chartdb/issues/803)) ([dba372d](https://github.com/chartdb/chartdb/commit/dba372d25a8c642baf8600d05aa154882729d446))
|
||||
* **dbml actions:** set dbml tooltips side ([#798](https://github.com/chartdb/chartdb/issues/798)) ([a119854](https://github.com/chartdb/chartdb/commit/a119854da7c935eb595984ea9398e04136ce60c4))
|
||||
* **dbml editor:** move tooltips button to be on the right ([#797](https://github.com/chartdb/chartdb/issues/797)) ([bfbfd7b](https://github.com/chartdb/chartdb/commit/bfbfd7b843f96c894b1966ad95393b866c927466))
|
||||
* **dbml export:** fix handle tables with same name under different schemas ([#807](https://github.com/chartdb/chartdb/issues/807)) ([18e9142](https://github.com/chartdb/chartdb/commit/18e914242faccd6376fe5a7cd5a4478667f065ee))
|
||||
* **dbml export:** handle tables with same name under different schemas ([#806](https://github.com/chartdb/chartdb/issues/806)) ([e68837a](https://github.com/chartdb/chartdb/commit/e68837a34aa635fb6fc02c7f1289495e5c448242))
|
||||
* **dbml field comments:** support export field comments in dbml ([#796](https://github.com/chartdb/chartdb/issues/796)) ([0ca7008](https://github.com/chartdb/chartdb/commit/0ca700873577bbfbf1dd3f8088c258fc89b10c53))
|
||||
* **dbml import:** fix dbml import types + schemas ([#808](https://github.com/chartdb/chartdb/issues/808)) ([00bd535](https://github.com/chartdb/chartdb/commit/00bd535b3c62d26d25a6276d52beb10e26afad76))
|
||||
* **dbml-export:** merge field attributes into single brackets and fix schema syntax ([#790](https://github.com/chartdb/chartdb/issues/790)) ([309ee9c](https://github.com/chartdb/chartdb/commit/309ee9cb0ff1f5a68ed183e3919e1a11a8410909))
|
||||
* **dbml-import:** handle unsupported DBML features and add comprehensive tests ([#766](https://github.com/chartdb/chartdb/issues/766)) ([22d46e1](https://github.com/chartdb/chartdb/commit/22d46e1e90729730cc25dd6961bfe8c3d2ae0c98))
|
||||
* **dbml:** dbml indentation ([#829](https://github.com/chartdb/chartdb/issues/829)) ([16f9f46](https://github.com/chartdb/chartdb/commit/16f9f4671e011eb66ba9594bed47570eda3eed66))
|
||||
* **dbml:** dbml note syntax ([#826](https://github.com/chartdb/chartdb/issues/826)) ([337f7cd](https://github.com/chartdb/chartdb/commit/337f7cdab4759d15cb4d25a8c0e9394e99ba33d4))
|
||||
* **dbml:** fix dbml output format ([#815](https://github.com/chartdb/chartdb/issues/815)) ([eed104b](https://github.com/chartdb/chartdb/commit/eed104be5ba2b7d9940ffac38e7877722ad764fc))
|
||||
* **dbml:** fix schemas with same table names ([#828](https://github.com/chartdb/chartdb/issues/828)) ([0c300e5](https://github.com/chartdb/chartdb/commit/0c300e5e72cc5ff22cac42f8dbaed167061157c6))
|
||||
* **dbml:** import dbml notes (table + fields) ([#827](https://github.com/chartdb/chartdb/issues/827)) ([b9a1e78](https://github.com/chartdb/chartdb/commit/b9a1e78b53c932c0b1a12ee38b62494a5c2f9348))
|
||||
* **dbml:** support multiple relationships on same field in inline DBML ([#822](https://github.com/chartdb/chartdb/issues/822)) ([a5f8e56](https://github.com/chartdb/chartdb/commit/a5f8e56b3ca97b851b6953481644d3a3ff7ce882))
|
||||
* **dbml:** support spaces in names ([#794](https://github.com/chartdb/chartdb/issues/794)) ([8f27f10](https://github.com/chartdb/chartdb/commit/8f27f10dec96af400dc2c12a30b22b3a346803a9))
|
||||
* fix hotkeys on form elements ([#778](https://github.com/chartdb/chartdb/issues/778)) ([43d1dff](https://github.com/chartdb/chartdb/commit/43d1dfff71f2b960358a79b0112b78d11df91fb7))
|
||||
* fix screen freeze after schema select ([#800](https://github.com/chartdb/chartdb/issues/800)) ([8aeb1df](https://github.com/chartdb/chartdb/commit/8aeb1df0ad353c49e91243453f24bfa5921a89ab))
|
||||
* **i18n:** add Croatian (hr) language support ([#802](https://github.com/chartdb/chartdb/issues/802)) ([2eb48e7](https://github.com/chartdb/chartdb/commit/2eb48e75d303d622f51327d22502a6f78e7fb32d))
|
||||
* improve SQL export formatting and add schema-aware FK grouping ([#783](https://github.com/chartdb/chartdb/issues/783)) ([6df588f](https://github.com/chartdb/chartdb/commit/6df588f40e6e7066da6125413b94466429d48767))
|
||||
* lost in canvas button animation ([#793](https://github.com/chartdb/chartdb/issues/793)) ([a93ec2c](https://github.com/chartdb/chartdb/commit/a93ec2cab906d0e4431d8d1668adcf2dbfc3c80f))
|
||||
* **readonly:** fix zoom out on readonly ([#818](https://github.com/chartdb/chartdb/issues/818)) ([8ffde62](https://github.com/chartdb/chartdb/commit/8ffde62c1a00893c4bf6b4dd39068df530375416))
|
||||
* remove error lag after autofix ([#764](https://github.com/chartdb/chartdb/issues/764)) ([bf32c08](https://github.com/chartdb/chartdb/commit/bf32c08d37c02ee6d7946a41633bb97b2271fcb7))
|
||||
* remove unnecessary import ([#791](https://github.com/chartdb/chartdb/issues/791)) ([87836e5](https://github.com/chartdb/chartdb/commit/87836e53d145b825f9c4f80abca72f418df50e6c))
|
||||
* **scroll:** disable scroll x behavior ([#795](https://github.com/chartdb/chartdb/issues/795)) ([4bc71c5](https://github.com/chartdb/chartdb/commit/4bc71c52ff5c462800d8530b72a5aadb7d7f85ed))
|
||||
* set focus on filter search ([#775](https://github.com/chartdb/chartdb/issues/775)) ([9949a46](https://github.com/chartdb/chartdb/commit/9949a46ee3ba7f46a2ea7f2c0d7101cc9336df4f))
|
||||
* solve issue with multiple render of tables ([#823](https://github.com/chartdb/chartdb/issues/823)) ([0c7eaa2](https://github.com/chartdb/chartdb/commit/0c7eaa2df20cfb6994b7e6251c760a2d4581c879))
|
||||
* **sql-export:** escape newlines and quotes in multi-line comments ([#765](https://github.com/chartdb/chartdb/issues/765)) ([f7f9290](https://github.com/chartdb/chartdb/commit/f7f92903def84a94ac0c66f625f96a6681383945))
|
||||
* **sql-server:** improvment for sql-server import via sql script ([#789](https://github.com/chartdb/chartdb/issues/789)) ([79b8855](https://github.com/chartdb/chartdb/commit/79b885502e3385e996a52093a3ccd5f6e469993a))
|
||||
* **table-node:** fix comment icon on field ([#786](https://github.com/chartdb/chartdb/issues/786)) ([745bdee](https://github.com/chartdb/chartdb/commit/745bdee86d07f1e9c3a2d24237c48c25b9a8eeea))
|
||||
* **table-node:** improve field spacing ([#785](https://github.com/chartdb/chartdb/issues/785)) ([08eb9cc](https://github.com/chartdb/chartdb/commit/08eb9cc55f0077f53afea6f9ce720341e1a583c2))
|
||||
* **table-select:** add loading indication for import ([#782](https://github.com/chartdb/chartdb/issues/782)) ([b46ed58](https://github.com/chartdb/chartdb/commit/b46ed58dff1ec74579fb1544dba46b0f77730c52))
|
||||
* **ui:** reduce spacing between primary key icon and short field types ([#816](https://github.com/chartdb/chartdb/issues/816)) ([984b2ae](https://github.com/chartdb/chartdb/commit/984b2aeee22c43cb9bda77df2c22087973079af4))
|
||||
* update MariaDB database import smart query ([#792](https://github.com/chartdb/chartdb/issues/792)) ([386e40a](https://github.com/chartdb/chartdb/commit/386e40a0bf93d9aef1486bb1e729d8f485e675eb))
|
||||
* update multiple schemas toast to require user action ([#771](https://github.com/chartdb/chartdb/issues/771)) ([f56fab9](https://github.com/chartdb/chartdb/commit/f56fab9876fb9fc46c6c708231324a90d8a7851d))
|
||||
* update relationship when table width changes via expand/shrink ([#825](https://github.com/chartdb/chartdb/issues/825)) ([bc52933](https://github.com/chartdb/chartdb/commit/bc52933b58bfe6bc73779d9401128254cbf497d5))
|
||||
|
||||
## [1.13.2](https://github.com/chartdb/chartdb/compare/v1.13.1...v1.13.2) (2025-07-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add DISABLE_ANALYTICS flag to opt-out of Fathom analytics ([#750](https://github.com/chartdb/chartdb/issues/750)) ([aa0b629](https://github.com/chartdb/chartdb/commit/aa0b629a3eaf8e8b60473ea3f28f769270c7714c))
|
||||
|
||||
## [1.13.1](https://github.com/chartdb/chartdb/compare/v1.13.0...v1.13.1) (2025-07-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **custom_types:** fix display custom types in select box ([#737](https://github.com/chartdb/chartdb/issues/737)) ([24be28a](https://github.com/chartdb/chartdb/commit/24be28a662c48fc5bc62e76446b9669d83d7d3e0))
|
||||
* **dbml-editor:** for some cases that the dbml had issues ([#739](https://github.com/chartdb/chartdb/issues/739)) ([e0ff198](https://github.com/chartdb/chartdb/commit/e0ff198c3fd416498dac5680bb323ec88c54b65c))
|
||||
* **dbml:** Filter duplicate tables at diagram level before export dbml ([#746](https://github.com/chartdb/chartdb/issues/746)) ([d429128](https://github.com/chartdb/chartdb/commit/d429128e65aa28c500eac2487356e4869506e948))
|
||||
* **export-sql:** conditionally show generic option and reorder by diagram type ([#708](https://github.com/chartdb/chartdb/issues/708)) ([c6118e0](https://github.com/chartdb/chartdb/commit/c6118e0cdb0e5caaf73447d33db2fde1a98efe60))
|
||||
* general performance improvements on canvas ([#751](https://github.com/chartdb/chartdb/issues/751)) ([4fcc49d](https://github.com/chartdb/chartdb/commit/4fcc49d49a76a4b886ffd6cf0b40cf2fc49952ec))
|
||||
* **import-database:** for custom types query to import supabase & timescale ([#745](https://github.com/chartdb/chartdb/issues/745)) ([2fce832](https://github.com/chartdb/chartdb/commit/2fce8326b67b751d38dd34f409fea574449d0298))
|
||||
* **import-db:** fix mariadb import ([#740](https://github.com/chartdb/chartdb/issues/740)) ([7d063b9](https://github.com/chartdb/chartdb/commit/7d063b905f19f51501468bd0bd794a25cf65e1be))
|
||||
* **performance:** improve storage provider performance ([#734](https://github.com/chartdb/chartdb/issues/734)) ([c6788b4](https://github.com/chartdb/chartdb/commit/c6788b49173d9cce23571daeb460285cb7cffb11))
|
||||
* resolve unresponsive cursor and input glitches when editing field comments ([#749](https://github.com/chartdb/chartdb/issues/749)) ([d15985e](https://github.com/chartdb/chartdb/commit/d15985e3999a0cd54213b2fb08c55d48a1b8b3b2))
|
||||
* **table name:** updates table name value when its updated from canvas/sidebar ([#716](https://github.com/chartdb/chartdb/issues/716)) ([8b86e1c](https://github.com/chartdb/chartdb/commit/8b86e1c22992aaadcce7ad5fc1d267c5a57a99f0))
|
||||
|
||||
## [1.13.0](https://github.com/chartdb/chartdb/compare/v1.12.0...v1.13.0) (2025-05-28)
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ FROM node:22-alpine AS builder
|
||||
ARG VITE_OPENAI_API_KEY
|
||||
ARG VITE_OPENAI_API_ENDPOINT
|
||||
ARG VITE_LLM_MODEL_NAME
|
||||
ARG VITE_HIDE_BUCKLE_DOT_DEV
|
||||
ARG VITE_HIDE_CHARTDB_CLOUD
|
||||
ARG VITE_DISABLE_ANALYTICS
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
@@ -16,7 +17,8 @@ COPY . .
|
||||
RUN echo "VITE_OPENAI_API_KEY=${VITE_OPENAI_API_KEY}" > .env && \
|
||||
echo "VITE_OPENAI_API_ENDPOINT=${VITE_OPENAI_API_ENDPOINT}" >> .env && \
|
||||
echo "VITE_LLM_MODEL_NAME=${VITE_LLM_MODEL_NAME}" >> .env && \
|
||||
echo "VITE_HIDE_BUCKLE_DOT_DEV=${VITE_HIDE_BUCKLE_DOT_DEV}" >> .env
|
||||
echo "VITE_HIDE_CHARTDB_CLOUD=${VITE_HIDE_CHARTDB_CLOUD}" >> .env && \
|
||||
echo "VITE_DISABLE_ANALYTICS=${VITE_DISABLE_ANALYTICS}" >> .env
|
||||
|
||||
RUN npm run build
|
||||
|
||||
|
||||
@@ -125,6 +125,8 @@ docker run \
|
||||
-p 8080:80 chartdb
|
||||
```
|
||||
|
||||
> **Privacy Note:** ChartDB includes privacy-focused analytics via Fathom Analytics. You can disable this by adding `-e DISABLE_ANALYTICS=true` to the run command or `--build-arg VITE_DISABLE_ANALYTICS=true` when building.
|
||||
|
||||
> **Note:** You must configure either Option 1 (OpenAI API key) OR Option 2 (Custom endpoint and model name) for AI capabilities to work. Do not mix the two options.
|
||||
|
||||
Open your browser and navigate to `http://localhost:8080`.
|
||||
|
||||
@@ -10,11 +10,12 @@ server {
|
||||
|
||||
location /config.js {
|
||||
default_type application/javascript;
|
||||
return 200 "window.env = {
|
||||
return 200 "window.env = {
|
||||
OPENAI_API_KEY: \"$OPENAI_API_KEY\",
|
||||
OPENAI_API_ENDPOINT: \"$OPENAI_API_ENDPOINT\",
|
||||
LLM_MODEL_NAME: \"$LLM_MODEL_NAME\",
|
||||
HIDE_BUCKLE_DOT_DEV: \"$HIDE_BUCKLE_DOT_DEV\"
|
||||
HIDE_CHARTDB_CLOUD: \"$HIDE_CHARTDB_CLOUD\",
|
||||
DISABLE_ANALYTICS: \"$DISABLE_ANALYTICS\"
|
||||
};";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Replace placeholders in nginx.conf
|
||||
envsubst '${OPENAI_API_KEY} ${OPENAI_API_ENDPOINT} ${LLM_MODEL_NAME} ${HIDE_BUCKLE_DOT_DEV}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf
|
||||
envsubst '${OPENAI_API_KEY} ${OPENAI_API_ENDPOINT} ${LLM_MODEL_NAME} ${HIDE_CHARTDB_CLOUD} ${DISABLE_ANALYTICS}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Start Nginx
|
||||
nginx -g "daemon off;"
|
||||
|
||||
28
index.html
28
index.html
@@ -4,8 +4,9 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="robots" content="max-image-preview:large" />
|
||||
<meta name="robots" content="noindex, max-image-preview:large" />
|
||||
<title>ChartDB - Create & Visualize Database Schema Diagrams</title>
|
||||
<link rel="canonical" href="https://chartdb.io" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
@@ -13,11 +14,26 @@
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script src="/config.js"></script>
|
||||
<script
|
||||
src="https://cdn.usefathom.com/script.js"
|
||||
data-site="PRHIVBNN"
|
||||
defer
|
||||
></script>
|
||||
<script>
|
||||
// Load analytics only if not disabled
|
||||
(function () {
|
||||
const disableAnalytics =
|
||||
(window.env && window.env.DISABLE_ANALYTICS === 'true') ||
|
||||
(typeof process !== 'undefined' &&
|
||||
process.env &&
|
||||
process.env.VITE_DISABLE_ANALYTICS === 'true');
|
||||
|
||||
if (!disableAnalytics) {
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://cdn.usefathom.com/script.js';
|
||||
script.setAttribute('data-site', 'PRHIVBNN');
|
||||
script.setAttribute('data-canonical', 'false');
|
||||
script.setAttribute('data-spa', 'auto');
|
||||
script.defer = true;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
1885
package-lock.json
generated
1885
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chartdb",
|
||||
"private": true,
|
||||
"version": "1.13.0",
|
||||
"version": "1.15.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -9,11 +9,15 @@
|
||||
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"preview": "vite preview",
|
||||
"prepare": "husky"
|
||||
"prepare": "husky",
|
||||
"test": "vitest",
|
||||
"test:ci": "vitest run --reporter=verbose --bail=1",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:coverage": "vitest --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^0.0.51",
|
||||
"@dbml/core": "^3.9.5",
|
||||
"@dbml/core": "^3.13.9",
|
||||
"@dnd-kit/sortable": "^8.0.0",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@radix-ui/react-accordion": "^1.2.0",
|
||||
@@ -22,24 +26,24 @@
|
||||
"@radix-ui/react-checkbox": "^1.1.1",
|
||||
"@radix-ui/react-collapsible": "^1.1.0",
|
||||
"@radix-ui/react-context-menu": "^2.2.1",
|
||||
"@radix-ui/react-dialog": "^1.1.6",
|
||||
"@radix-ui/react-dialog": "^1.1.14",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"@radix-ui/react-hover-card": "^1.1.1",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-menubar": "^1.1.1",
|
||||
"@radix-ui/react-popover": "^1.1.1",
|
||||
"@radix-ui/react-scroll-area": "1.2.0",
|
||||
"@radix-ui/react-select": "^2.1.1",
|
||||
"@radix-ui/react-separator": "^1.1.2",
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-toast": "^1.2.1",
|
||||
"@radix-ui/react-toggle": "^1.1.0",
|
||||
"@radix-ui/react-toggle-group": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@radix-ui/react-tooltip": "^1.2.7",
|
||||
"@uidotdev/usehooks": "^2.4.1",
|
||||
"@xyflow/react": "^12.3.1",
|
||||
"@xyflow/react": "^12.8.2",
|
||||
"ahooks": "^3.8.1",
|
||||
"ai": "^3.3.14",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
@@ -50,8 +54,9 @@
|
||||
"html-to-image": "^1.11.11",
|
||||
"i18next": "^23.14.0",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"lucide-react": "^0.441.0",
|
||||
"lucide-react": "^0.525.0",
|
||||
"monaco-editor": "^0.52.0",
|
||||
"motion": "^12.23.6",
|
||||
"nanoid": "^5.0.7",
|
||||
"node-sql-parser": "^5.3.2",
|
||||
"react": "^18.3.1",
|
||||
@@ -73,12 +78,16 @@
|
||||
"@eslint/compat": "^1.2.4",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.16.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/node": "^22.1.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
||||
"@typescript-eslint/parser": "^8.18.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.16.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
@@ -90,6 +99,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"eslint-plugin-tailwindcss": "^3.17.4",
|
||||
"globals": "^15.13.0",
|
||||
"happy-dom": "^18.0.1",
|
||||
"husky": "^9.1.5",
|
||||
"postcss": "^8.4.40",
|
||||
"prettier": "^3.3.3",
|
||||
@@ -97,6 +107,7 @@
|
||||
"tailwindcss": "^3.4.7",
|
||||
"typescript": "^5.2.2",
|
||||
"unplugin-inject-preload": "^3.0.0",
|
||||
"vite": "^5.3.4"
|
||||
"vite": "^5.3.4",
|
||||
"vitest": "^3.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /
|
||||
|
||||
Sitemap: https://app.chartdb.io/sitemap.xml
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
export const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
137
src/components/button/button-with-alternatives.tsx
Normal file
137
src/components/button/button-with-alternatives.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import React from 'react';
|
||||
import { ChevronDownIcon } from '@radix-ui/react-icons';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { type VariantProps } from 'class-variance-authority';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { buttonVariants } from './button-variants';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/dropdown-menu/dropdown-menu';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/tooltip/tooltip';
|
||||
|
||||
export interface ButtonAlternative {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
disabled?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
className?: string;
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
export interface ButtonWithAlternativesProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
alternatives: Array<ButtonAlternative>;
|
||||
dropdownTriggerClassName?: string;
|
||||
chevronDownIconClassName?: string;
|
||||
}
|
||||
|
||||
const ButtonWithAlternatives = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
ButtonWithAlternativesProps
|
||||
>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
asChild = false,
|
||||
alternatives,
|
||||
children,
|
||||
onClick,
|
||||
dropdownTriggerClassName,
|
||||
chevronDownIconClassName,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
const hasAlternatives = (alternatives?.length ?? 0) > 0;
|
||||
|
||||
return (
|
||||
<div className="inline-flex items-stretch">
|
||||
<Comp
|
||||
className={cn(
|
||||
buttonVariants({ variant, size }),
|
||||
{ 'rounded-r-none': hasAlternatives },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
onClick={onClick}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Comp>
|
||||
{hasAlternatives ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
className={cn(
|
||||
buttonVariants({ variant, size }),
|
||||
'rounded-l-none border-l border-l-primary/5 px-2 min-w-0',
|
||||
className?.includes('h-') &&
|
||||
className.match(/h-\d+/)?.[0],
|
||||
className?.includes('text-') &&
|
||||
className.match(/text-\w+/)?.[0],
|
||||
dropdownTriggerClassName
|
||||
)}
|
||||
type="button"
|
||||
>
|
||||
<ChevronDownIcon
|
||||
className={cn(
|
||||
'size-4 shrink-0',
|
||||
chevronDownIconClassName
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{alternatives.map((alternative, index) => {
|
||||
const menuItem = (
|
||||
<DropdownMenuItem
|
||||
key={index}
|
||||
onClick={alternative.onClick}
|
||||
disabled={alternative.disabled}
|
||||
className={cn(alternative.className)}
|
||||
>
|
||||
<span className="flex w-full items-center justify-between gap-2">
|
||||
{alternative.label}
|
||||
{alternative.icon}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
|
||||
if (alternative.tooltip) {
|
||||
return (
|
||||
<Tooltip key={index}>
|
||||
<TooltipTrigger asChild>
|
||||
{menuItem}
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="left">
|
||||
{alternative.tooltip}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return menuItem;
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
ButtonWithAlternatives.displayName = 'ButtonWithAlternatives';
|
||||
|
||||
export { ButtonWithAlternatives };
|
||||
@@ -31,6 +31,7 @@ export interface CodeSnippetAction {
|
||||
label: string;
|
||||
icon: LucideIcon;
|
||||
onClick: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface CodeSnippetProps {
|
||||
@@ -43,6 +44,8 @@ export interface CodeSnippetProps {
|
||||
isComplete?: boolean;
|
||||
editorProps?: React.ComponentProps<EditorType>;
|
||||
actions?: CodeSnippetAction[];
|
||||
actionsTooltipSide?: 'top' | 'right' | 'bottom' | 'left';
|
||||
allowCopy?: boolean;
|
||||
}
|
||||
|
||||
export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
|
||||
@@ -56,6 +59,8 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
|
||||
isComplete = true,
|
||||
editorProps,
|
||||
actions,
|
||||
actionsTooltipSide,
|
||||
allowCopy = true,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const monaco = useMonaco();
|
||||
@@ -129,33 +134,37 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
|
||||
<Suspense fallback={<Spinner />}>
|
||||
{isComplete ? (
|
||||
<div className="absolute right-1 top-1 z-10 flex flex-col gap-1">
|
||||
<Tooltip
|
||||
onOpenChange={setTooltipOpen}
|
||||
open={isCopied || tooltipOpen}
|
||||
>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
className="h-fit p-1.5"
|
||||
variant="outline"
|
||||
onClick={copyToClipboard}
|
||||
>
|
||||
{isCopied ? (
|
||||
<CopyCheck size={16} />
|
||||
) : (
|
||||
<Copy size={16} />
|
||||
)}
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{t(
|
||||
isCopied
|
||||
? 'copied'
|
||||
: 'copy_to_clipboard'
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
{allowCopy ? (
|
||||
<Tooltip
|
||||
onOpenChange={setTooltipOpen}
|
||||
open={isCopied || tooltipOpen}
|
||||
>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
className="h-fit p-1.5"
|
||||
variant="outline"
|
||||
onClick={copyToClipboard}
|
||||
>
|
||||
{isCopied ? (
|
||||
<CopyCheck size={16} />
|
||||
) : (
|
||||
<Copy size={16} />
|
||||
)}
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side={actionsTooltipSide}
|
||||
>
|
||||
{t(
|
||||
isCopied
|
||||
? 'copied'
|
||||
: 'copy_to_clipboard'
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
|
||||
{actions &&
|
||||
actions.length > 0 &&
|
||||
@@ -164,7 +173,10 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
className="h-fit p-1.5"
|
||||
className={cn(
|
||||
'h-fit p-1.5',
|
||||
action.className
|
||||
)}
|
||||
variant="outline"
|
||||
onClick={action.onClick}
|
||||
>
|
||||
@@ -174,7 +186,9 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = React.memo(
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<TooltipContent
|
||||
side={actionsTooltipSide}
|
||||
>
|
||||
{action.label}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
51
src/components/code-snippet/dbml/utils.ts
Normal file
51
src/components/code-snippet/dbml/utils.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { DBMLError } from '@/lib/dbml/dbml-import/dbml-import-error';
|
||||
import * as monaco from 'monaco-editor';
|
||||
|
||||
export const highlightErrorLine = ({
|
||||
error,
|
||||
model,
|
||||
editorDecorationsCollection,
|
||||
}: {
|
||||
error: DBMLError;
|
||||
model?: monaco.editor.ITextModel | null;
|
||||
editorDecorationsCollection:
|
||||
| monaco.editor.IEditorDecorationsCollection
|
||||
| undefined;
|
||||
}) => {
|
||||
if (!model) return;
|
||||
if (!editorDecorationsCollection) return;
|
||||
|
||||
const decorations = [
|
||||
{
|
||||
range: new monaco.Range(
|
||||
error.line,
|
||||
1,
|
||||
error.line,
|
||||
model.getLineMaxColumn(error.line)
|
||||
),
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
className: 'dbml-error-line',
|
||||
glyphMarginClassName: 'dbml-error-glyph',
|
||||
hoverMessage: { value: error.message },
|
||||
overviewRuler: {
|
||||
color: '#ff0000',
|
||||
position: monaco.editor.OverviewRulerLane.Right,
|
||||
darkColor: '#ff0000',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
editorDecorationsCollection?.set(decorations);
|
||||
};
|
||||
|
||||
export const clearErrorHighlight = (
|
||||
editorDecorationsCollection:
|
||||
| monaco.editor.IEditorDecorationsCollection
|
||||
| undefined
|
||||
) => {
|
||||
if (editorDecorationsCollection) {
|
||||
editorDecorationsCollection.clear();
|
||||
}
|
||||
};
|
||||
@@ -37,18 +37,28 @@ export const setupDBMLLanguage = (monaco: Monaco) => {
|
||||
const datatypePattern = dataTypesNames.join('|');
|
||||
|
||||
monaco.languages.setMonarchTokensProvider('dbml', {
|
||||
keywords: ['Table', 'Ref', 'Indexes'],
|
||||
keywords: ['Table', 'Ref', 'Indexes', 'Note', 'Enum'],
|
||||
datatypes: dataTypesNames,
|
||||
tokenizer: {
|
||||
root: [
|
||||
[/\b(Table|Ref|Indexes)\b/, 'keyword'],
|
||||
[
|
||||
/\b([Tt][Aa][Bb][Ll][Ee]|[Ee][Nn][Uu][Mm]|[Rr][Ee][Ff]|[Ii][Nn][Dd][Ee][Xx][Ee][Ss]|[Nn][Oo][Tt][Ee])\b/,
|
||||
'keyword',
|
||||
],
|
||||
[/\[.*?\]/, 'annotation'],
|
||||
[/'''/, 'string', '@tripleQuoteString'],
|
||||
[/".*?"/, 'string'],
|
||||
[/'.*?'/, 'string'],
|
||||
[/`.*?`/, 'string'],
|
||||
[/[{}]/, 'delimiter'],
|
||||
[/[<>]/, 'operator'],
|
||||
[new RegExp(`\\b(${datatypePattern})\\b`, 'i'), 'type'], // Added 'i' flag for case-insensitive matching
|
||||
],
|
||||
tripleQuoteString: [
|
||||
[/[^']+/, 'string'],
|
||||
[/'''/, 'string', '@pop'],
|
||||
[/'/, 'string'],
|
||||
],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,21 +5,33 @@ import {
|
||||
PopoverTrigger,
|
||||
} from '@/components/popover/popover';
|
||||
import { colorOptions } from '@/lib/colors';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface ColorPickerProps {
|
||||
color: string;
|
||||
onChange: (color: string) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const ColorPicker = React.forwardRef<
|
||||
React.ElementRef<typeof PopoverTrigger>,
|
||||
ColorPickerProps
|
||||
>(({ color, onChange }, ref) => {
|
||||
>(({ color, onChange, disabled }, ref) => {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild ref={ref}>
|
||||
<PopoverTrigger
|
||||
asChild
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
{...(disabled ? { onClick: (e) => e.preventDefault() } : {})}
|
||||
>
|
||||
<div
|
||||
className="h-6 w-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md"
|
||||
className={cn(
|
||||
'h-6 w-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md',
|
||||
{
|
||||
'hover:shadow-none cursor-default': disabled,
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
}}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Cross2Icon } from '@radix-ui/react-icons';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ScrollArea } from '../scroll-area/scroll-area';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
@@ -32,28 +33,75 @@ const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
||||
showClose?: boolean;
|
||||
showBack?: boolean;
|
||||
backButtonClassName?: string;
|
||||
blurBackground?: boolean;
|
||||
forceOverlay?: boolean;
|
||||
onBackClick?: () => void;
|
||||
}
|
||||
>(({ className, children, showClose, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{showClose && (
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<Cross2Icon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
));
|
||||
>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
children,
|
||||
showClose,
|
||||
showBack,
|
||||
onBackClick,
|
||||
backButtonClassName,
|
||||
blurBackground,
|
||||
forceOverlay,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => (
|
||||
<DialogPortal>
|
||||
{forceOverlay ? (
|
||||
<div
|
||||
className={cn(
|
||||
'fixed inset-0 z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||
{
|
||||
'bg-black/80': !blurBackground,
|
||||
'bg-black/30 backdrop-blur-sm': blurBackground,
|
||||
}
|
||||
)}
|
||||
data-state="open"
|
||||
/>
|
||||
) : null}
|
||||
<DialogOverlay
|
||||
className={cn({
|
||||
'bg-black/30 backdrop-blur-sm': blurBackground,
|
||||
})}
|
||||
/>
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{showBack && (
|
||||
<button
|
||||
onClick={() => onBackClick?.()}
|
||||
className={cn(
|
||||
'absolute left-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground',
|
||||
backButtonClassName
|
||||
)}
|
||||
>
|
||||
<ChevronLeft className="size-4" />
|
||||
</button>
|
||||
)}
|
||||
{showClose && (
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<Cross2Icon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
);
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||
|
||||
const DialogHeader = ({
|
||||
|
||||
@@ -52,7 +52,7 @@ export const EmptyState = forwardRef<
|
||||
</Label>
|
||||
<Label
|
||||
className={cn(
|
||||
'text-sm font-normal text-muted-foreground',
|
||||
'text-sm text-center font-normal text-muted-foreground',
|
||||
descriptionClassName
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -2,16 +2,13 @@ import React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
121
src/components/pagination/pagination.tsx
Normal file
121
src/components/pagination/pagination.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { ButtonProps } from '../button/button';
|
||||
import { buttonVariants } from '../button/button-variants';
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
DotsHorizontalIcon,
|
||||
} from '@radix-ui/react-icons';
|
||||
|
||||
const Pagination = ({ className, ...props }: React.ComponentProps<'nav'>) => (
|
||||
<nav
|
||||
role="navigation"
|
||||
aria-label="pagination"
|
||||
className={cn('mx-auto flex w-full justify-center', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
Pagination.displayName = 'Pagination';
|
||||
|
||||
const PaginationContent = React.forwardRef<
|
||||
HTMLUListElement,
|
||||
React.ComponentProps<'ul'>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ul
|
||||
ref={ref}
|
||||
className={cn('flex flex-row items-center gap-1', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
PaginationContent.displayName = 'PaginationContent';
|
||||
|
||||
const PaginationItem = React.forwardRef<
|
||||
HTMLLIElement,
|
||||
React.ComponentProps<'li'>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<li ref={ref} className={cn('', className)} {...props} />
|
||||
));
|
||||
PaginationItem.displayName = 'PaginationItem';
|
||||
|
||||
type PaginationLinkProps = {
|
||||
isActive?: boolean;
|
||||
} & Pick<ButtonProps, 'size'> &
|
||||
React.ComponentProps<'a'>;
|
||||
|
||||
const PaginationLink = ({
|
||||
className,
|
||||
isActive,
|
||||
size = 'icon',
|
||||
...props
|
||||
}: PaginationLinkProps) => (
|
||||
<a
|
||||
aria-current={isActive ? 'page' : undefined}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: isActive ? 'outline' : 'ghost',
|
||||
size,
|
||||
}),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
PaginationLink.displayName = 'PaginationLink';
|
||||
|
||||
const PaginationPrevious = ({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof PaginationLink>) => (
|
||||
<PaginationLink
|
||||
aria-label="Go to previous page"
|
||||
size="default"
|
||||
className={cn('gap-1 pl-2.5', className)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronLeftIcon className="size-4" />
|
||||
<span>Previous</span>
|
||||
</PaginationLink>
|
||||
);
|
||||
PaginationPrevious.displayName = 'PaginationPrevious';
|
||||
|
||||
const PaginationNext = ({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof PaginationLink>) => (
|
||||
<PaginationLink
|
||||
aria-label="Go to next page"
|
||||
size="default"
|
||||
className={cn('gap-1 pr-2.5', className)}
|
||||
{...props}
|
||||
>
|
||||
<span>Next</span>
|
||||
<ChevronRightIcon className="size-4" />
|
||||
</PaginationLink>
|
||||
);
|
||||
PaginationNext.displayName = 'PaginationNext';
|
||||
|
||||
const PaginationEllipsis = ({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<'span'>) => (
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn('flex h-9 w-9 items-center justify-center', className)}
|
||||
{...props}
|
||||
>
|
||||
<DotsHorizontalIcon className="size-4" />
|
||||
<span className="sr-only">More pages</span>
|
||||
</span>
|
||||
);
|
||||
PaginationEllipsis.displayName = 'PaginationEllipsis';
|
||||
|
||||
export {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationLink,
|
||||
PaginationItem,
|
||||
PaginationPrevious,
|
||||
PaginationNext,
|
||||
PaginationEllipsis,
|
||||
};
|
||||
@@ -27,6 +27,7 @@ export interface SelectBoxOption {
|
||||
regex?: string;
|
||||
extractRegex?: RegExp;
|
||||
group?: string;
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface SelectBoxProps {
|
||||
@@ -53,6 +54,8 @@ export interface SelectBoxProps {
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
popoverClassName?: string;
|
||||
readonly?: boolean;
|
||||
footerButtons?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||
@@ -78,6 +81,8 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||
open,
|
||||
onOpenChange: setOpen,
|
||||
popoverClassName,
|
||||
readonly,
|
||||
footerButtons,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
@@ -93,6 +98,12 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||
(isOpen: boolean) => {
|
||||
setOpen?.(isOpen);
|
||||
setIsOpen(isOpen);
|
||||
|
||||
if (isOpen) {
|
||||
setSearchTerm('');
|
||||
}
|
||||
|
||||
setTimeout(() => (document.body.style.pointerEvents = ''), 500);
|
||||
},
|
||||
[setOpen]
|
||||
);
|
||||
@@ -146,18 +157,20 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||
className={`inline-flex min-w-0 shrink-0 items-center gap-1 rounded-md border py-0.5 pl-2 pr-1 text-xs font-medium text-foreground transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 ${oneLine ? 'mx-0.5' : ''}`}
|
||||
>
|
||||
<span>{option.label}</span>
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleSelect(option.value);
|
||||
}}
|
||||
className="flex items-center rounded-sm px-px text-muted-foreground/60 hover:bg-accent hover:text-muted-foreground"
|
||||
>
|
||||
<Cross2Icon />
|
||||
</span>
|
||||
{!readonly ? (
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleSelect(option.value);
|
||||
}}
|
||||
className="flex items-center rounded-sm px-px text-muted-foreground/60 hover:bg-accent hover:text-muted-foreground"
|
||||
>
|
||||
<Cross2Icon />
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
)),
|
||||
[options, value, handleSelect, oneLine, keepOrder]
|
||||
[options, value, handleSelect, oneLine, keepOrder, readonly]
|
||||
);
|
||||
|
||||
const isAllSelected = React.useMemo(
|
||||
@@ -227,7 +240,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||
onSelect={() =>
|
||||
handleSelect(
|
||||
option.value,
|
||||
matches?.map((match) => match.toString())
|
||||
matches?.map((match) => match?.toString())
|
||||
)
|
||||
}
|
||||
>
|
||||
@@ -244,6 +257,11 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-1 items-center truncate">
|
||||
{option.icon ? (
|
||||
<span className="mr-2 shrink-0">
|
||||
{option.icon}
|
||||
</span>
|
||||
) : null}
|
||||
<span>
|
||||
{isRegexMatch ? searchTerm : option.label}
|
||||
{!isRegexMatch && optionSuffix
|
||||
@@ -278,7 +296,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||
<PopoverTrigger asChild tabIndex={0} onKeyDown={handleKeyDown}>
|
||||
<div
|
||||
className={cn(
|
||||
`flex min-h-[36px] cursor-pointer items-center justify-between rounded-md border px-3 py-1 data-[state=open]:border-ring ${disabled ? 'bg-muted pointer-events-none' : ''}`,
|
||||
`flex min-h-[36px] cursor-pointer items-center justify-between rounded-md border px-3 py-1 data-[state=open]:border-ring ${disabled ? 'bg-muted pointer-events-none' : ''} ${readonly ? 'pointer-events-none' : ''}`,
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -418,30 +436,28 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||
|
||||
<ScrollArea>
|
||||
<div className="max-h-64 w-full">
|
||||
<CommandGroup>
|
||||
<CommandList className="max-h-fit w-full">
|
||||
{hasGroups
|
||||
? Object.entries(groups).map(
|
||||
([
|
||||
groupName,
|
||||
groupOptions,
|
||||
]) => (
|
||||
<CommandGroup
|
||||
key={groupName}
|
||||
heading={groupName}
|
||||
>
|
||||
{groupOptions.map(
|
||||
renderOption
|
||||
)}
|
||||
</CommandGroup>
|
||||
)
|
||||
<CommandList className="max-h-fit w-full">
|
||||
{hasGroups
|
||||
? Object.entries(groups).map(
|
||||
([groupName, groupOptions]) => (
|
||||
<CommandGroup
|
||||
key={groupName}
|
||||
heading={groupName}
|
||||
>
|
||||
{groupOptions.map(
|
||||
renderOption
|
||||
)}
|
||||
</CommandGroup>
|
||||
)
|
||||
: options.map(renderOption)}
|
||||
</CommandList>
|
||||
</CommandGroup>
|
||||
)
|
||||
: options.map(renderOption)}
|
||||
</CommandList>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</Command>
|
||||
{footerButtons ? (
|
||||
<div className="border-t">{footerButtons}</div>
|
||||
) : null}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
@@ -29,6 +29,7 @@ const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
||||
const SIDEBAR_WIDTH = '16rem';
|
||||
const SIDEBAR_WIDTH_MOBILE = '18rem';
|
||||
const SIDEBAR_WIDTH_ICON = '3rem';
|
||||
const SIDEBAR_WIDTH_ICON_EXTENDED = '4rem';
|
||||
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
||||
|
||||
type SidebarContext = {
|
||||
@@ -142,6 +143,8 @@ const SidebarProvider = React.forwardRef<
|
||||
{
|
||||
'--sidebar-width': SIDEBAR_WIDTH,
|
||||
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
|
||||
'--sidebar-width-icon-extended':
|
||||
SIDEBAR_WIDTH_ICON_EXTENDED,
|
||||
...style,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
@@ -166,7 +169,7 @@ const Sidebar = React.forwardRef<
|
||||
React.ComponentProps<'div'> & {
|
||||
side?: 'left' | 'right';
|
||||
variant?: 'sidebar' | 'floating' | 'inset';
|
||||
collapsible?: 'offcanvas' | 'icon' | 'none';
|
||||
collapsible?: 'offcanvas' | 'icon' | 'icon-extended' | 'none';
|
||||
}
|
||||
>(
|
||||
(
|
||||
@@ -245,8 +248,8 @@ const Sidebar = React.forwardRef<
|
||||
'group-data-[collapsible=offcanvas]:w-0',
|
||||
'group-data-[side=right]:rotate-180',
|
||||
variant === 'floating' || variant === 'inset'
|
||||
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]'
|
||||
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon]'
|
||||
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))] group-data-[collapsible=icon-extended]:w-[calc(var(--sidebar-width-icon-extended)_+_theme(spacing.4))]'
|
||||
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[collapsible=icon-extended]:w-[--sidebar-width-icon-extended]'
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
@@ -257,8 +260,8 @@ const Sidebar = React.forwardRef<
|
||||
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
|
||||
// Adjust the padding for floating and inset variants.
|
||||
variant === 'floating' || variant === 'inset'
|
||||
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]'
|
||||
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l',
|
||||
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)] group-data-[collapsible=icon-extended]:w-[calc(var(--sidebar-width-icon-extended)_+_theme(spacing.4)_+2px)]'
|
||||
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[collapsible=icon-extended]:w-[--sidebar-width-icon-extended] group-data-[side=left]:border-r group-data-[side=right]:border-l',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -421,7 +424,7 @@ const SidebarContent = React.forwardRef<
|
||||
ref={ref}
|
||||
data-sidebar="content"
|
||||
className={cn(
|
||||
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
|
||||
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden group-data-[collapsible=icon-extended]:overflow-hidden',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -461,6 +464,7 @@ const SidebarGroupLabel = React.forwardRef<
|
||||
className={cn(
|
||||
'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
|
||||
'group-data-[collapsible=icon-extended]:-mt-8 group-data-[collapsible=icon-extended]:opacity-0',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -483,7 +487,7 @@ const SidebarGroupAction = React.forwardRef<
|
||||
'absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
// Increases the hit area of the button on mobile.
|
||||
'after:absolute after:-inset-2 after:md:hidden',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
'group-data-[collapsible=icon]:hidden group-data-[collapsible=icon-extended]:hidden',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -532,7 +536,7 @@ const SidebarMenuItem = React.forwardRef<
|
||||
SidebarMenuItem.displayName = 'SidebarMenuItem';
|
||||
|
||||
const sidebarMenuButtonVariants = cva(
|
||||
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon-extended]:h-auto group-data-[collapsible=icon-extended]:flex-col group-data-[collapsible=icon-extended]:gap-1 group-data-[collapsible=icon-extended]:p-2 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate group-data-[collapsible=icon-extended]:[&>span]:w-full group-data-[collapsible=icon-extended]:[&>span]:text-center group-data-[collapsible=icon-extended]:[&>span]:text-[10px] group-data-[collapsible=icon-extended]:[&>span]:leading-tight [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -636,7 +640,7 @@ const SidebarMenuAction = React.forwardRef<
|
||||
'peer-data-[size=sm]/menu-button:top-1',
|
||||
'peer-data-[size=default]/menu-button:top-1.5',
|
||||
'peer-data-[size=lg]/menu-button:top-2.5',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
'group-data-[collapsible=icon]:hidden group-data-[collapsible=icon-extended]:hidden',
|
||||
showOnHover &&
|
||||
'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0',
|
||||
className
|
||||
@@ -753,7 +757,7 @@ const SidebarMenuSubButton = React.forwardRef<
|
||||
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
|
||||
size === 'sm' && 'text-xs',
|
||||
size === 'md' && 'text-sm',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
'group-data-[collapsible=icon]:hidden group-data-[collapsible=icon-extended]:hidden',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -20,6 +20,7 @@ export function Toaster() {
|
||||
description,
|
||||
action,
|
||||
layout = 'row',
|
||||
hideCloseButton = false,
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
@@ -38,7 +39,7 @@ export function Toaster() {
|
||||
) : null}
|
||||
</div>
|
||||
{layout === 'row' ? action : null}
|
||||
<ToastClose />
|
||||
{!hideCloseButton ? <ToastClose /> : null}
|
||||
</Toast>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -12,6 +12,7 @@ type ToasterToast = ToastProps & {
|
||||
description?: React.ReactNode;
|
||||
action?: ToastActionElement;
|
||||
layout?: 'row' | 'column';
|
||||
hideCloseButton?: boolean;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
@@ -13,15 +13,17 @@ const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
// <TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
// </TooltipPrimitive.Portal>
|
||||
));
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
|
||||
17
src/components/tree-view/tree-item-skeleton.tsx
Normal file
17
src/components/tree-view/tree-item-skeleton.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { Skeleton } from '../skeleton/skeleton';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface TreeItemSkeletonProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const TreeItemSkeleton: React.FC<TreeItemSkeletonProps> = ({
|
||||
className,
|
||||
style,
|
||||
}) => {
|
||||
return (
|
||||
<div className={cn('px-2 py-1', className)} style={style}>
|
||||
<Skeleton className="h-3.5 w-full rounded-sm" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
461
src/components/tree-view/tree-view.tsx
Normal file
461
src/components/tree-view/tree-view.tsx
Normal file
@@ -0,0 +1,461 @@
|
||||
import {
|
||||
ChevronRight,
|
||||
File,
|
||||
Folder,
|
||||
Loader2,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Button } from '@/components/button/button';
|
||||
import type {
|
||||
TreeNode,
|
||||
FetchChildrenFunction,
|
||||
SelectableTreeProps,
|
||||
} from './tree';
|
||||
import type { ExpandedState } from './use-tree';
|
||||
import { useTree } from './use-tree';
|
||||
import type { Dispatch, ReactNode, SetStateAction } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { TreeItemSkeleton } from './tree-item-skeleton';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/tooltip/tooltip';
|
||||
|
||||
interface TreeViewProps<
|
||||
Type extends string,
|
||||
Context extends Record<Type, unknown>,
|
||||
> {
|
||||
data: TreeNode<Type, Context>[];
|
||||
fetchChildren?: FetchChildrenFunction<Type, Context>;
|
||||
onNodeClick?: (node: TreeNode<Type, Context>) => void;
|
||||
className?: string;
|
||||
defaultIcon?: LucideIcon;
|
||||
defaultFolderIcon?: LucideIcon;
|
||||
defaultIconProps?: React.ComponentProps<LucideIcon>;
|
||||
defaultFolderIconProps?: React.ComponentProps<LucideIcon>;
|
||||
selectable?: SelectableTreeProps<Type, Context>;
|
||||
expanded?: ExpandedState;
|
||||
setExpanded?: Dispatch<SetStateAction<ExpandedState>>;
|
||||
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||
loadingNodeIds?: string[];
|
||||
}
|
||||
|
||||
export function TreeView<
|
||||
Type extends string,
|
||||
Context extends Record<Type, unknown>,
|
||||
>({
|
||||
data,
|
||||
fetchChildren,
|
||||
onNodeClick,
|
||||
className,
|
||||
defaultIcon = File,
|
||||
defaultFolderIcon = Folder,
|
||||
defaultIconProps,
|
||||
defaultFolderIconProps,
|
||||
selectable,
|
||||
expanded: expandedProp,
|
||||
setExpanded: setExpandedProp,
|
||||
renderHoverComponent,
|
||||
renderActionsComponent,
|
||||
loadingNodeIds,
|
||||
}: TreeViewProps<Type, Context>) {
|
||||
const { expanded, loading, loadedChildren, hasMoreChildren, toggleNode } =
|
||||
useTree({
|
||||
fetchChildren,
|
||||
expanded: expandedProp,
|
||||
setExpanded: setExpandedProp,
|
||||
});
|
||||
const [selectedIdInternal, setSelectedIdInternal] = React.useState<
|
||||
string | undefined
|
||||
>(selectable?.defaultSelectedId);
|
||||
|
||||
const selectedId = useMemo(() => {
|
||||
return selectable?.selectedId ?? selectedIdInternal;
|
||||
}, [selectable?.selectedId, selectedIdInternal]);
|
||||
|
||||
const setSelectedId = useCallback(
|
||||
(value: SetStateAction<string | undefined>) => {
|
||||
if (selectable?.setSelectedId) {
|
||||
selectable.setSelectedId(value);
|
||||
} else {
|
||||
setSelectedIdInternal(value);
|
||||
}
|
||||
},
|
||||
[selectable, setSelectedIdInternal]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectable?.enabled && selectable.defaultSelectedId) {
|
||||
if (selectable.defaultSelectedId === selectedId) return;
|
||||
setSelectedId(selectable.defaultSelectedId);
|
||||
const { node, path } = findNodeById(
|
||||
data,
|
||||
selectable.defaultSelectedId
|
||||
);
|
||||
|
||||
if (node) {
|
||||
selectable.onSelectedChange?.(node);
|
||||
|
||||
// Expand all parent nodes
|
||||
for (const parent of path) {
|
||||
if (expanded[parent.id]) continue;
|
||||
toggleNode(
|
||||
parent.id,
|
||||
parent.type,
|
||||
parent.context,
|
||||
parent.children
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [selectable, toggleNode, selectedId, data, expanded, setSelectedId]);
|
||||
|
||||
const handleNodeSelect = (node: TreeNode<Type, Context>) => {
|
||||
if (selectable?.enabled) {
|
||||
setSelectedId(node.id);
|
||||
selectable.onSelectedChange?.(node);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('w-full', className)}>
|
||||
{data.map((node, index) => (
|
||||
<TreeNode
|
||||
key={node.id}
|
||||
node={node}
|
||||
level={0}
|
||||
expanded={expanded}
|
||||
loading={loading}
|
||||
loadedChildren={loadedChildren}
|
||||
hasMoreChildren={hasMoreChildren}
|
||||
onToggle={toggleNode}
|
||||
onNodeClick={onNodeClick}
|
||||
defaultIcon={defaultIcon}
|
||||
defaultFolderIcon={defaultFolderIcon}
|
||||
defaultIconProps={defaultIconProps}
|
||||
defaultFolderIconProps={defaultFolderIconProps}
|
||||
selectable={selectable?.enabled}
|
||||
selectedId={selectedId}
|
||||
onSelect={handleNodeSelect}
|
||||
className={index > 0 ? 'mt-0.5' : ''}
|
||||
renderHoverComponent={renderHoverComponent}
|
||||
renderActionsComponent={renderActionsComponent}
|
||||
loadingNodeIds={loadingNodeIds}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface TreeNodeProps<
|
||||
Type extends string,
|
||||
Context extends Record<Type, unknown>,
|
||||
> {
|
||||
node: TreeNode<Type, Context>;
|
||||
level: number;
|
||||
expanded: Record<string, boolean>;
|
||||
loading: Record<string, boolean>;
|
||||
loadedChildren: Record<string, TreeNode<Type, Context>[]>;
|
||||
hasMoreChildren: Record<string, boolean>;
|
||||
onToggle: (
|
||||
nodeId: string,
|
||||
nodeType: Type,
|
||||
nodeContext: Context[Type],
|
||||
staticChildren?: TreeNode<Type, Context>[]
|
||||
) => void;
|
||||
onNodeClick?: (node: TreeNode<Type, Context>) => void;
|
||||
defaultIcon: LucideIcon;
|
||||
defaultFolderIcon: LucideIcon;
|
||||
defaultIconProps?: React.ComponentProps<LucideIcon>;
|
||||
defaultFolderIconProps?: React.ComponentProps<LucideIcon>;
|
||||
selectable?: boolean;
|
||||
selectedId?: string;
|
||||
onSelect: (node: TreeNode<Type, Context>) => void;
|
||||
className?: string;
|
||||
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||
loadingNodeIds?: string[];
|
||||
}
|
||||
|
||||
function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
||||
node,
|
||||
level,
|
||||
expanded,
|
||||
loading,
|
||||
loadedChildren,
|
||||
hasMoreChildren,
|
||||
onToggle,
|
||||
onNodeClick,
|
||||
defaultIcon: DefaultIcon,
|
||||
defaultFolderIcon: DefaultFolderIcon,
|
||||
defaultIconProps,
|
||||
defaultFolderIconProps,
|
||||
selectable,
|
||||
selectedId,
|
||||
onSelect,
|
||||
className,
|
||||
renderHoverComponent,
|
||||
renderActionsComponent,
|
||||
loadingNodeIds,
|
||||
}: TreeNodeProps<Type, Context>) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const isExpanded = expanded[node.id];
|
||||
const isLoading = loading[node.id];
|
||||
const children = loadedChildren[node.id] || node.children;
|
||||
const isSelected = selectedId === node.id;
|
||||
|
||||
const IconComponent =
|
||||
node.icon || (node.isFolder ? DefaultFolderIcon : DefaultIcon);
|
||||
const iconProps: React.ComponentProps<LucideIcon> = {
|
||||
strokeWidth: isSelected ? 2.5 : 2,
|
||||
...(node.isFolder ? defaultFolderIconProps : defaultIconProps),
|
||||
...node.iconProps,
|
||||
className: cn(
|
||||
'h-3.5 w-3.5 text-muted-foreground flex-none',
|
||||
isSelected && 'text-primary text-white',
|
||||
node.iconProps?.className
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn(className)}>
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 px-2 py-1 rounded-lg cursor-pointer group h-6',
|
||||
'transition-colors duration-200',
|
||||
isSelected
|
||||
? 'bg-sky-500 border border-sky-600 border dark:bg-sky-600 dark:border-sky-700'
|
||||
: 'hover:bg-gray-200/50 border border-transparent dark:hover:bg-gray-700/50',
|
||||
node.className
|
||||
)}
|
||||
{...(isSelected ? { 'data-selected': true } : {})}
|
||||
style={{ paddingLeft: `${level * 16 + 8}px` }}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (selectable && !node.unselectable) {
|
||||
onSelect(node);
|
||||
}
|
||||
// if (node.isFolder) {
|
||||
// onToggle(node.id, node.children);
|
||||
// }
|
||||
|
||||
// called only once in case of double click
|
||||
if (e.detail !== 2) {
|
||||
onNodeClick?.(node);
|
||||
}
|
||||
}}
|
||||
onDoubleClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (node.isFolder) {
|
||||
onToggle(
|
||||
node.id,
|
||||
node.type,
|
||||
node.context,
|
||||
node.children
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-none items-center gap-1.5">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={cn(
|
||||
'h-3.5 w-3.5 p-0 hover:bg-transparent flex-none',
|
||||
isExpanded && 'rotate-90',
|
||||
'transition-transform duration-200'
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (node.isFolder) {
|
||||
onToggle(
|
||||
node.id,
|
||||
node.type,
|
||||
node.context,
|
||||
node.children
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{node.isFolder &&
|
||||
(isLoading ? (
|
||||
<Loader2
|
||||
className={cn('size-3.5 animate-spin', {
|
||||
'text-white': isSelected,
|
||||
})}
|
||||
/>
|
||||
) : (
|
||||
<ChevronRight
|
||||
className={cn('size-3.5', {
|
||||
'text-white': isSelected,
|
||||
})}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
))}
|
||||
</Button>
|
||||
|
||||
{node.tooltip ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
{loadingNodeIds?.includes(node.id) ? (
|
||||
<Loader2
|
||||
className={cn('size-3.5 animate-spin', {
|
||||
'text-white': isSelected,
|
||||
})}
|
||||
/>
|
||||
) : (
|
||||
<IconComponent
|
||||
{...(isSelected
|
||||
? { 'data-selected': true }
|
||||
: {})}
|
||||
{...iconProps}
|
||||
/>
|
||||
)}
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
align="center"
|
||||
className="max-w-[400px]"
|
||||
>
|
||||
{node.tooltip}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : node.empty ? null : loadingNodeIds?.includes(
|
||||
node.id
|
||||
) ? (
|
||||
<Loader2
|
||||
className={cn('size-3.5 animate-spin', {
|
||||
// 'text-white': isSelected,
|
||||
})}
|
||||
/>
|
||||
) : (
|
||||
<IconComponent
|
||||
{...(isSelected ? { 'data-selected': true } : {})}
|
||||
{...iconProps}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span
|
||||
{...node.labelProps}
|
||||
className={cn(
|
||||
'text-xs truncate min-w-0 flex-1 w-0',
|
||||
isSelected && 'font-medium text-primary text-white',
|
||||
node.labelProps?.className
|
||||
)}
|
||||
{...(isSelected ? { 'data-selected': true } : {})}
|
||||
>
|
||||
{node.empty ? '' : node.name}
|
||||
</span>
|
||||
{renderActionsComponent && renderActionsComponent(node)}
|
||||
{isHovered && renderHoverComponent
|
||||
? renderHoverComponent(node)
|
||||
: null}
|
||||
</div>
|
||||
|
||||
<AnimatePresence initial={false}>
|
||||
{isExpanded && children && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{
|
||||
height: 'auto',
|
||||
opacity: 1,
|
||||
transition: {
|
||||
height: {
|
||||
duration: Math.min(
|
||||
0.3 + children.length * 0.018,
|
||||
0.7
|
||||
),
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
opacity: {
|
||||
duration: Math.min(
|
||||
0.2 + children.length * 0.012,
|
||||
0.4
|
||||
),
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
}}
|
||||
exit={{
|
||||
height: 0,
|
||||
opacity: 0,
|
||||
transition: {
|
||||
height: {
|
||||
duration: Math.min(
|
||||
0.2 + children.length * 0.01,
|
||||
0.45
|
||||
),
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
opacity: {
|
||||
duration: 0.1,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
}}
|
||||
style={{ overflow: 'hidden' }}
|
||||
>
|
||||
{children.map((child) => (
|
||||
<TreeNode
|
||||
key={child.id}
|
||||
node={child}
|
||||
level={level + 1}
|
||||
expanded={expanded}
|
||||
loading={loading}
|
||||
loadedChildren={loadedChildren}
|
||||
hasMoreChildren={hasMoreChildren}
|
||||
onToggle={onToggle}
|
||||
onNodeClick={onNodeClick}
|
||||
defaultIcon={DefaultIcon}
|
||||
defaultFolderIcon={DefaultFolderIcon}
|
||||
defaultIconProps={defaultIconProps}
|
||||
defaultFolderIconProps={defaultFolderIconProps}
|
||||
selectable={selectable}
|
||||
selectedId={selectedId}
|
||||
onSelect={onSelect}
|
||||
className="mt-0.5"
|
||||
renderHoverComponent={renderHoverComponent}
|
||||
renderActionsComponent={renderActionsComponent}
|
||||
loadingNodeIds={loadingNodeIds}
|
||||
/>
|
||||
))}
|
||||
{isLoading ? (
|
||||
<TreeItemSkeleton
|
||||
style={{
|
||||
paddingLeft: `${level + 2 * 16 + 8}px`,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function findNodeById<
|
||||
Type extends string,
|
||||
Context extends Record<Type, unknown>,
|
||||
>(
|
||||
nodes: TreeNode<Type, Context>[],
|
||||
id: string,
|
||||
initialPath: TreeNode<Type, Context>[] = []
|
||||
): { node: TreeNode<Type, Context> | null; path: TreeNode<Type, Context>[] } {
|
||||
const path: TreeNode<Type, Context>[] = [...initialPath];
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return { node, path };
|
||||
if (node.children) {
|
||||
const found = findNodeById(node.children, id, [...path, node]);
|
||||
if (found.node) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { node: null, path };
|
||||
}
|
||||
41
src/components/tree-view/tree.ts
Normal file
41
src/components/tree-view/tree.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import type React from 'react';
|
||||
|
||||
export interface TreeNode<
|
||||
Type extends string,
|
||||
Context extends Record<Type, unknown>,
|
||||
> {
|
||||
id: string;
|
||||
name: string;
|
||||
isFolder?: boolean;
|
||||
children?: TreeNode<Type, Context>[];
|
||||
icon?: LucideIcon;
|
||||
iconProps?: React.ComponentProps<LucideIcon>;
|
||||
labelProps?: React.ComponentProps<'span'>;
|
||||
type: Type;
|
||||
unselectable?: boolean;
|
||||
tooltip?: string;
|
||||
context: Context[Type];
|
||||
empty?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export type FetchChildrenFunction<
|
||||
Type extends string,
|
||||
Context extends Record<Type, unknown>,
|
||||
> = (
|
||||
nodeId: string,
|
||||
nodeType: Type,
|
||||
nodeContext: Context[Type]
|
||||
) => Promise<TreeNode<Type, Context>[]>;
|
||||
|
||||
export interface SelectableTreeProps<
|
||||
Type extends string,
|
||||
Context extends Record<Type, unknown>,
|
||||
> {
|
||||
enabled: boolean;
|
||||
defaultSelectedId?: string;
|
||||
onSelectedChange?: (node: TreeNode<Type, Context>) => void;
|
||||
selectedId?: string;
|
||||
setSelectedId?: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
}
|
||||
153
src/components/tree-view/use-tree.ts
Normal file
153
src/components/tree-view/use-tree.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import { useState, useCallback, useMemo } from 'react';
|
||||
import type { TreeNode, FetchChildrenFunction } from './tree';
|
||||
|
||||
export interface ExpandedState {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
interface LoadingState {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
interface LoadedChildren<
|
||||
Type extends string,
|
||||
Context extends Record<Type, unknown>,
|
||||
> {
|
||||
[key: string]: TreeNode<Type, Context>[];
|
||||
}
|
||||
|
||||
interface HasMoreChildrenState {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
export function useTree<
|
||||
Type extends string,
|
||||
Context extends Record<Type, unknown>,
|
||||
>({
|
||||
fetchChildren,
|
||||
expanded: expandedProp,
|
||||
setExpanded: setExpandedProp,
|
||||
}: {
|
||||
fetchChildren?: FetchChildrenFunction<Type, Context>;
|
||||
expanded?: ExpandedState;
|
||||
setExpanded?: Dispatch<SetStateAction<ExpandedState>>;
|
||||
}) {
|
||||
const [expandedInternal, setExpandedInternal] = useState<ExpandedState>({});
|
||||
|
||||
const expanded = useMemo(
|
||||
() => expandedProp ?? expandedInternal,
|
||||
[expandedProp, expandedInternal]
|
||||
);
|
||||
const setExpanded = useCallback(
|
||||
(value: SetStateAction<ExpandedState>) => {
|
||||
if (setExpandedProp) {
|
||||
setExpandedProp(value);
|
||||
} else {
|
||||
setExpandedInternal(value);
|
||||
}
|
||||
},
|
||||
[setExpandedProp, setExpandedInternal]
|
||||
);
|
||||
|
||||
const [loading, setLoading] = useState<LoadingState>({});
|
||||
const [loadedChildren, setLoadedChildren] = useState<
|
||||
LoadedChildren<Type, Context>
|
||||
>({});
|
||||
const [hasMoreChildren, setHasMoreChildren] =
|
||||
useState<HasMoreChildrenState>({});
|
||||
|
||||
const mergeChildren = useCallback(
|
||||
(
|
||||
staticChildren: TreeNode<Type, Context>[] = [],
|
||||
fetchedChildren: TreeNode<Type, Context>[] = []
|
||||
) => {
|
||||
const fetchedChildrenIds = new Set(
|
||||
fetchedChildren.map((child) => child.id)
|
||||
);
|
||||
const uniqueStaticChildren = staticChildren.filter(
|
||||
(child) => !fetchedChildrenIds.has(child.id)
|
||||
);
|
||||
return [...uniqueStaticChildren, ...fetchedChildren];
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const toggleNode = useCallback(
|
||||
async (
|
||||
nodeId: string,
|
||||
nodeType: Type,
|
||||
nodeContext: Context[Type],
|
||||
staticChildren?: TreeNode<Type, Context>[]
|
||||
) => {
|
||||
if (expanded[nodeId]) {
|
||||
// If we're collapsing, just update expanded state
|
||||
setExpanded((prev) => ({ ...prev, [nodeId]: false }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get any previously fetched children
|
||||
const previouslyFetchedChildren = loadedChildren[nodeId] || [];
|
||||
|
||||
// If we have static children, merge them with any previously fetched children
|
||||
if (staticChildren?.length) {
|
||||
const mergedChildren = mergeChildren(
|
||||
staticChildren,
|
||||
previouslyFetchedChildren
|
||||
);
|
||||
setLoadedChildren((prev) => ({
|
||||
...prev,
|
||||
[nodeId]: mergedChildren,
|
||||
}));
|
||||
|
||||
// Only show "more loading" if we haven't fetched children before
|
||||
setHasMoreChildren((prev) => ({
|
||||
...prev,
|
||||
[nodeId]: !previouslyFetchedChildren.length,
|
||||
}));
|
||||
}
|
||||
|
||||
// Set expanded state immediately to show static/previously fetched children
|
||||
setExpanded((prev) => ({ ...prev, [nodeId]: true }));
|
||||
|
||||
// If we haven't loaded dynamic children yet
|
||||
if (!previouslyFetchedChildren.length) {
|
||||
setLoading((prev) => ({ ...prev, [nodeId]: true }));
|
||||
try {
|
||||
const fetchedChildren = await fetchChildren?.(
|
||||
nodeId,
|
||||
nodeType,
|
||||
nodeContext
|
||||
);
|
||||
// Merge static and newly fetched children
|
||||
const allChildren = mergeChildren(
|
||||
staticChildren || [],
|
||||
fetchedChildren
|
||||
);
|
||||
|
||||
setLoadedChildren((prev) => ({
|
||||
...prev,
|
||||
[nodeId]: allChildren,
|
||||
}));
|
||||
setHasMoreChildren((prev) => ({
|
||||
...prev,
|
||||
[nodeId]: false,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error loading children:', error);
|
||||
} finally {
|
||||
setLoading((prev) => ({ ...prev, [nodeId]: false }));
|
||||
}
|
||||
}
|
||||
},
|
||||
[expanded, loadedChildren, fetchChildren, mergeChildren, setExpanded]
|
||||
);
|
||||
|
||||
return {
|
||||
expanded,
|
||||
loading,
|
||||
loadedChildren,
|
||||
hasMoreChildren,
|
||||
toggleNode,
|
||||
};
|
||||
}
|
||||
@@ -12,6 +12,8 @@ export interface CanvasContext {
|
||||
}) => void;
|
||||
setOverlapGraph: (graph: Graph<string>) => void;
|
||||
overlapGraph: Graph<string>;
|
||||
setShowFilter: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
showFilter: boolean;
|
||||
}
|
||||
|
||||
export const canvasContext = createContext<CanvasContext>({
|
||||
@@ -19,4 +21,6 @@ export const canvasContext = createContext<CanvasContext>({
|
||||
fitView: emptyFn,
|
||||
setOverlapGraph: emptyFn,
|
||||
overlapGraph: createGraph(),
|
||||
setShowFilter: emptyFn,
|
||||
showFilter: false,
|
||||
});
|
||||
|
||||
@@ -1,26 +1,56 @@
|
||||
import React, { type ReactNode, useCallback, useState } from 'react';
|
||||
import React, {
|
||||
type ReactNode,
|
||||
useCallback,
|
||||
useState,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { canvasContext } from './canvas-context';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import {
|
||||
adjustTablePositions,
|
||||
shouldShowTablesBySchemaFilter,
|
||||
} from '@/lib/domain/db-table';
|
||||
import { adjustTablePositions } from '@/lib/domain/db-table';
|
||||
import { useReactFlow } from '@xyflow/react';
|
||||
import { findOverlappingTables } from '@/pages/editor-page/canvas/canvas-utils';
|
||||
import type { Graph } from '@/lib/graph';
|
||||
import { createGraph } from '@/lib/graph';
|
||||
import { useDiagramFilter } from '../diagram-filter-context/use-diagram-filter';
|
||||
import { filterTable } from '@/lib/domain/diagram-filter/filter';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
|
||||
interface CanvasProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
||||
const { tables, relationships, updateTablesState, filteredSchemas } =
|
||||
useChartDB();
|
||||
const {
|
||||
tables,
|
||||
relationships,
|
||||
updateTablesState,
|
||||
databaseType,
|
||||
areas,
|
||||
diagramId,
|
||||
} = useChartDB();
|
||||
const { filter, loading: filterLoading } = useDiagramFilter();
|
||||
const { fitView } = useReactFlow();
|
||||
const [overlapGraph, setOverlapGraph] =
|
||||
useState<Graph<string>>(createGraph());
|
||||
|
||||
const [showFilter, setShowFilter] = useState(false);
|
||||
const diagramIdActiveFilterRef = useRef<string>();
|
||||
|
||||
useEffect(() => {
|
||||
if (filterLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (diagramIdActiveFilterRef.current === diagramId) {
|
||||
return;
|
||||
}
|
||||
|
||||
diagramIdActiveFilterRef.current = diagramId;
|
||||
|
||||
setShowFilter(true);
|
||||
}, [filterLoading, diagramId]);
|
||||
|
||||
const reorderTables = useCallback(
|
||||
(
|
||||
options: { updateHistory?: boolean } = {
|
||||
@@ -30,9 +60,19 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
||||
const newTables = adjustTablePositions({
|
||||
relationships,
|
||||
tables: tables.filter((table) =>
|
||||
shouldShowTablesBySchemaFilter(table, filteredSchemas)
|
||||
filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
})
|
||||
),
|
||||
mode: 'all', // Use 'all' mode for manual reordering
|
||||
areas,
|
||||
mode: 'all',
|
||||
});
|
||||
|
||||
const updatedOverlapGraph = findOverlappingTables({
|
||||
@@ -67,7 +107,15 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
||||
});
|
||||
}, 500);
|
||||
},
|
||||
[filteredSchemas, relationships, tables, updateTablesState, fitView]
|
||||
[
|
||||
filter,
|
||||
relationships,
|
||||
tables,
|
||||
updateTablesState,
|
||||
fitView,
|
||||
databaseType,
|
||||
areas,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -77,6 +125,8 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
||||
fitView,
|
||||
setOverlapGraph,
|
||||
overlapGraph,
|
||||
setShowFilter,
|
||||
showFilter,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -78,8 +78,8 @@ export interface ChartDBContext {
|
||||
events: EventEmitter<ChartDBEvent>;
|
||||
readonly?: boolean;
|
||||
|
||||
filteredSchemas?: string[];
|
||||
filterSchemas: (schemaIds: string[]) => void;
|
||||
highlightedCustomType?: DBCustomType;
|
||||
highlightCustomTypeId: (id?: string) => void;
|
||||
|
||||
// General operations
|
||||
updateDiagramId: (id: string) => Promise<void>;
|
||||
@@ -92,6 +92,10 @@ export interface ChartDBContext {
|
||||
updateDiagramUpdatedAt: () => Promise<void>;
|
||||
clearDiagramData: () => Promise<void>;
|
||||
deleteDiagram: () => Promise<void>;
|
||||
updateDiagramData: (
|
||||
diagram: Diagram,
|
||||
options?: { forceUpdateStorage?: boolean }
|
||||
) => Promise<void>;
|
||||
|
||||
// Database type operations
|
||||
updateDatabaseType: (databaseType: DatabaseType) => Promise<void>;
|
||||
@@ -289,8 +293,7 @@ export const chartDBContext = createContext<ChartDBContext>({
|
||||
areas: [],
|
||||
customTypes: [],
|
||||
schemas: [],
|
||||
filteredSchemas: [],
|
||||
filterSchemas: emptyFn,
|
||||
highlightCustomTypeId: emptyFn,
|
||||
currentDiagram: {
|
||||
id: '',
|
||||
name: '',
|
||||
@@ -308,6 +311,7 @@ export const chartDBContext = createContext<ChartDBContext>({
|
||||
loadDiagramFromData: emptyFn,
|
||||
clearDiagramData: emptyFn,
|
||||
deleteDiagram: emptyFn,
|
||||
updateDiagramData: emptyFn,
|
||||
|
||||
// Database type operations
|
||||
updateDatabaseType: emptyFn,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import type { DBTable } from '@/lib/domain/db-table';
|
||||
import { deepCopy, generateId } from '@/lib/utils';
|
||||
import { randomColor } from '@/lib/colors';
|
||||
import { defaultTableColor, defaultAreaColor, viewColor } from '@/lib/colors';
|
||||
import type { ChartDBContext, ChartDBEvent } from './chartdb-context';
|
||||
import { chartDBContext } from './chartdb-context';
|
||||
import { DatabaseType } from '@/lib/domain/database-type';
|
||||
import type { DBField } from '@/lib/domain/db-field';
|
||||
import type { DBIndex } from '@/lib/domain/db-index';
|
||||
import {
|
||||
getTableIndexesWithPrimaryKey,
|
||||
type DBIndex,
|
||||
} from '@/lib/domain/db-index';
|
||||
import type { DBRelationship } from '@/lib/domain/db-relationship';
|
||||
import { useStorage } from '@/hooks/use-storage';
|
||||
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
||||
@@ -17,7 +20,6 @@ import {
|
||||
databasesWithSchemas,
|
||||
schemaNameToSchemaId,
|
||||
} from '@/lib/domain/db-schema';
|
||||
import { useLocalConfig } from '@/hooks/use-local-config';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
import { useEventEmitter } from 'ahooks';
|
||||
import type { DBDependency } from '@/lib/domain/db-dependency';
|
||||
@@ -39,11 +41,11 @@ export const ChartDBProvider: React.FC<
|
||||
React.PropsWithChildren<ChartDBProviderProps>
|
||||
> = ({ children, diagram, readonly: readonlyProp }) => {
|
||||
const { hasDiff } = useDiff();
|
||||
let db = useStorage();
|
||||
const storageDB = useStorage();
|
||||
const events = useEventEmitter<ChartDBEvent>();
|
||||
const { setSchemasFilter, schemasFilter } = useLocalConfig();
|
||||
const { addUndoAction, resetRedoStack, resetUndoStack } =
|
||||
useRedoUndoStack();
|
||||
|
||||
const [diagramId, setDiagramId] = useState('');
|
||||
const [diagramName, setDiagramName] = useState('');
|
||||
const [diagramCreatedAt, setDiagramCreatedAt] = useState<Date>(new Date());
|
||||
@@ -65,8 +67,12 @@ export const ChartDBProvider: React.FC<
|
||||
const [customTypes, setCustomTypes] = useState<DBCustomType[]>(
|
||||
diagram?.customTypes ?? []
|
||||
);
|
||||
|
||||
const { events: diffEvents } = useDiff();
|
||||
|
||||
const [highlightedCustomTypeId, setHighlightedCustomTypeId] =
|
||||
useState<string>();
|
||||
|
||||
const diffCalculatedHandler = useCallback((event: DiffCalculatedEvent) => {
|
||||
const { tablesAdded, fieldsAdded, relationshipsAdded } = event.data;
|
||||
setTables((tables) =>
|
||||
@@ -85,17 +91,16 @@ export const ChartDBProvider: React.FC<
|
||||
|
||||
diffEvents.useSubscription(diffCalculatedHandler);
|
||||
|
||||
const defaultSchemaName = defaultSchemas[databaseType];
|
||||
const defaultSchemaName = useMemo(
|
||||
() => defaultSchemas[databaseType],
|
||||
[databaseType]
|
||||
);
|
||||
|
||||
const readonly = useMemo(
|
||||
() => readonlyProp ?? hasDiff ?? false,
|
||||
[readonlyProp, hasDiff]
|
||||
);
|
||||
|
||||
if (readonly) {
|
||||
db = storageInitialValue;
|
||||
}
|
||||
|
||||
const schemas = useMemo(
|
||||
() =>
|
||||
databasesWithSchemas.includes(databaseType)
|
||||
@@ -106,9 +111,11 @@ export const ChartDBProvider: React.FC<
|
||||
.filter((schema) => !!schema) as string[]
|
||||
),
|
||||
]
|
||||
.sort((a, b) =>
|
||||
a === defaultSchemaName ? -1 : a.localeCompare(b)
|
||||
)
|
||||
.sort((a, b) => {
|
||||
if (a === defaultSchemaName) return -1;
|
||||
if (b === defaultSchemaName) return 1;
|
||||
return a.localeCompare(b);
|
||||
})
|
||||
.map(
|
||||
(schema): DBSchema => ({
|
||||
id: schemaNameToSchemaId(schema),
|
||||
@@ -122,34 +129,11 @@ export const ChartDBProvider: React.FC<
|
||||
[tables, defaultSchemaName, databaseType]
|
||||
);
|
||||
|
||||
const filterSchemas: ChartDBContext['filterSchemas'] = useCallback(
|
||||
(schemaIds) => {
|
||||
setSchemasFilter((prev) => ({
|
||||
...prev,
|
||||
[diagramId]: schemaIds,
|
||||
}));
|
||||
},
|
||||
[diagramId, setSchemasFilter]
|
||||
const db = useMemo(
|
||||
() => (readonly ? storageInitialValue : storageDB),
|
||||
[storageDB, readonly]
|
||||
);
|
||||
|
||||
const filteredSchemas: ChartDBContext['filteredSchemas'] = useMemo(() => {
|
||||
if (schemas.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const schemasFilterFromCache =
|
||||
(schemasFilter[diagramId] ?? []).length === 0
|
||||
? undefined // in case of empty filter, skip cache
|
||||
: schemasFilter[diagramId];
|
||||
|
||||
return (
|
||||
schemasFilterFromCache ?? [
|
||||
schemas.find((s) => s.name === defaultSchemaName)?.id ??
|
||||
schemas[0]?.id,
|
||||
]
|
||||
);
|
||||
}, [schemasFilter, diagramId, schemas, defaultSchemaName]);
|
||||
|
||||
const currentDiagram: Diagram = useMemo(
|
||||
() => ({
|
||||
id: diagramId,
|
||||
@@ -304,22 +288,27 @@ export const ChartDBProvider: React.FC<
|
||||
);
|
||||
|
||||
const addTables: ChartDBContext['addTables'] = useCallback(
|
||||
async (tables: DBTable[], options = { updateHistory: true }) => {
|
||||
setTables((currentTables) => [...currentTables, ...tables]);
|
||||
async (tablesToAdd: DBTable[], options = { updateHistory: true }) => {
|
||||
setTables((currentTables) => [...currentTables, ...tablesToAdd]);
|
||||
const updatedAt = new Date();
|
||||
setDiagramUpdatedAt(updatedAt);
|
||||
await Promise.all([
|
||||
db.updateDiagram({ id: diagramId, attributes: { updatedAt } }),
|
||||
...tables.map((table) => db.addTable({ diagramId, table })),
|
||||
...tablesToAdd.map((table) =>
|
||||
db.addTable({ diagramId, table })
|
||||
),
|
||||
]);
|
||||
|
||||
events.emit({ action: 'add_tables', data: { tables } });
|
||||
events.emit({
|
||||
action: 'add_tables',
|
||||
data: { tables: tablesToAdd },
|
||||
});
|
||||
|
||||
if (options.updateHistory) {
|
||||
addUndoAction({
|
||||
action: 'addTables',
|
||||
redoData: { tables },
|
||||
undoData: { tableIds: tables.map((t) => t.id) },
|
||||
redoData: { tables: tablesToAdd },
|
||||
undoData: { tableIds: tablesToAdd.map((t) => t.id) },
|
||||
});
|
||||
resetRedoStack();
|
||||
}
|
||||
@@ -356,12 +345,17 @@ export const ChartDBProvider: React.FC<
|
||||
},
|
||||
],
|
||||
indexes: [],
|
||||
color: randomColor(),
|
||||
color: attributes?.isView ? viewColor : defaultTableColor,
|
||||
createdAt: Date.now(),
|
||||
isView: false,
|
||||
order: tables.length,
|
||||
...attributes,
|
||||
};
|
||||
|
||||
table.indexes = getTableIndexesWithPrimaryKey({
|
||||
table,
|
||||
});
|
||||
|
||||
await addTable(table);
|
||||
|
||||
return table;
|
||||
@@ -653,17 +647,30 @@ export const ChartDBProvider: React.FC<
|
||||
options = { updateHistory: true }
|
||||
) => {
|
||||
const prevField = getField(tableId, fieldId);
|
||||
|
||||
const updateTableFn = (table: DBTable) => {
|
||||
const updatedTable: DBTable = {
|
||||
...table,
|
||||
fields: table.fields.map((f) =>
|
||||
f.id === fieldId ? { ...f, ...field } : f
|
||||
),
|
||||
} satisfies DBTable;
|
||||
|
||||
updatedTable.indexes = getTableIndexesWithPrimaryKey({
|
||||
table: updatedTable,
|
||||
});
|
||||
|
||||
return updatedTable;
|
||||
};
|
||||
|
||||
setTables((tables) =>
|
||||
tables.map((table) =>
|
||||
table.id === tableId
|
||||
? {
|
||||
...table,
|
||||
fields: table.fields.map((f) =>
|
||||
f.id === fieldId ? { ...f, ...field } : f
|
||||
),
|
||||
}
|
||||
: table
|
||||
)
|
||||
tables.map((table) => {
|
||||
if (table.id === tableId) {
|
||||
return updateTableFn(table);
|
||||
}
|
||||
|
||||
return table;
|
||||
})
|
||||
);
|
||||
|
||||
const table = await db.getTable({ diagramId, id: tableId });
|
||||
@@ -678,10 +685,7 @@ export const ChartDBProvider: React.FC<
|
||||
db.updateTable({
|
||||
id: tableId,
|
||||
attributes: {
|
||||
...table,
|
||||
fields: table.fields.map((f) =>
|
||||
f.id === fieldId ? { ...f, ...field } : f
|
||||
),
|
||||
...updateTableFn(table),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
@@ -708,19 +712,29 @@ export const ChartDBProvider: React.FC<
|
||||
fieldId: string,
|
||||
options = { updateHistory: true }
|
||||
) => {
|
||||
const updateTableFn = (table: DBTable) => {
|
||||
const updatedTable: DBTable = {
|
||||
...table,
|
||||
fields: table.fields.filter((f) => f.id !== fieldId),
|
||||
} satisfies DBTable;
|
||||
|
||||
updatedTable.indexes = getTableIndexesWithPrimaryKey({
|
||||
table: updatedTable,
|
||||
});
|
||||
|
||||
return updatedTable;
|
||||
};
|
||||
|
||||
const fields = getTable(tableId)?.fields ?? [];
|
||||
const prevField = getField(tableId, fieldId);
|
||||
setTables((tables) =>
|
||||
tables.map((table) =>
|
||||
table.id === tableId
|
||||
? {
|
||||
...table,
|
||||
fields: table.fields.filter(
|
||||
(f) => f.id !== fieldId
|
||||
),
|
||||
}
|
||||
: table
|
||||
)
|
||||
tables.map((table) => {
|
||||
if (table.id === tableId) {
|
||||
return updateTableFn(table);
|
||||
}
|
||||
|
||||
return table;
|
||||
})
|
||||
);
|
||||
|
||||
events.emit({
|
||||
@@ -744,8 +758,7 @@ export const ChartDBProvider: React.FC<
|
||||
db.updateTable({
|
||||
id: tableId,
|
||||
attributes: {
|
||||
...table,
|
||||
fields: table.fields.filter((f) => f.id !== fieldId),
|
||||
...updateTableFn(table),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
@@ -778,13 +791,23 @@ export const ChartDBProvider: React.FC<
|
||||
options = { updateHistory: true }
|
||||
) => {
|
||||
const fields = getTable(tableId)?.fields ?? [];
|
||||
setTables((tables) =>
|
||||
tables.map((table) =>
|
||||
table.id === tableId
|
||||
? { ...table, fields: [...table.fields, field] }
|
||||
: table
|
||||
)
|
||||
);
|
||||
setTables((tables) => {
|
||||
return tables.map((table) => {
|
||||
if (table.id === tableId) {
|
||||
db.updateTable({
|
||||
id: tableId,
|
||||
attributes: {
|
||||
...table,
|
||||
fields: [...table.fields, field],
|
||||
},
|
||||
});
|
||||
|
||||
return { ...table, fields: [...table.fields, field] };
|
||||
}
|
||||
|
||||
return table;
|
||||
});
|
||||
});
|
||||
|
||||
events.emit({
|
||||
action: 'add_field',
|
||||
@@ -805,13 +828,6 @@ export const ChartDBProvider: React.FC<
|
||||
setDiagramUpdatedAt(updatedAt);
|
||||
await Promise.all([
|
||||
db.updateDiagram({ id: diagramId, attributes: { updatedAt } }),
|
||||
db.updateTable({
|
||||
id: tableId,
|
||||
attributes: {
|
||||
...table,
|
||||
fields: [...table.fields, field],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
if (options.updateHistory) {
|
||||
@@ -1098,12 +1114,15 @@ export const ChartDBProvider: React.FC<
|
||||
|
||||
const sourceFieldName = sourceField?.name ?? '';
|
||||
|
||||
const targetTable = getTable(targetTableId);
|
||||
const targetTableSchema = targetTable?.schema;
|
||||
|
||||
const relationship: DBRelationship = {
|
||||
id: generateId(),
|
||||
name: `${sourceTableName}_${sourceFieldName}_fk`,
|
||||
sourceSchema: sourceTable?.schema,
|
||||
sourceTableId,
|
||||
targetSchema: sourceTable?.schema,
|
||||
targetSchema: targetTableSchema,
|
||||
targetTableId,
|
||||
sourceFieldId,
|
||||
targetFieldId,
|
||||
@@ -1425,7 +1444,7 @@ export const ChartDBProvider: React.FC<
|
||||
y: 0,
|
||||
width: 300,
|
||||
height: 200,
|
||||
color: randomColor(),
|
||||
color: defaultAreaColor,
|
||||
...attributes,
|
||||
};
|
||||
|
||||
@@ -1508,22 +1527,37 @@ export const ChartDBProvider: React.FC<
|
||||
[db, diagramId, setAreas, getArea, addUndoAction, resetRedoStack]
|
||||
);
|
||||
|
||||
const highlightCustomTypeId = useCallback(
|
||||
(id?: string) => setHighlightedCustomTypeId(id),
|
||||
[setHighlightedCustomTypeId]
|
||||
);
|
||||
|
||||
const highlightedCustomType = useMemo(() => {
|
||||
return highlightedCustomTypeId
|
||||
? customTypes.find((type) => type.id === highlightedCustomTypeId)
|
||||
: undefined;
|
||||
}, [highlightedCustomTypeId, customTypes]);
|
||||
|
||||
const loadDiagramFromData: ChartDBContext['loadDiagramFromData'] =
|
||||
useCallback(
|
||||
async (diagram) => {
|
||||
(diagram) => {
|
||||
setDiagramId(diagram.id);
|
||||
setDiagramName(diagram.name);
|
||||
setDatabaseType(diagram.databaseType);
|
||||
setDatabaseEdition(diagram.databaseEdition);
|
||||
setTables(diagram?.tables ?? []);
|
||||
setRelationships(diagram?.relationships ?? []);
|
||||
setDependencies(diagram?.dependencies ?? []);
|
||||
setAreas(diagram?.areas ?? []);
|
||||
setCustomTypes(diagram?.customTypes ?? []);
|
||||
setTables(diagram.tables ?? []);
|
||||
setRelationships(diagram.relationships ?? []);
|
||||
setDependencies(diagram.dependencies ?? []);
|
||||
setAreas(diagram.areas ?? []);
|
||||
setCustomTypes(diagram.customTypes ?? []);
|
||||
setDiagramCreatedAt(diagram.createdAt);
|
||||
setDiagramUpdatedAt(diagram.updatedAt);
|
||||
setHighlightedCustomTypeId(undefined);
|
||||
|
||||
events.emit({ action: 'load_diagram', data: { diagram } });
|
||||
|
||||
resetRedoStack();
|
||||
resetUndoStack();
|
||||
},
|
||||
[
|
||||
setDiagramId,
|
||||
@@ -1537,13 +1571,26 @@ export const ChartDBProvider: React.FC<
|
||||
setCustomTypes,
|
||||
setDiagramCreatedAt,
|
||||
setDiagramUpdatedAt,
|
||||
setHighlightedCustomTypeId,
|
||||
events,
|
||||
resetRedoStack,
|
||||
resetUndoStack,
|
||||
]
|
||||
);
|
||||
|
||||
const updateDiagramData: ChartDBContext['updateDiagramData'] = useCallback(
|
||||
async (diagram, options) => {
|
||||
const st = options?.forceUpdateStorage ? storageDB : db;
|
||||
await st.deleteDiagram(diagram.id);
|
||||
await st.addDiagram({ diagram });
|
||||
loadDiagramFromData(diagram);
|
||||
},
|
||||
[db, storageDB, loadDiagramFromData]
|
||||
);
|
||||
|
||||
const loadDiagram: ChartDBContext['loadDiagram'] = useCallback(
|
||||
async (diagramId: string) => {
|
||||
const diagram = await db.getDiagram(diagramId, {
|
||||
const diagram = await storageDB.getDiagram(diagramId, {
|
||||
includeRelationships: true,
|
||||
includeTables: true,
|
||||
includeDependencies: true,
|
||||
@@ -1557,7 +1604,7 @@ export const ChartDBProvider: React.FC<
|
||||
|
||||
return diagram;
|
||||
},
|
||||
[db, loadDiagramFromData]
|
||||
[storageDB, loadDiagramFromData]
|
||||
);
|
||||
|
||||
// Custom type operations
|
||||
@@ -1716,10 +1763,9 @@ export const ChartDBProvider: React.FC<
|
||||
areas,
|
||||
currentDiagram,
|
||||
schemas,
|
||||
filteredSchemas,
|
||||
events,
|
||||
readonly,
|
||||
filterSchemas,
|
||||
updateDiagramData,
|
||||
updateDiagramId,
|
||||
updateDiagramName,
|
||||
loadDiagram,
|
||||
@@ -1776,6 +1822,8 @@ export const ChartDBProvider: React.FC<
|
||||
removeCustomType,
|
||||
removeCustomTypes,
|
||||
updateCustomType,
|
||||
highlightCustomTypeId,
|
||||
highlightedCustomType,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ConfigContext } from './config-context';
|
||||
|
||||
import { useStorage } from '@/hooks/use-storage';
|
||||
@@ -8,7 +8,7 @@ export const ConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const { getConfig, updateConfig: updateDataConfig } = useStorage();
|
||||
const [config, setConfig] = React.useState<ChartDBConfig | undefined>();
|
||||
const [config, setConfig] = useState<ChartDBConfig | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
const loadConfig = async () => {
|
||||
@@ -45,7 +45,12 @@ export const ConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfigContext.Provider value={{ config, updateConfig }}>
|
||||
<ConfigContext.Provider
|
||||
value={{
|
||||
config,
|
||||
updateConfig,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ConfigContext.Provider>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { DBSchema } from '@/lib/domain';
|
||||
import type {
|
||||
DiagramFilter,
|
||||
FilterTableInfo,
|
||||
} from '@/lib/domain/diagram-filter/diagram-filter';
|
||||
import { emptyFn } from '@/lib/utils';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export interface DiagramFilterContext {
|
||||
filter?: DiagramFilter;
|
||||
loading: boolean;
|
||||
|
||||
hasActiveFilter: boolean;
|
||||
schemasDisplayed: DBSchema[];
|
||||
|
||||
clearSchemaIdsFilter: () => void;
|
||||
clearTableIdsFilter: () => void;
|
||||
|
||||
setTableIdsFilterEmpty: () => void;
|
||||
|
||||
// reset
|
||||
resetFilter: () => void;
|
||||
|
||||
toggleSchemaFilter: (schemaId: string) => void;
|
||||
toggleTableFilter: (tableId: string) => void;
|
||||
addSchemaToFilter: (schemaId: string) => void;
|
||||
addTablesToFilter: (attrs: {
|
||||
tableIds?: string[];
|
||||
filterCallback?: (table: FilterTableInfo) => boolean;
|
||||
}) => void;
|
||||
removeTablesFromFilter: (attrs: {
|
||||
tableIds?: string[];
|
||||
filterCallback?: (table: FilterTableInfo) => boolean;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
export const diagramFilterContext = createContext<DiagramFilterContext>({
|
||||
hasActiveFilter: false,
|
||||
clearSchemaIdsFilter: emptyFn,
|
||||
clearTableIdsFilter: emptyFn,
|
||||
setTableIdsFilterEmpty: emptyFn,
|
||||
resetFilter: emptyFn,
|
||||
toggleSchemaFilter: emptyFn,
|
||||
toggleTableFilter: emptyFn,
|
||||
addSchemaToFilter: emptyFn,
|
||||
schemasDisplayed: [],
|
||||
addTablesToFilter: emptyFn,
|
||||
removeTablesFromFilter: emptyFn,
|
||||
loading: false,
|
||||
});
|
||||
559
src/context/diagram-filter-context/diagram-filter-provider.tsx
Normal file
559
src/context/diagram-filter-context/diagram-filter-provider.tsx
Normal file
@@ -0,0 +1,559 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import type { DiagramFilterContext } from './diagram-filter-context';
|
||||
import { diagramFilterContext } from './diagram-filter-context';
|
||||
import type {
|
||||
DiagramFilter,
|
||||
FilterTableInfo,
|
||||
} from '@/lib/domain/diagram-filter/diagram-filter';
|
||||
import {
|
||||
reduceFilter,
|
||||
spreadFilterTables,
|
||||
} from '@/lib/domain/diagram-filter/diagram-filter';
|
||||
import { useStorage } from '@/hooks/use-storage';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { filterTable } from '@/lib/domain/diagram-filter/filter';
|
||||
import { databasesWithSchemas, schemaNameToSchemaId } from '@/lib/domain';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
import type { ChartDBEvent } from '../chartdb-context/chartdb-context';
|
||||
|
||||
export const DiagramFilterProvider: React.FC<React.PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const { diagramId, tables, schemas, databaseType, events } = useChartDB();
|
||||
const { getDiagramFilter, updateDiagramFilter } = useStorage();
|
||||
const [filter, setFilter] = useState<DiagramFilter>({});
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
const allSchemasIds = useMemo(() => {
|
||||
return schemas.map((schema) => schema.id);
|
||||
}, [schemas]);
|
||||
|
||||
const allTables: FilterTableInfo[] = useMemo(() => {
|
||||
return tables.map(
|
||||
(table) =>
|
||||
({
|
||||
id: table.id,
|
||||
schemaId: table.schema
|
||||
? schemaNameToSchemaId(table.schema)
|
||||
: defaultSchemas[databaseType],
|
||||
schema: table.schema ?? defaultSchemas[databaseType],
|
||||
areaId: table.parentAreaId ?? undefined,
|
||||
}) satisfies FilterTableInfo
|
||||
);
|
||||
}, [tables, databaseType]);
|
||||
|
||||
const diagramIdOfLoadedFilter = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (diagramId && diagramId === diagramIdOfLoadedFilter.current) {
|
||||
updateDiagramFilter(diagramId, filter);
|
||||
}
|
||||
}, [diagramId, filter, updateDiagramFilter]);
|
||||
|
||||
// Reset filter when diagram changes
|
||||
useEffect(() => {
|
||||
if (diagramIdOfLoadedFilter.current === diagramId) {
|
||||
// If the diagramId hasn't changed, do not reset the filter
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
const loadFilterFromStorage = async (diagramId: string) => {
|
||||
if (diagramId) {
|
||||
const storedFilter = await getDiagramFilter(diagramId);
|
||||
|
||||
let filterToSet = storedFilter;
|
||||
|
||||
if (!filterToSet) {
|
||||
// If no filter is stored, set default based on database type
|
||||
filterToSet =
|
||||
schemas.length > 1
|
||||
? { schemaIds: [schemas[0].id] }
|
||||
: {};
|
||||
}
|
||||
|
||||
setFilter(filterToSet);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
setFilter({});
|
||||
|
||||
if (diagramId) {
|
||||
loadFilterFromStorage(diagramId);
|
||||
diagramIdOfLoadedFilter.current = diagramId;
|
||||
}
|
||||
}, [diagramId, getDiagramFilter, schemas]);
|
||||
|
||||
const clearSchemaIds: DiagramFilterContext['clearSchemaIdsFilter'] =
|
||||
useCallback(() => {
|
||||
setFilter(
|
||||
(prev) =>
|
||||
({
|
||||
...prev,
|
||||
schemaIds: undefined,
|
||||
}) satisfies DiagramFilter
|
||||
);
|
||||
}, []);
|
||||
|
||||
const clearTableIds: DiagramFilterContext['clearTableIdsFilter'] =
|
||||
useCallback(() => {
|
||||
setFilter(
|
||||
(prev) =>
|
||||
({
|
||||
...prev,
|
||||
tableIds: undefined,
|
||||
}) satisfies DiagramFilter
|
||||
);
|
||||
}, []);
|
||||
|
||||
const setTableIdsEmpty: DiagramFilterContext['setTableIdsFilterEmpty'] =
|
||||
useCallback(() => {
|
||||
setFilter(
|
||||
(prev) =>
|
||||
({
|
||||
...prev,
|
||||
tableIds: [],
|
||||
}) satisfies DiagramFilter
|
||||
);
|
||||
}, []);
|
||||
|
||||
// Reset filter
|
||||
const resetFilter: DiagramFilterContext['resetFilter'] = useCallback(() => {
|
||||
setFilter({});
|
||||
}, []);
|
||||
|
||||
const toggleSchemaFilter: DiagramFilterContext['toggleSchemaFilter'] =
|
||||
useCallback(
|
||||
(schemaId: string) => {
|
||||
setFilter((prev) => {
|
||||
const currentSchemaIds = prev.schemaIds;
|
||||
|
||||
// Check if schema is currently visible
|
||||
const isSchemaVisible = !allTables.some(
|
||||
(table) =>
|
||||
table.schemaId === schemaId &&
|
||||
filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter: prev,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
}) === false
|
||||
);
|
||||
|
||||
let newSchemaIds: string[] | undefined;
|
||||
let newTableIds: string[] | undefined = prev.tableIds;
|
||||
|
||||
if (isSchemaVisible) {
|
||||
// Schema is visible, make it not visible
|
||||
if (!currentSchemaIds) {
|
||||
// All schemas are visible, create filter with all except this one
|
||||
newSchemaIds = allSchemasIds.filter(
|
||||
(id) => id !== schemaId
|
||||
);
|
||||
} else {
|
||||
// Remove this schema from the filter
|
||||
newSchemaIds = currentSchemaIds.filter(
|
||||
(id) => id !== schemaId
|
||||
);
|
||||
}
|
||||
|
||||
// Remove tables from this schema from tableIds if present
|
||||
if (prev.tableIds) {
|
||||
const schemaTableIds = allTables
|
||||
.filter((table) => table.schemaId === schemaId)
|
||||
.map((table) => table.id);
|
||||
newTableIds = prev.tableIds.filter(
|
||||
(id) => !schemaTableIds.includes(id)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Schema is not visible, make it visible
|
||||
newSchemaIds = [
|
||||
...new Set([...(currentSchemaIds || []), schemaId]),
|
||||
];
|
||||
|
||||
// Add tables from this schema to tableIds if tableIds is defined
|
||||
if (prev.tableIds) {
|
||||
const schemaTableIds = allTables
|
||||
.filter((table) => table.schemaId === schemaId)
|
||||
.map((table) => table.id);
|
||||
newTableIds = [
|
||||
...new Set([
|
||||
...prev.tableIds,
|
||||
...schemaTableIds,
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Use reduceFilter to optimize and handle edge cases
|
||||
return reduceFilter(
|
||||
{
|
||||
schemaIds: newSchemaIds,
|
||||
tableIds: newTableIds,
|
||||
},
|
||||
allTables satisfies FilterTableInfo[],
|
||||
{
|
||||
databaseWithSchemas:
|
||||
databasesWithSchemas.includes(databaseType),
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
[allSchemasIds, allTables, databaseType]
|
||||
);
|
||||
|
||||
const toggleTableFilterForNoSchema = useCallback(
|
||||
(tableId: string) => {
|
||||
setFilter((prev) => {
|
||||
const currentTableIds = prev.tableIds;
|
||||
|
||||
// Check if table is currently visible
|
||||
const isTableVisible = filterTable({
|
||||
table: { id: tableId, schema: undefined },
|
||||
filter: prev,
|
||||
options: { defaultSchema: undefined },
|
||||
});
|
||||
|
||||
let newTableIds: string[] | undefined;
|
||||
|
||||
if (isTableVisible) {
|
||||
// Table is visible, make it not visible
|
||||
if (!currentTableIds) {
|
||||
// All tables are visible, create filter with all except this one
|
||||
newTableIds = allTables
|
||||
.filter((t) => t.id !== tableId)
|
||||
.map((t) => t.id);
|
||||
} else {
|
||||
// Remove this table from the filter
|
||||
newTableIds = currentTableIds.filter(
|
||||
(id) => id !== tableId
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Table is not visible, make it visible
|
||||
newTableIds = [
|
||||
...new Set([...(currentTableIds || []), tableId]),
|
||||
];
|
||||
}
|
||||
|
||||
// Use reduceFilter to optimize and handle edge cases
|
||||
return reduceFilter(
|
||||
{
|
||||
schemaIds: undefined,
|
||||
tableIds: newTableIds,
|
||||
},
|
||||
allTables satisfies FilterTableInfo[],
|
||||
{
|
||||
databaseWithSchemas:
|
||||
databasesWithSchemas.includes(databaseType),
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
[allTables, databaseType]
|
||||
);
|
||||
|
||||
const toggleTableFilter: DiagramFilterContext['toggleTableFilter'] =
|
||||
useCallback(
|
||||
(tableId: string) => {
|
||||
if (!databasesWithSchemas.includes(databaseType)) {
|
||||
// No schemas, toggle table filter without schema context
|
||||
toggleTableFilterForNoSchema(tableId);
|
||||
return;
|
||||
}
|
||||
|
||||
setFilter((prev) => {
|
||||
// Find the table in the tables list
|
||||
const tableInfo = allTables.find((t) => t.id === tableId);
|
||||
|
||||
if (!tableInfo) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
// Check if table is currently visible using filterTable
|
||||
const isTableVisible = filterTable({
|
||||
table: {
|
||||
id: tableInfo.id,
|
||||
schema: tableInfo.schema,
|
||||
},
|
||||
filter: prev,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
});
|
||||
|
||||
let newSchemaIds = prev.schemaIds;
|
||||
let newTableIds = prev.tableIds;
|
||||
|
||||
if (isTableVisible) {
|
||||
// Table is visible, make it not visible
|
||||
|
||||
// If the table is visible due to its schema being in schemaIds
|
||||
if (
|
||||
tableInfo?.schemaId &&
|
||||
prev.schemaIds?.includes(tableInfo.schemaId)
|
||||
) {
|
||||
// Remove the schema from schemaIds and add all other tables from that schema to tableIds
|
||||
newSchemaIds = prev.schemaIds.filter(
|
||||
(id) => id !== tableInfo.schemaId
|
||||
);
|
||||
|
||||
// Get all other tables from this schema (except the one being toggled)
|
||||
const otherTablesFromSchema = allTables
|
||||
.filter(
|
||||
(t) =>
|
||||
t.schemaId === tableInfo.schemaId &&
|
||||
t.id !== tableId
|
||||
)
|
||||
.map((t) => t.id);
|
||||
|
||||
// Add these tables to tableIds
|
||||
newTableIds = [
|
||||
...(prev.tableIds || []),
|
||||
...otherTablesFromSchema,
|
||||
];
|
||||
} else if (prev.tableIds?.includes(tableId)) {
|
||||
// Table is visible because it's in tableIds, remove it
|
||||
newTableIds = prev.tableIds.filter(
|
||||
(id) => id !== tableId
|
||||
);
|
||||
} else if (!prev.tableIds && !prev.schemaIds) {
|
||||
// No filters = all visible, create filter with all tables except this one
|
||||
newTableIds = allTables
|
||||
.filter((t) => t.id !== tableId)
|
||||
.map((t) => t.id);
|
||||
}
|
||||
} else {
|
||||
// Table is not visible, make it visible by adding to tableIds
|
||||
newTableIds = [...(prev.tableIds || []), tableId];
|
||||
}
|
||||
|
||||
// Use reduceFilter to optimize and handle edge cases
|
||||
return reduceFilter(
|
||||
{
|
||||
schemaIds: newSchemaIds,
|
||||
tableIds: newTableIds,
|
||||
},
|
||||
allTables satisfies FilterTableInfo[],
|
||||
{
|
||||
databaseWithSchemas:
|
||||
databasesWithSchemas.includes(databaseType),
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
[allTables, databaseType, toggleTableFilterForNoSchema]
|
||||
);
|
||||
|
||||
const addSchemaToFilter: DiagramFilterContext['addSchemaToFilter'] =
|
||||
useCallback(
|
||||
(schemaId: string) => {
|
||||
setFilter((prev) => {
|
||||
const currentSchemaIds = prev.schemaIds;
|
||||
if (!currentSchemaIds) {
|
||||
// No schemas are filtered
|
||||
return prev;
|
||||
}
|
||||
|
||||
// If schema is already filtered, do nothing
|
||||
if (currentSchemaIds.includes(schemaId)) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
// Add schema to the filter
|
||||
const newSchemaIds = [...currentSchemaIds, schemaId];
|
||||
|
||||
if (newSchemaIds.length === allSchemasIds.length) {
|
||||
// All schemas are now filtered, set to undefined
|
||||
return {
|
||||
...prev,
|
||||
schemaIds: undefined,
|
||||
} satisfies DiagramFilter;
|
||||
}
|
||||
return {
|
||||
...prev,
|
||||
schemaIds: newSchemaIds,
|
||||
} satisfies DiagramFilter;
|
||||
});
|
||||
},
|
||||
[allSchemasIds.length]
|
||||
);
|
||||
|
||||
const hasActiveFilter: boolean = useMemo(() => {
|
||||
return !!filter.schemaIds || !!filter.tableIds;
|
||||
}, [filter]);
|
||||
|
||||
const schemasDisplayed: DiagramFilterContext['schemasDisplayed'] =
|
||||
useMemo(() => {
|
||||
if (!hasActiveFilter) {
|
||||
return schemas;
|
||||
}
|
||||
|
||||
const displayedSchemaIds = new Set<string>();
|
||||
for (const table of allTables) {
|
||||
if (
|
||||
filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
})
|
||||
) {
|
||||
if (table.schemaId) {
|
||||
displayedSchemaIds.add(table.schemaId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return schemas.filter((schema) =>
|
||||
displayedSchemaIds.has(schema.id)
|
||||
);
|
||||
}, [hasActiveFilter, schemas, allTables, filter, databaseType]);
|
||||
|
||||
const addTablesToFilter: DiagramFilterContext['addTablesToFilter'] =
|
||||
useCallback(
|
||||
({ tableIds, filterCallback }) => {
|
||||
setFilter((prev) => {
|
||||
let tableIdsToAdd: string[];
|
||||
|
||||
if (tableIds) {
|
||||
// If tableIds are provided, use them directly
|
||||
tableIdsToAdd = tableIds;
|
||||
} else if (filterCallback) {
|
||||
// If filterCallback is provided, filter tables based on it
|
||||
tableIdsToAdd = allTables
|
||||
.filter(filterCallback)
|
||||
.map((table) => table.id);
|
||||
} else {
|
||||
// If neither is provided, do nothing
|
||||
return prev;
|
||||
}
|
||||
|
||||
const filterByTableIds = spreadFilterTables(
|
||||
prev,
|
||||
allTables satisfies FilterTableInfo[]
|
||||
);
|
||||
|
||||
const currentTableIds = filterByTableIds.tableIds || [];
|
||||
const newTableIds = [
|
||||
...new Set([...currentTableIds, ...tableIdsToAdd]),
|
||||
];
|
||||
|
||||
return reduceFilter(
|
||||
{
|
||||
...filterByTableIds,
|
||||
tableIds: newTableIds,
|
||||
},
|
||||
allTables satisfies FilterTableInfo[],
|
||||
{
|
||||
databaseWithSchemas:
|
||||
databasesWithSchemas.includes(databaseType),
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
[allTables, databaseType]
|
||||
);
|
||||
|
||||
const removeTablesFromFilter: DiagramFilterContext['removeTablesFromFilter'] =
|
||||
useCallback(
|
||||
({ tableIds, filterCallback }) => {
|
||||
setFilter((prev) => {
|
||||
let tableIdsToRemovoe: string[];
|
||||
|
||||
if (tableIds) {
|
||||
// If tableIds are provided, use them directly
|
||||
tableIdsToRemovoe = tableIds;
|
||||
} else if (filterCallback) {
|
||||
// If filterCallback is provided, filter tables based on it
|
||||
tableIdsToRemovoe = allTables
|
||||
.filter(filterCallback)
|
||||
.map((table) => table.id);
|
||||
} else {
|
||||
// If neither is provided, do nothing
|
||||
return prev;
|
||||
}
|
||||
|
||||
const filterByTableIds = spreadFilterTables(
|
||||
prev,
|
||||
allTables satisfies FilterTableInfo[]
|
||||
);
|
||||
|
||||
const currentTableIds = filterByTableIds.tableIds || [];
|
||||
const newTableIds = currentTableIds.filter(
|
||||
(id) => !tableIdsToRemovoe.includes(id)
|
||||
);
|
||||
|
||||
return reduceFilter(
|
||||
{
|
||||
...filterByTableIds,
|
||||
tableIds: newTableIds,
|
||||
},
|
||||
allTables satisfies FilterTableInfo[],
|
||||
{
|
||||
databaseWithSchemas:
|
||||
databasesWithSchemas.includes(databaseType),
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
[allTables, databaseType]
|
||||
);
|
||||
|
||||
const eventConsumer = useCallback(
|
||||
(event: ChartDBEvent) => {
|
||||
if (!hasActiveFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.action === 'add_tables') {
|
||||
addTablesToFilter({
|
||||
tableIds: event.data.tables.map((table) => table.id),
|
||||
});
|
||||
}
|
||||
},
|
||||
[hasActiveFilter, addTablesToFilter]
|
||||
);
|
||||
|
||||
events.useSubscription(eventConsumer);
|
||||
|
||||
const value: DiagramFilterContext = {
|
||||
loading,
|
||||
filter,
|
||||
clearSchemaIdsFilter: clearSchemaIds,
|
||||
setTableIdsFilterEmpty: setTableIdsEmpty,
|
||||
clearTableIdsFilter: clearTableIds,
|
||||
resetFilter,
|
||||
toggleSchemaFilter,
|
||||
toggleTableFilter,
|
||||
addSchemaToFilter,
|
||||
hasActiveFilter,
|
||||
schemasDisplayed,
|
||||
addTablesToFilter,
|
||||
removeTablesFromFilter,
|
||||
};
|
||||
|
||||
return (
|
||||
<diagramFilterContext.Provider value={value}>
|
||||
{children}
|
||||
</diagramFilterContext.Provider>
|
||||
);
|
||||
};
|
||||
4
src/context/diagram-filter-context/use-diagram-filter.ts
Normal file
4
src/context/diagram-filter-context/use-diagram-filter.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { useContext } from 'react';
|
||||
import { diagramFilterContext } from './diagram-filter-context';
|
||||
|
||||
export const useDiagramFilter = () => useContext(diagramFilterContext);
|
||||
@@ -32,14 +32,20 @@ export interface DiffContext {
|
||||
originalDiagram: Diagram | null;
|
||||
diffMap: DiffMap;
|
||||
hasDiff: boolean;
|
||||
isSummaryOnly: boolean;
|
||||
|
||||
calculateDiff: ({
|
||||
diagram,
|
||||
newDiagram,
|
||||
options,
|
||||
}: {
|
||||
diagram: Diagram;
|
||||
newDiagram: Diagram;
|
||||
options?: {
|
||||
summaryOnly?: boolean;
|
||||
};
|
||||
}) => void;
|
||||
resetDiff: () => void;
|
||||
|
||||
// table diff
|
||||
checkIfTableHasChange: ({ tableId }: { tableId: string }) => boolean;
|
||||
@@ -60,6 +66,15 @@ export interface DiffContext {
|
||||
checkIfNewField: ({ fieldId }: { fieldId: string }) => boolean;
|
||||
getFieldNewName: ({ fieldId }: { fieldId: string }) => string | null;
|
||||
getFieldNewType: ({ fieldId }: { fieldId: string }) => DataType | null;
|
||||
getFieldNewPrimaryKey: ({ fieldId }: { fieldId: string }) => boolean | null;
|
||||
getFieldNewNullable: ({ fieldId }: { fieldId: string }) => boolean | null;
|
||||
getFieldNewCharacterMaximumLength: ({
|
||||
fieldId,
|
||||
}: {
|
||||
fieldId: string;
|
||||
}) => string | null;
|
||||
getFieldNewScale: ({ fieldId }: { fieldId: string }) => number | null;
|
||||
getFieldNewPrecision: ({ fieldId }: { fieldId: string }) => number | null;
|
||||
|
||||
// relationship diff
|
||||
checkIfNewRelationship: ({
|
||||
|
||||
@@ -6,7 +6,10 @@ import type {
|
||||
} from './diff-context';
|
||||
import { diffContext } from './diff-context';
|
||||
|
||||
import { generateDiff, getDiffMapKey } from './diff-check/diff-check';
|
||||
import {
|
||||
generateDiff,
|
||||
getDiffMapKey,
|
||||
} from '@/lib/domain/diff/diff-check/diff-check';
|
||||
import type { Diagram } from '@/lib/domain/diagram';
|
||||
import { useEventEmitter } from 'ahooks';
|
||||
import type { DBField } from '@/lib/domain/db-field';
|
||||
@@ -29,6 +32,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
||||
const [fieldsChanged, setFieldsChanged] = React.useState<
|
||||
Map<string, boolean>
|
||||
>(new Map<string, boolean>());
|
||||
const [isSummaryOnly, setIsSummaryOnly] = React.useState<boolean>(false);
|
||||
|
||||
const events = useEventEmitter<DiffEvent>();
|
||||
|
||||
@@ -124,7 +128,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
||||
);
|
||||
|
||||
const calculateDiff: DiffContext['calculateDiff'] = useCallback(
|
||||
({ diagram, newDiagram: newDiagramArg }) => {
|
||||
({ diagram, newDiagram: newDiagramArg, options }) => {
|
||||
const {
|
||||
diffMap: newDiffs,
|
||||
changedTables: newChangedTables,
|
||||
@@ -136,6 +140,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
||||
setFieldsChanged(newChangedFields);
|
||||
setNewDiagram(newDiagramArg);
|
||||
setOriginalDiagram(diagram);
|
||||
setIsSummaryOnly(options?.summaryOnly ?? false);
|
||||
|
||||
events.emit({
|
||||
action: 'diff_calculated',
|
||||
@@ -302,6 +307,117 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
||||
[diffMap]
|
||||
);
|
||||
|
||||
const getFieldNewPrimaryKey = useCallback<
|
||||
DiffContext['getFieldNewPrimaryKey']
|
||||
>(
|
||||
({ fieldId }) => {
|
||||
const fieldKey = getDiffMapKey({
|
||||
diffObject: 'field',
|
||||
objectId: fieldId,
|
||||
attribute: 'primaryKey',
|
||||
});
|
||||
|
||||
if (diffMap.has(fieldKey)) {
|
||||
const diff = diffMap.get(fieldKey);
|
||||
|
||||
if (diff?.type === 'changed') {
|
||||
return diff.newValue as boolean;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
[diffMap]
|
||||
);
|
||||
|
||||
const getFieldNewNullable = useCallback<DiffContext['getFieldNewNullable']>(
|
||||
({ fieldId }) => {
|
||||
const fieldKey = getDiffMapKey({
|
||||
diffObject: 'field',
|
||||
objectId: fieldId,
|
||||
attribute: 'nullable',
|
||||
});
|
||||
|
||||
if (diffMap.has(fieldKey)) {
|
||||
const diff = diffMap.get(fieldKey);
|
||||
|
||||
if (diff?.type === 'changed') {
|
||||
return diff.newValue as boolean;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
[diffMap]
|
||||
);
|
||||
|
||||
const getFieldNewCharacterMaximumLength = useCallback<
|
||||
DiffContext['getFieldNewCharacterMaximumLength']
|
||||
>(
|
||||
({ fieldId }) => {
|
||||
const fieldKey = getDiffMapKey({
|
||||
diffObject: 'field',
|
||||
objectId: fieldId,
|
||||
attribute: 'characterMaximumLength',
|
||||
});
|
||||
|
||||
if (diffMap.has(fieldKey)) {
|
||||
const diff = diffMap.get(fieldKey);
|
||||
|
||||
if (diff?.type === 'changed') {
|
||||
return diff.newValue as string;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
[diffMap]
|
||||
);
|
||||
|
||||
const getFieldNewScale = useCallback<DiffContext['getFieldNewScale']>(
|
||||
({ fieldId }) => {
|
||||
const fieldKey = getDiffMapKey({
|
||||
diffObject: 'field',
|
||||
objectId: fieldId,
|
||||
attribute: 'scale',
|
||||
});
|
||||
|
||||
if (diffMap.has(fieldKey)) {
|
||||
const diff = diffMap.get(fieldKey);
|
||||
|
||||
if (diff?.type === 'changed') {
|
||||
return diff.newValue as number;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
[diffMap]
|
||||
);
|
||||
|
||||
const getFieldNewPrecision = useCallback<
|
||||
DiffContext['getFieldNewPrecision']
|
||||
>(
|
||||
({ fieldId }) => {
|
||||
const fieldKey = getDiffMapKey({
|
||||
diffObject: 'field',
|
||||
objectId: fieldId,
|
||||
attribute: 'precision',
|
||||
});
|
||||
|
||||
if (diffMap.has(fieldKey)) {
|
||||
const diff = diffMap.get(fieldKey);
|
||||
|
||||
if (diff?.type === 'changed') {
|
||||
return diff.newValue as number;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
[diffMap]
|
||||
);
|
||||
|
||||
const checkIfNewRelationship = useCallback<
|
||||
DiffContext['checkIfNewRelationship']
|
||||
>(
|
||||
@@ -336,6 +452,15 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
||||
[diffMap]
|
||||
);
|
||||
|
||||
const resetDiff = useCallback<DiffContext['resetDiff']>(() => {
|
||||
setDiffMap(new Map<string, ChartDBDiff>());
|
||||
setTablesChanged(new Map<string, boolean>());
|
||||
setFieldsChanged(new Map<string, boolean>());
|
||||
setNewDiagram(null);
|
||||
setOriginalDiagram(null);
|
||||
setIsSummaryOnly(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<diffContext.Provider
|
||||
value={{
|
||||
@@ -343,8 +468,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
||||
originalDiagram,
|
||||
diffMap,
|
||||
hasDiff: diffMap.size > 0,
|
||||
isSummaryOnly,
|
||||
|
||||
calculateDiff,
|
||||
resetDiff,
|
||||
|
||||
// table diff
|
||||
getTableNewName,
|
||||
@@ -359,6 +486,11 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
||||
checkIfNewField,
|
||||
getFieldNewName,
|
||||
getFieldNewType,
|
||||
getFieldNewPrimaryKey,
|
||||
getFieldNewNullable,
|
||||
getFieldNewCharacterMaximumLength,
|
||||
getFieldNewScale,
|
||||
getFieldNewPrecision,
|
||||
|
||||
// relationship diff
|
||||
checkIfNewRelationship,
|
||||
|
||||
@@ -8,6 +8,7 @@ export enum KeyboardShortcutAction {
|
||||
TOGGLE_SIDE_PANEL = 'toggle_side_panel',
|
||||
SHOW_ALL = 'show_all',
|
||||
TOGGLE_THEME = 'toggle_theme',
|
||||
TOGGLE_FILTER = 'toggle_filter',
|
||||
}
|
||||
|
||||
export interface KeyboardShortcut {
|
||||
@@ -71,6 +72,13 @@ export const keyboardShortcuts: Record<
|
||||
keyCombinationMac: 'meta+m',
|
||||
keyCombinationWin: 'ctrl+m',
|
||||
},
|
||||
[KeyboardShortcutAction.TOGGLE_FILTER]: {
|
||||
action: KeyboardShortcutAction.TOGGLE_FILTER,
|
||||
keyCombinationLabelMac: '⌘F',
|
||||
keyCombinationLabelWin: 'Ctrl+F',
|
||||
keyCombinationMac: 'meta+f',
|
||||
keyCombinationWin: 'ctrl+f',
|
||||
},
|
||||
};
|
||||
|
||||
export interface KeyboardShortcutForOS {
|
||||
|
||||
@@ -2,9 +2,9 @@ import { emptyFn } from '@/lib/utils';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export type SidebarSection =
|
||||
| 'dbml'
|
||||
| 'tables'
|
||||
| 'relationships'
|
||||
| 'dependencies'
|
||||
| 'refs'
|
||||
| 'areas'
|
||||
| 'customTypes';
|
||||
|
||||
@@ -13,14 +13,16 @@ export interface LayoutContext {
|
||||
openTableFromSidebar: (tableId: string) => void;
|
||||
closeAllTablesInSidebar: () => void;
|
||||
|
||||
openedRelationshipInSidebar: string | undefined;
|
||||
openRelationshipFromSidebar: (relationshipId: string) => void;
|
||||
closeAllRelationshipsInSidebar: () => void;
|
||||
|
||||
openedDependencyInSidebar: string | undefined;
|
||||
openDependencyFromSidebar: (dependencyId: string) => void;
|
||||
closeAllDependenciesInSidebar: () => void;
|
||||
|
||||
openedRefInSidebar: string | undefined;
|
||||
openRefFromSidebar: (refId: string) => void;
|
||||
closeAllRefsInSidebar: () => void;
|
||||
|
||||
openedAreaInSidebar: string | undefined;
|
||||
openAreaFromSidebar: (areaId: string) => void;
|
||||
closeAllAreasInSidebar: () => void;
|
||||
@@ -36,24 +38,22 @@ export interface LayoutContext {
|
||||
hideSidePanel: () => void;
|
||||
showSidePanel: () => void;
|
||||
toggleSidePanel: () => void;
|
||||
|
||||
isSelectSchemaOpen: boolean;
|
||||
openSelectSchema: () => void;
|
||||
closeSelectSchema: () => void;
|
||||
}
|
||||
|
||||
export const layoutContext = createContext<LayoutContext>({
|
||||
openedTableInSidebar: undefined,
|
||||
selectedSidebarSection: 'tables',
|
||||
|
||||
openedRelationshipInSidebar: undefined,
|
||||
openRelationshipFromSidebar: emptyFn,
|
||||
closeAllRelationshipsInSidebar: emptyFn,
|
||||
|
||||
openedDependencyInSidebar: undefined,
|
||||
openDependencyFromSidebar: emptyFn,
|
||||
closeAllDependenciesInSidebar: emptyFn,
|
||||
|
||||
openedRefInSidebar: undefined,
|
||||
openRefFromSidebar: emptyFn,
|
||||
closeAllRefsInSidebar: emptyFn,
|
||||
|
||||
openedAreaInSidebar: undefined,
|
||||
openAreaFromSidebar: emptyFn,
|
||||
closeAllAreasInSidebar: emptyFn,
|
||||
@@ -70,8 +70,4 @@ export const layoutContext = createContext<LayoutContext>({
|
||||
hideSidePanel: emptyFn,
|
||||
showSidePanel: emptyFn,
|
||||
toggleSidePanel: emptyFn,
|
||||
|
||||
isSelectSchemaOpen: false,
|
||||
openSelectSchema: emptyFn,
|
||||
closeSelectSchema: emptyFn,
|
||||
});
|
||||
|
||||
@@ -10,10 +10,9 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
||||
const [openedTableInSidebar, setOpenedTableInSidebar] = React.useState<
|
||||
string | undefined
|
||||
>();
|
||||
const [openedRelationshipInSidebar, setOpenedRelationshipInSidebar] =
|
||||
React.useState<string | undefined>();
|
||||
const [openedDependencyInSidebar, setOpenedDependencyInSidebar] =
|
||||
React.useState<string | undefined>();
|
||||
const [openedRefInSidebar, setOpenedRefInSidebar] = React.useState<
|
||||
string | undefined
|
||||
>();
|
||||
const [openedAreaInSidebar, setOpenedAreaInSidebar] = React.useState<
|
||||
string | undefined
|
||||
>();
|
||||
@@ -23,17 +22,18 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
||||
React.useState<SidebarSection>('tables');
|
||||
const [isSidePanelShowed, setIsSidePanelShowed] =
|
||||
React.useState<boolean>(isDesktop);
|
||||
const [isSelectSchemaOpen, setIsSelectSchemaOpen] =
|
||||
React.useState<boolean>(false);
|
||||
|
||||
const closeAllTablesInSidebar: LayoutContext['closeAllTablesInSidebar'] =
|
||||
() => setOpenedTableInSidebar('');
|
||||
|
||||
const closeAllRelationshipsInSidebar: LayoutContext['closeAllRelationshipsInSidebar'] =
|
||||
() => setOpenedRelationshipInSidebar('');
|
||||
() => setOpenedRefInSidebar('');
|
||||
|
||||
const closeAllDependenciesInSidebar: LayoutContext['closeAllDependenciesInSidebar'] =
|
||||
() => setOpenedDependencyInSidebar('');
|
||||
() => setOpenedRefInSidebar('');
|
||||
|
||||
const closeAllRefsInSidebar: LayoutContext['closeAllRefsInSidebar'] = () =>
|
||||
setOpenedRefInSidebar('');
|
||||
|
||||
const closeAllAreasInSidebar: LayoutContext['closeAllAreasInSidebar'] =
|
||||
() => setOpenedAreaInSidebar('');
|
||||
@@ -62,17 +62,23 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
||||
const openRelationshipFromSidebar: LayoutContext['openRelationshipFromSidebar'] =
|
||||
(relationshipId) => {
|
||||
showSidePanel();
|
||||
setSelectedSidebarSection('relationships');
|
||||
setOpenedRelationshipInSidebar(relationshipId);
|
||||
setSelectedSidebarSection('refs');
|
||||
setOpenedRefInSidebar(relationshipId);
|
||||
};
|
||||
|
||||
const openDependencyFromSidebar: LayoutContext['openDependencyFromSidebar'] =
|
||||
(dependencyId) => {
|
||||
showSidePanel();
|
||||
setSelectedSidebarSection('dependencies');
|
||||
setOpenedDependencyInSidebar(dependencyId);
|
||||
setSelectedSidebarSection('refs');
|
||||
setOpenedRefInSidebar(dependencyId);
|
||||
};
|
||||
|
||||
const openRefFromSidebar: LayoutContext['openRefFromSidebar'] = (refId) => {
|
||||
showSidePanel();
|
||||
setSelectedSidebarSection('refs');
|
||||
setOpenedRefInSidebar(refId);
|
||||
};
|
||||
|
||||
const openAreaFromSidebar: LayoutContext['openAreaFromSidebar'] = (
|
||||
areaId
|
||||
) => {
|
||||
@@ -88,11 +94,6 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
||||
setOpenedTableInSidebar(customTypeId);
|
||||
};
|
||||
|
||||
const openSelectSchema: LayoutContext['openSelectSchema'] = () =>
|
||||
setIsSelectSchemaOpen(true);
|
||||
|
||||
const closeSelectSchema: LayoutContext['closeSelectSchema'] = () =>
|
||||
setIsSelectSchemaOpen(false);
|
||||
return (
|
||||
<layoutContext.Provider
|
||||
value={{
|
||||
@@ -100,7 +101,6 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
||||
selectedSidebarSection,
|
||||
openTableFromSidebar,
|
||||
selectSidebarSection: setSelectedSidebarSection,
|
||||
openedRelationshipInSidebar,
|
||||
openRelationshipFromSidebar,
|
||||
closeAllTablesInSidebar,
|
||||
closeAllRelationshipsInSidebar,
|
||||
@@ -108,12 +108,11 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
||||
hideSidePanel,
|
||||
showSidePanel,
|
||||
toggleSidePanel,
|
||||
isSelectSchemaOpen,
|
||||
openSelectSchema,
|
||||
closeSelectSchema,
|
||||
openedDependencyInSidebar,
|
||||
openDependencyFromSidebar,
|
||||
closeAllDependenciesInSidebar,
|
||||
openedRefInSidebar,
|
||||
openRefFromSidebar,
|
||||
closeAllRefsInSidebar,
|
||||
openedAreaInSidebar,
|
||||
openAreaFromSidebar,
|
||||
closeAllAreasInSidebar,
|
||||
|
||||
@@ -4,8 +4,6 @@ import type { Theme } from '../theme-context/theme-context';
|
||||
|
||||
export type ScrollAction = 'pan' | 'zoom';
|
||||
|
||||
export type SchemasFilter = Record<string, string[]>;
|
||||
|
||||
export interface LocalConfigContext {
|
||||
theme: Theme;
|
||||
setTheme: (theme: Theme) => void;
|
||||
@@ -13,16 +11,14 @@ export interface LocalConfigContext {
|
||||
scrollAction: ScrollAction;
|
||||
setScrollAction: (action: ScrollAction) => void;
|
||||
|
||||
schemasFilter: SchemasFilter;
|
||||
setSchemasFilter: React.Dispatch<React.SetStateAction<SchemasFilter>>;
|
||||
showDBViews: boolean;
|
||||
setShowDBViews: (showViews: boolean) => void;
|
||||
|
||||
showCardinality: boolean;
|
||||
setShowCardinality: (showCardinality: boolean) => void;
|
||||
|
||||
hideMultiSchemaNotification: boolean;
|
||||
setHideMultiSchemaNotification: (
|
||||
hideMultiSchemaNotification: boolean
|
||||
) => void;
|
||||
showFieldAttributes: boolean;
|
||||
setShowFieldAttributes: (showFieldAttributes: boolean) => void;
|
||||
|
||||
githubRepoOpened: boolean;
|
||||
setGithubRepoOpened: (githubRepoOpened: boolean) => void;
|
||||
@@ -30,9 +26,6 @@ export interface LocalConfigContext {
|
||||
starUsDialogLastOpen: number;
|
||||
setStarUsDialogLastOpen: (lastOpen: number) => void;
|
||||
|
||||
showDependenciesOnCanvas: boolean;
|
||||
setShowDependenciesOnCanvas: (showDependenciesOnCanvas: boolean) => void;
|
||||
|
||||
showMiniMapOnCanvas: boolean;
|
||||
setShowMiniMapOnCanvas: (showMiniMapOnCanvas: boolean) => void;
|
||||
}
|
||||
@@ -44,14 +37,14 @@ export const LocalConfigContext = createContext<LocalConfigContext>({
|
||||
scrollAction: 'pan',
|
||||
setScrollAction: emptyFn,
|
||||
|
||||
schemasFilter: {},
|
||||
setSchemasFilter: emptyFn,
|
||||
showDBViews: false,
|
||||
setShowDBViews: emptyFn,
|
||||
|
||||
showCardinality: true,
|
||||
setShowCardinality: emptyFn,
|
||||
|
||||
hideMultiSchemaNotification: false,
|
||||
setHideMultiSchemaNotification: emptyFn,
|
||||
showFieldAttributes: true,
|
||||
setShowFieldAttributes: emptyFn,
|
||||
|
||||
githubRepoOpened: false,
|
||||
setGithubRepoOpened: emptyFn,
|
||||
@@ -59,9 +52,6 @@ export const LocalConfigContext = createContext<LocalConfigContext>({
|
||||
starUsDialogLastOpen: 0,
|
||||
setStarUsDialogLastOpen: emptyFn,
|
||||
|
||||
showDependenciesOnCanvas: false,
|
||||
setShowDependenciesOnCanvas: emptyFn,
|
||||
|
||||
showMiniMapOnCanvas: false,
|
||||
setShowMiniMapOnCanvas: emptyFn,
|
||||
});
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import type { SchemasFilter, ScrollAction } from './local-config-context';
|
||||
import type { ScrollAction } from './local-config-context';
|
||||
import { LocalConfigContext } from './local-config-context';
|
||||
import type { Theme } from '../theme-context/theme-context';
|
||||
|
||||
const themeKey = 'theme';
|
||||
const scrollActionKey = 'scroll_action';
|
||||
const schemasFilterKey = 'schemas_filter';
|
||||
const showCardinalityKey = 'show_cardinality';
|
||||
const hideMultiSchemaNotificationKey = 'hide_multi_schema_notification';
|
||||
const showFieldAttributesKey = 'show_field_attributes';
|
||||
const githubRepoOpenedKey = 'github_repo_opened';
|
||||
const starUsDialogLastOpenKey = 'star_us_dialog_last_open';
|
||||
const showDependenciesOnCanvasKey = 'show_dependencies_on_canvas';
|
||||
const showMiniMapOnCanvasKey = 'show_minimap_on_canvas';
|
||||
const showDBViewsKey = 'show_db_views';
|
||||
|
||||
export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
children,
|
||||
@@ -24,20 +23,17 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
(localStorage.getItem(scrollActionKey) as ScrollAction) || 'pan'
|
||||
);
|
||||
|
||||
const [schemasFilter, setSchemasFilter] = React.useState<SchemasFilter>(
|
||||
JSON.parse(
|
||||
localStorage.getItem(schemasFilterKey) || '{}'
|
||||
) as SchemasFilter
|
||||
const [showDBViews, setShowDBViews] = React.useState<boolean>(
|
||||
(localStorage.getItem(showDBViewsKey) || 'false') === 'true'
|
||||
);
|
||||
|
||||
const [showCardinality, setShowCardinality] = React.useState<boolean>(
|
||||
(localStorage.getItem(showCardinalityKey) || 'true') === 'true'
|
||||
);
|
||||
|
||||
const [hideMultiSchemaNotification, setHideMultiSchemaNotification] =
|
||||
const [showFieldAttributes, setShowFieldAttributes] =
|
||||
React.useState<boolean>(
|
||||
(localStorage.getItem(hideMultiSchemaNotificationKey) ||
|
||||
'false') === 'true'
|
||||
(localStorage.getItem(showFieldAttributesKey) || 'true') === 'true'
|
||||
);
|
||||
|
||||
const [githubRepoOpened, setGithubRepoOpened] = React.useState<boolean>(
|
||||
@@ -49,12 +45,6 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
parseInt(localStorage.getItem(starUsDialogLastOpenKey) || '0')
|
||||
);
|
||||
|
||||
const [showDependenciesOnCanvas, setShowDependenciesOnCanvas] =
|
||||
React.useState<boolean>(
|
||||
(localStorage.getItem(showDependenciesOnCanvasKey) || 'false') ===
|
||||
'true'
|
||||
);
|
||||
|
||||
const [showMiniMapOnCanvas, setShowMiniMapOnCanvas] =
|
||||
React.useState<boolean>(
|
||||
(localStorage.getItem(showMiniMapOnCanvasKey) || 'true') === 'true'
|
||||
@@ -71,13 +61,6 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
localStorage.setItem(githubRepoOpenedKey, githubRepoOpened.toString());
|
||||
}, [githubRepoOpened]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(
|
||||
hideMultiSchemaNotificationKey,
|
||||
hideMultiSchemaNotification.toString()
|
||||
);
|
||||
}, [hideMultiSchemaNotification]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(themeKey, theme);
|
||||
}, [theme]);
|
||||
@@ -87,20 +70,13 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
}, [scrollAction]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(schemasFilterKey, JSON.stringify(schemasFilter));
|
||||
}, [schemasFilter]);
|
||||
localStorage.setItem(showDBViewsKey, showDBViews.toString());
|
||||
}, [showDBViews]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(showCardinalityKey, showCardinality.toString());
|
||||
}, [showCardinality]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(
|
||||
showDependenciesOnCanvasKey,
|
||||
showDependenciesOnCanvas.toString()
|
||||
);
|
||||
}, [showDependenciesOnCanvas]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(
|
||||
showMiniMapOnCanvasKey,
|
||||
@@ -115,18 +91,16 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
setTheme,
|
||||
scrollAction,
|
||||
setScrollAction,
|
||||
schemasFilter,
|
||||
setSchemasFilter,
|
||||
showDBViews,
|
||||
setShowDBViews,
|
||||
showCardinality,
|
||||
setShowCardinality,
|
||||
hideMultiSchemaNotification,
|
||||
setHideMultiSchemaNotification,
|
||||
showFieldAttributes,
|
||||
setShowFieldAttributes,
|
||||
setGithubRepoOpened,
|
||||
githubRepoOpened,
|
||||
starUsDialogLastOpen,
|
||||
setStarUsDialogLastOpen,
|
||||
showDependenciesOnCanvas,
|
||||
setShowDependenciesOnCanvas,
|
||||
showMiniMapOnCanvas,
|
||||
setShowMiniMapOnCanvas,
|
||||
}}
|
||||
|
||||
@@ -7,12 +7,21 @@ import type { ChartDBConfig } from '@/lib/domain/config';
|
||||
import type { DBDependency } from '@/lib/domain/db-dependency';
|
||||
import type { Area } from '@/lib/domain/area';
|
||||
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
||||
import type { DiagramFilter } from '@/lib/domain/diagram-filter/diagram-filter';
|
||||
|
||||
export interface StorageContext {
|
||||
// Config operations
|
||||
getConfig: () => Promise<ChartDBConfig | undefined>;
|
||||
updateConfig: (config: Partial<ChartDBConfig>) => Promise<void>;
|
||||
|
||||
// Diagram filter operations
|
||||
getDiagramFilter: (diagramId: string) => Promise<DiagramFilter | undefined>;
|
||||
updateDiagramFilter: (
|
||||
diagramId: string,
|
||||
filter: DiagramFilter
|
||||
) => Promise<void>;
|
||||
deleteDiagramFilter: (diagramId: string) => Promise<void>;
|
||||
|
||||
// Diagram operations
|
||||
addDiagram: (params: { diagram: Diagram }) => Promise<void>;
|
||||
listDiagrams: (options?: {
|
||||
@@ -132,6 +141,10 @@ export const storageInitialValue: StorageContext = {
|
||||
getConfig: emptyFn,
|
||||
updateConfig: emptyFn,
|
||||
|
||||
getDiagramFilter: emptyFn,
|
||||
updateDiagramFilter: emptyFn,
|
||||
deleteDiagramFilter: emptyFn,
|
||||
|
||||
addDiagram: emptyFn,
|
||||
listDiagrams: emptyFn,
|
||||
getDiagram: emptyFn,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,7 @@
|
||||
import { createContext } from 'react';
|
||||
import { emptyFn } from '@/lib/utils';
|
||||
|
||||
export type Theme = 'light' | 'dark' | 'system';
|
||||
export type EffectiveTheme = Exclude<Theme, 'system'>;
|
||||
import type { Theme, EffectiveTheme } from '@/lib/types';
|
||||
export type { Theme, EffectiveTheme };
|
||||
|
||||
export interface ThemeContext {
|
||||
theme: Theme;
|
||||
|
||||
@@ -48,6 +48,7 @@ export const ThemeProvider: React.FC<React.PropsWithChildren> = ({
|
||||
handleThemeToggle,
|
||||
{
|
||||
preventDefault: true,
|
||||
enableOnFormTags: true,
|
||||
},
|
||||
[handleThemeToggle]
|
||||
);
|
||||
|
||||
@@ -35,7 +35,22 @@ import type { OnChange } from '@monaco-editor/react';
|
||||
import { useDebounce } from '@/hooks/use-debounce-v2';
|
||||
import { InstructionsSection } from './instructions-section/instructions-section';
|
||||
import { parseSQLError } from '@/lib/data/sql-import';
|
||||
import type { editor } from 'monaco-editor';
|
||||
import type { editor, IDisposable } from 'monaco-editor';
|
||||
import { waitFor } from '@/lib/utils';
|
||||
import {
|
||||
validateSQL,
|
||||
type ValidationResult,
|
||||
} from '@/lib/data/sql-import/sql-validator';
|
||||
import { SQLValidationStatus } from './sql-validation-status';
|
||||
|
||||
const calculateContentSizeMB = (content: string): number => {
|
||||
return content.length / (1024 * 1024); // Convert to MB
|
||||
};
|
||||
|
||||
const calculateIsLargeFile = (content: string): boolean => {
|
||||
const contentSizeMB = calculateContentSizeMB(content);
|
||||
return contentSizeMB > 2; // Consider large if over 2MB
|
||||
};
|
||||
|
||||
const errorScriptOutputMessage =
|
||||
'Invalid JSON. Please correct it or contact us at support@chartdb.io for help.';
|
||||
@@ -117,6 +132,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
const { effectiveTheme } = useTheme();
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
||||
const pasteDisposableRef = useRef<IDisposable | null>(null);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { isSm: isDesktop } = useBreakpoint('sm');
|
||||
@@ -124,6 +140,11 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
const [showCheckJsonButton, setShowCheckJsonButton] = useState(false);
|
||||
const [isCheckingJson, setIsCheckingJson] = useState(false);
|
||||
const [showSSMSInfoDialog, setShowSSMSInfoDialog] = useState(false);
|
||||
const [sqlValidation, setSqlValidation] = useState<ValidationResult | null>(
|
||||
null
|
||||
);
|
||||
const [isAutoFixing, setIsAutoFixing] = useState(false);
|
||||
const [showAutoFixButton, setShowAutoFixButton] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setScriptResult('');
|
||||
@@ -134,11 +155,33 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
// Check if the ddl is valid
|
||||
useEffect(() => {
|
||||
if (importMethod !== 'ddl') {
|
||||
setSqlValidation(null);
|
||||
setShowAutoFixButton(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scriptResult.trim()) return;
|
||||
if (!scriptResult.trim()) {
|
||||
setSqlValidation(null);
|
||||
setShowAutoFixButton(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// First run our validation based on database type
|
||||
const validation = validateSQL(scriptResult, databaseType);
|
||||
setSqlValidation(validation);
|
||||
|
||||
// If we have auto-fixable errors, show the auto-fix button
|
||||
if (validation.fixedSQL && validation.errors.length > 0) {
|
||||
setShowAutoFixButton(true);
|
||||
// Don't try to parse invalid SQL
|
||||
setErrorMessage('SQL contains syntax errors');
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide auto-fix button if no fixes available
|
||||
setShowAutoFixButton(false);
|
||||
|
||||
// Validate the SQL (either original or already fixed)
|
||||
parseSQLError({
|
||||
sqlContent: scriptResult,
|
||||
sourceDatabaseType: databaseType,
|
||||
@@ -184,8 +227,44 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
}
|
||||
}, [errorMessage.length, onImport, scriptResult]);
|
||||
|
||||
const handleAutoFix = useCallback(() => {
|
||||
if (sqlValidation?.fixedSQL) {
|
||||
setIsAutoFixing(true);
|
||||
setShowAutoFixButton(false);
|
||||
setErrorMessage('');
|
||||
|
||||
// Apply the fix with a delay so user sees the fixing message
|
||||
setTimeout(() => {
|
||||
setScriptResult(sqlValidation.fixedSQL!);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsAutoFixing(false);
|
||||
}, 100);
|
||||
}, 1000);
|
||||
}
|
||||
}, [sqlValidation, setScriptResult]);
|
||||
|
||||
const handleErrorClick = useCallback((line: number) => {
|
||||
if (editorRef.current) {
|
||||
// Set cursor to the error line
|
||||
editorRef.current.setPosition({ lineNumber: line, column: 1 });
|
||||
editorRef.current.revealLineInCenter(line);
|
||||
editorRef.current.focus();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const formatEditor = useCallback(() => {
|
||||
if (editorRef.current) {
|
||||
const model = editorRef.current.getModel();
|
||||
if (model) {
|
||||
const content = model.getValue();
|
||||
|
||||
// Skip formatting for large files (> 2MB)
|
||||
if (calculateIsLargeFile(content)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
editorRef.current
|
||||
?.getAction('editor.action.formatDocument')
|
||||
@@ -211,7 +290,8 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
const handleCheckJson = useCallback(async () => {
|
||||
setIsCheckingJson(true);
|
||||
|
||||
const fixedJson = await fixMetadataJson(scriptResult);
|
||||
await waitFor(1000);
|
||||
const fixedJson = fixMetadataJson(scriptResult);
|
||||
|
||||
if (isStringMetadataJson(fixedJson)) {
|
||||
setScriptResult(fixedJson);
|
||||
@@ -227,37 +307,69 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
setIsCheckingJson(false);
|
||||
}, [scriptResult, setScriptResult, formatEditor]);
|
||||
|
||||
const detectAndSetImportMethod = useCallback(() => {
|
||||
const content = editorRef.current?.getValue();
|
||||
if (content && content.trim()) {
|
||||
const detectedType = detectContentType(content);
|
||||
if (detectedType && detectedType !== importMethod) {
|
||||
setImportMethod(detectedType);
|
||||
}
|
||||
}
|
||||
}, [setImportMethod, importMethod]);
|
||||
|
||||
const [editorDidMount, setEditorDidMount] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (editorRef.current && editorDidMount) {
|
||||
editorRef.current.onDidPaste(() => {
|
||||
setTimeout(() => {
|
||||
editorRef.current
|
||||
?.getAction('editor.action.formatDocument')
|
||||
?.run();
|
||||
}, 0);
|
||||
setTimeout(detectAndSetImportMethod, 0);
|
||||
});
|
||||
}
|
||||
}, [detectAndSetImportMethod, editorDidMount]);
|
||||
// Cleanup paste handler on unmount
|
||||
return () => {
|
||||
if (pasteDisposableRef.current) {
|
||||
pasteDisposableRef.current.dispose();
|
||||
pasteDisposableRef.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleEditorDidMount = useCallback(
|
||||
(editor: editor.IStandaloneCodeEditor) => {
|
||||
editorRef.current = editor;
|
||||
setEditorDidMount(true);
|
||||
|
||||
// Cleanup previous disposable if it exists
|
||||
if (pasteDisposableRef.current) {
|
||||
pasteDisposableRef.current.dispose();
|
||||
pasteDisposableRef.current = null;
|
||||
}
|
||||
|
||||
// Add paste handler for all modes
|
||||
const disposable = editor.onDidPaste(() => {
|
||||
const model = editor.getModel();
|
||||
if (!model) return;
|
||||
|
||||
const content = model.getValue();
|
||||
|
||||
// Skip formatting for large files (> 2MB) to prevent browser freezing
|
||||
const isLargeFile = calculateIsLargeFile(content);
|
||||
|
||||
// First, detect content type to determine if we should switch modes
|
||||
const detectedType = detectContentType(content);
|
||||
if (detectedType && detectedType !== importMethod) {
|
||||
// Switch to the detected mode immediately
|
||||
setImportMethod(detectedType);
|
||||
|
||||
// Only format if it's JSON (query mode) AND file is not too large
|
||||
if (detectedType === 'query' && !isLargeFile) {
|
||||
// For JSON mode, format after a short delay
|
||||
setTimeout(() => {
|
||||
editor
|
||||
.getAction('editor.action.formatDocument')
|
||||
?.run();
|
||||
}, 100);
|
||||
}
|
||||
// For DDL mode, do NOT format as it can break the SQL
|
||||
} else {
|
||||
// Content type didn't change, apply formatting based on current mode
|
||||
if (importMethod === 'query' && !isLargeFile) {
|
||||
// Only format JSON content if not too large
|
||||
setTimeout(() => {
|
||||
editor
|
||||
.getAction('editor.action.formatDocument')
|
||||
?.run();
|
||||
}, 100);
|
||||
}
|
||||
// For DDL mode or large files, do NOT format
|
||||
}
|
||||
});
|
||||
|
||||
pasteDisposableRef.current = disposable;
|
||||
},
|
||||
[]
|
||||
[importMethod, setImportMethod]
|
||||
);
|
||||
|
||||
const renderHeader = useCallback(() => {
|
||||
@@ -314,7 +426,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
: 'dbml-light'
|
||||
}
|
||||
options={{
|
||||
formatOnPaste: true,
|
||||
formatOnPaste: false, // Never format on paste - we handle it manually
|
||||
minimap: { enabled: false },
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true,
|
||||
@@ -343,10 +455,13 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
{errorMessage ? (
|
||||
<div className="mt-2 flex shrink-0 items-center gap-2">
|
||||
<p className="text-xs text-red-700">{errorMessage}</p>
|
||||
</div>
|
||||
{errorMessage || (importMethod === 'ddl' && sqlValidation) ? (
|
||||
<SQLValidationStatus
|
||||
validation={sqlValidation}
|
||||
errorMessage={errorMessage}
|
||||
isAutoFixing={isAutoFixing}
|
||||
onErrorClick={handleErrorClick}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
),
|
||||
@@ -357,6 +472,9 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
effectiveTheme,
|
||||
debouncedHandleInputChange,
|
||||
handleEditorDidMount,
|
||||
sqlValidation,
|
||||
isAutoFixing,
|
||||
handleErrorClick,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -442,13 +560,28 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
)
|
||||
)}
|
||||
</Button>
|
||||
) : showAutoFixButton && importMethod === 'ddl' ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={handleAutoFix}
|
||||
disabled={isAutoFixing}
|
||||
className="bg-sky-600 text-white hover:bg-sky-700"
|
||||
>
|
||||
{isAutoFixing ? (
|
||||
<Spinner size="small" />
|
||||
) : (
|
||||
'Try auto-fix'
|
||||
)}
|
||||
</Button>
|
||||
) : keepDialogAfterImport ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
disabled={
|
||||
scriptResult.trim().length === 0 ||
|
||||
errorMessage.length > 0
|
||||
errorMessage.length > 0 ||
|
||||
isAutoFixing
|
||||
}
|
||||
onClick={handleImport}
|
||||
>
|
||||
@@ -461,7 +594,8 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
variant="default"
|
||||
disabled={
|
||||
scriptResult.trim().length === 0 ||
|
||||
errorMessage.length > 0
|
||||
errorMessage.length > 0 ||
|
||||
isAutoFixing
|
||||
}
|
||||
onClick={handleImport}
|
||||
>
|
||||
@@ -494,6 +628,10 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||
handleCheckJson,
|
||||
goBack,
|
||||
t,
|
||||
importMethod,
|
||||
isAutoFixing,
|
||||
showAutoFixButton,
|
||||
handleAutoFix,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
||||
179
src/dialogs/common/import-database/sql-validation-status.tsx
Normal file
179
src/dialogs/common/import-database/sql-validation-status.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { CheckCircle, AlertTriangle, MessageCircleWarning } from 'lucide-react';
|
||||
import { Alert, AlertDescription } from '@/components/alert/alert';
|
||||
import type { ValidationResult } from '@/lib/data/sql-import/sql-validator';
|
||||
import { Separator } from '@/components/separator/separator';
|
||||
import { ScrollArea } from '@/components/scroll-area/scroll-area';
|
||||
import { Spinner } from '@/components/spinner/spinner';
|
||||
|
||||
interface SQLValidationStatusProps {
|
||||
validation?: ValidationResult | null;
|
||||
errorMessage: string;
|
||||
isAutoFixing?: boolean;
|
||||
onErrorClick?: (line: number) => void;
|
||||
}
|
||||
|
||||
export const SQLValidationStatus: React.FC<SQLValidationStatusProps> = ({
|
||||
validation,
|
||||
errorMessage,
|
||||
isAutoFixing = false,
|
||||
onErrorClick,
|
||||
}) => {
|
||||
const hasErrors = useMemo(
|
||||
() => validation?.errors.length && validation.errors.length > 0,
|
||||
[validation?.errors]
|
||||
);
|
||||
const hasWarnings = useMemo(
|
||||
() => validation?.warnings && validation.warnings.length > 0,
|
||||
[validation?.warnings]
|
||||
);
|
||||
const wasAutoFixed = useMemo(
|
||||
() =>
|
||||
validation?.warnings?.some((w) =>
|
||||
w.message.includes('Auto-fixed')
|
||||
) || false,
|
||||
[validation?.warnings]
|
||||
);
|
||||
|
||||
if (!validation && !errorMessage && !isAutoFixing) return null;
|
||||
|
||||
if (isAutoFixing) {
|
||||
return (
|
||||
<>
|
||||
<Separator className="mb-1 mt-2" />
|
||||
<div className="rounded-md border border-sky-200 bg-sky-50 dark:border-sky-800 dark:bg-sky-950">
|
||||
<div className="space-y-3 p-3 pt-2 text-sky-700 dark:text-sky-300">
|
||||
<div className="flex items-start gap-2">
|
||||
<Spinner className="mt-0.5 size-4 shrink-0 text-sky-700 dark:text-sky-300" />
|
||||
<div className="flex-1 text-sm text-sky-700 dark:text-sky-300">
|
||||
Auto-fixing SQL syntax errors...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// If we have parser errors (errorMessage) after validation
|
||||
if (errorMessage && !hasErrors) {
|
||||
return (
|
||||
<>
|
||||
<Separator className="mb-1 mt-2" />
|
||||
<div className="mb-1 flex shrink-0 items-center gap-2">
|
||||
<p className="text-xs text-red-700">{errorMessage}</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separator className="mb-1 mt-2" />
|
||||
|
||||
{hasErrors ? (
|
||||
<div className="rounded-md border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950">
|
||||
<ScrollArea className="h-24">
|
||||
<div className="space-y-3 p-3 pt-2 text-red-700 dark:text-red-300">
|
||||
{validation?.errors
|
||||
.slice(0, 3)
|
||||
.map((error, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="flex items-start gap-2"
|
||||
>
|
||||
<MessageCircleWarning className="mt-0.5 size-4 shrink-0 text-red-700 dark:text-red-300" />
|
||||
<div className="flex-1 text-sm text-red-700 dark:text-red-300">
|
||||
<button
|
||||
onClick={() =>
|
||||
onErrorClick?.(error.line)
|
||||
}
|
||||
className="rounded font-medium underline hover:text-red-600 focus:outline-none focus:ring-1 focus:ring-red-500 dark:hover:text-red-200"
|
||||
type="button"
|
||||
>
|
||||
Line {error.line}
|
||||
</button>
|
||||
<span className="mx-1">:</span>
|
||||
<span className="text-xs">
|
||||
{error.message}
|
||||
</span>
|
||||
{error.suggestion && (
|
||||
<div className="mt-1 flex items-start gap-2">
|
||||
<span className="text-xs font-medium ">
|
||||
{error.suggestion}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{validation?.errors &&
|
||||
validation?.errors.length > 3 ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<MessageCircleWarning className="mt-0.5 size-4 shrink-0 text-red-700 dark:text-red-300" />
|
||||
<span className="text-xs font-medium">
|
||||
{validation.errors.length - 3} more
|
||||
error
|
||||
{validation.errors.length - 3 > 1
|
||||
? 's'
|
||||
: ''}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{wasAutoFixed && !hasErrors ? (
|
||||
<Alert className="border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950">
|
||||
<CheckCircle className="size-4 text-green-600 dark:text-green-400" />
|
||||
<AlertDescription className="text-sm text-green-700 dark:text-green-300">
|
||||
SQL syntax errors were automatically fixed. Your SQL is
|
||||
now ready to import.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
{hasWarnings && !hasErrors ? (
|
||||
<div className="rounded-md border border-sky-200 bg-sky-50 dark:border-sky-800 dark:bg-sky-950">
|
||||
<ScrollArea className="h-24">
|
||||
<div className="space-y-3 p-3 pt-2 text-sky-700 dark:text-sky-300">
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertTriangle className="mt-0.5 size-4 shrink-0 text-sky-700 dark:text-sky-300" />
|
||||
<div className="flex-1 text-sm text-sky-700 dark:text-sky-300">
|
||||
<div className="mb-1 font-medium">
|
||||
Import Info:
|
||||
</div>
|
||||
{validation?.warnings.map(
|
||||
(warning, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="ml-2 text-xs"
|
||||
>
|
||||
• {warning.message}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!hasErrors && !hasWarnings && !errorMessage && validation ? (
|
||||
<div className="rounded-md border border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950">
|
||||
<div className="space-y-3 p-3 pt-2 text-green-700 dark:text-green-300">
|
||||
<div className="flex items-start gap-2">
|
||||
<CheckCircle className="mt-0.5 size-4 shrink-0 text-green-700 dark:text-green-300" />
|
||||
<div className="flex-1 text-sm text-green-700 dark:text-green-300">
|
||||
SQL syntax validated successfully
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
2
src/dialogs/common/select-tables/constants.ts
Normal file
2
src/dialogs/common/select-tables/constants.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const MAX_TABLES_IN_DIAGRAM = 500;
|
||||
export const MAX_TABLES_WITHOUT_SHOWING_FILTER = 50;
|
||||
683
src/dialogs/common/select-tables/select-tables.tsx
Normal file
683
src/dialogs/common/select-tables/select-tables.tsx
Normal file
@@ -0,0 +1,683 @@
|
||||
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
||||
import { Button } from '@/components/button/button';
|
||||
import { Input } from '@/components/input/input';
|
||||
import { Search, AlertCircle, Check, X, View, Table } from 'lucide-react';
|
||||
import { Checkbox } from '@/components/checkbox/checkbox';
|
||||
import type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
||||
import { schemaNameToDomainSchemaName } from '@/lib/domain/db-schema';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogInternalContent,
|
||||
DialogTitle,
|
||||
} from '@/components/dialog/dialog';
|
||||
import type { SelectedTable } from '@/lib/data/import-metadata/filter-metadata';
|
||||
import { generateTableKey } from '@/lib/domain';
|
||||
import { Spinner } from '@/components/spinner/spinner';
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationItem,
|
||||
PaginationPrevious,
|
||||
PaginationNext,
|
||||
} from '@/components/pagination/pagination';
|
||||
import { MAX_TABLES_IN_DIAGRAM } from './constants';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface SelectTablesProps {
|
||||
databaseMetadata?: DatabaseMetadata;
|
||||
onImport: ({
|
||||
selectedTables,
|
||||
databaseMetadata,
|
||||
}: {
|
||||
selectedTables?: SelectedTable[];
|
||||
databaseMetadata?: DatabaseMetadata;
|
||||
}) => Promise<void>;
|
||||
onBack: () => void;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const TABLES_PER_PAGE = 10;
|
||||
|
||||
interface TableInfo {
|
||||
key: string;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
fullName: string;
|
||||
type: 'table' | 'view';
|
||||
}
|
||||
|
||||
export const SelectTables: React.FC<SelectTablesProps> = ({
|
||||
databaseMetadata,
|
||||
onImport,
|
||||
onBack,
|
||||
isLoading = false,
|
||||
}) => {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [showTables, setShowTables] = useState(true);
|
||||
const [showViews, setShowViews] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const [isImporting, setIsImporting] = useState(false);
|
||||
|
||||
// Prepare all tables and views with their metadata
|
||||
const allTables = useMemo(() => {
|
||||
const tables: TableInfo[] = [];
|
||||
|
||||
// Add regular tables
|
||||
databaseMetadata?.tables.forEach((table) => {
|
||||
const schema = schemaNameToDomainSchemaName(table.schema);
|
||||
const tableName = table.table;
|
||||
|
||||
const key = `table:${generateTableKey({ tableName, schemaName: schema })}`;
|
||||
|
||||
tables.push({
|
||||
key,
|
||||
schema,
|
||||
tableName,
|
||||
fullName: schema ? `${schema}.${tableName}` : tableName,
|
||||
type: 'table',
|
||||
});
|
||||
});
|
||||
|
||||
// Add views
|
||||
databaseMetadata?.views?.forEach((view) => {
|
||||
const schema = schemaNameToDomainSchemaName(view.schema);
|
||||
const viewName = view.view_name;
|
||||
|
||||
if (!viewName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = `view:${generateTableKey({
|
||||
tableName: viewName,
|
||||
schemaName: schema,
|
||||
})}`;
|
||||
|
||||
tables.push({
|
||||
key,
|
||||
schema,
|
||||
tableName: viewName,
|
||||
fullName:
|
||||
schema === 'default' ? viewName : `${schema}.${viewName}`,
|
||||
type: 'view',
|
||||
});
|
||||
});
|
||||
|
||||
return tables.sort((a, b) => a.fullName.localeCompare(b.fullName));
|
||||
}, [databaseMetadata?.tables, databaseMetadata?.views]);
|
||||
|
||||
// Count tables and views separately
|
||||
const tableCount = useMemo(
|
||||
() => allTables.filter((t) => t.type === 'table').length,
|
||||
[allTables]
|
||||
);
|
||||
const viewCount = useMemo(
|
||||
() => allTables.filter((t) => t.type === 'view').length,
|
||||
[allTables]
|
||||
);
|
||||
|
||||
// Initialize selectedTables with all tables (not views) if less than 100 tables
|
||||
const [selectedTables, setSelectedTables] = useState<Set<string>>(() => {
|
||||
const tables = allTables.filter((t) => t.type === 'table');
|
||||
if (tables.length < MAX_TABLES_IN_DIAGRAM) {
|
||||
return new Set(tables.map((t) => t.key));
|
||||
}
|
||||
return new Set();
|
||||
});
|
||||
|
||||
// Filter tables based on search term and type filters
|
||||
const filteredTables = useMemo(() => {
|
||||
let filtered = allTables;
|
||||
|
||||
// Filter by type
|
||||
filtered = filtered.filter((table) => {
|
||||
if (table.type === 'table' && !showTables) return false;
|
||||
if (table.type === 'view' && !showViews) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
// Filter by search term
|
||||
if (searchTerm.trim()) {
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
filtered = filtered.filter(
|
||||
(table) =>
|
||||
table.tableName.toLowerCase().includes(searchLower) ||
|
||||
table.schema?.toLowerCase().includes(searchLower) ||
|
||||
table.fullName.toLowerCase().includes(searchLower)
|
||||
);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}, [allTables, searchTerm, showTables, showViews]);
|
||||
|
||||
// Calculate pagination
|
||||
const totalPages = useMemo(
|
||||
() => Math.max(1, Math.ceil(filteredTables.length / TABLES_PER_PAGE)),
|
||||
[filteredTables.length]
|
||||
);
|
||||
|
||||
const paginatedTables = useMemo(() => {
|
||||
const startIndex = (currentPage - 1) * TABLES_PER_PAGE;
|
||||
const endIndex = startIndex + TABLES_PER_PAGE;
|
||||
return filteredTables.slice(startIndex, endIndex);
|
||||
}, [filteredTables, currentPage]);
|
||||
|
||||
// Get currently visible selected tables
|
||||
const visibleSelectedTables = useMemo(() => {
|
||||
return paginatedTables.filter((table) => selectedTables.has(table.key));
|
||||
}, [paginatedTables, selectedTables]);
|
||||
|
||||
const canAddMore = useMemo(
|
||||
() => selectedTables.size < MAX_TABLES_IN_DIAGRAM,
|
||||
[selectedTables.size]
|
||||
);
|
||||
const hasSearchResults = useMemo(
|
||||
() => filteredTables.length > 0,
|
||||
[filteredTables.length]
|
||||
);
|
||||
const allVisibleSelected = useMemo(
|
||||
() =>
|
||||
visibleSelectedTables.length === paginatedTables.length &&
|
||||
paginatedTables.length > 0,
|
||||
[visibleSelectedTables.length, paginatedTables.length]
|
||||
);
|
||||
const canSelectAllFiltered = useMemo(
|
||||
() =>
|
||||
filteredTables.length > 0 &&
|
||||
filteredTables.some((table) => !selectedTables.has(table.key)) &&
|
||||
canAddMore,
|
||||
[filteredTables, selectedTables, canAddMore]
|
||||
);
|
||||
|
||||
// Reset to first page when search changes
|
||||
useEffect(() => {
|
||||
setCurrentPage(1);
|
||||
}, [searchTerm]);
|
||||
|
||||
const handleTableToggle = useCallback(
|
||||
(tableKey: string) => {
|
||||
const newSelected = new Set(selectedTables);
|
||||
|
||||
if (newSelected.has(tableKey)) {
|
||||
newSelected.delete(tableKey);
|
||||
} else if (selectedTables.size < MAX_TABLES_IN_DIAGRAM) {
|
||||
newSelected.add(tableKey);
|
||||
}
|
||||
|
||||
setSelectedTables(newSelected);
|
||||
},
|
||||
[selectedTables]
|
||||
);
|
||||
|
||||
const handleTogglePageSelection = useCallback(() => {
|
||||
const newSelected = new Set(selectedTables);
|
||||
|
||||
if (allVisibleSelected) {
|
||||
// Deselect all on current page
|
||||
for (const table of paginatedTables) {
|
||||
newSelected.delete(table.key);
|
||||
}
|
||||
} else {
|
||||
// Select all on current page
|
||||
for (const table of paginatedTables) {
|
||||
if (newSelected.size >= MAX_TABLES_IN_DIAGRAM) break;
|
||||
newSelected.add(table.key);
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedTables(newSelected);
|
||||
}, [allVisibleSelected, paginatedTables, selectedTables]);
|
||||
|
||||
const handleSelectAllFiltered = useCallback(() => {
|
||||
const newSelected = new Set(selectedTables);
|
||||
|
||||
for (const table of filteredTables) {
|
||||
if (newSelected.size >= MAX_TABLES_IN_DIAGRAM) break;
|
||||
newSelected.add(table.key);
|
||||
}
|
||||
|
||||
setSelectedTables(newSelected);
|
||||
}, [filteredTables, selectedTables]);
|
||||
|
||||
const handleNextPage = useCallback(() => {
|
||||
if (currentPage < totalPages) {
|
||||
setCurrentPage(currentPage + 1);
|
||||
}
|
||||
}, [currentPage, totalPages]);
|
||||
|
||||
const handlePrevPage = useCallback(() => {
|
||||
if (currentPage > 1) {
|
||||
setCurrentPage(currentPage - 1);
|
||||
}
|
||||
}, [currentPage]);
|
||||
|
||||
const handleClearSelection = useCallback(() => {
|
||||
setSelectedTables(new Set());
|
||||
}, []);
|
||||
|
||||
const handleConfirm = useCallback(async () => {
|
||||
if (isImporting) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsImporting(true);
|
||||
|
||||
try {
|
||||
const selectedTableObjects: SelectedTable[] = Array.from(
|
||||
selectedTables
|
||||
)
|
||||
.map((key): SelectedTable | null => {
|
||||
const table = allTables.find((t) => t.key === key);
|
||||
if (!table) return null;
|
||||
|
||||
return {
|
||||
schema: table.schema,
|
||||
table: table.tableName,
|
||||
type: table.type,
|
||||
} satisfies SelectedTable;
|
||||
})
|
||||
.filter((t): t is SelectedTable => t !== null);
|
||||
|
||||
await onImport({
|
||||
selectedTables: selectedTableObjects,
|
||||
databaseMetadata,
|
||||
});
|
||||
} finally {
|
||||
setIsImporting(false);
|
||||
}
|
||||
}, [selectedTables, allTables, onImport, databaseMetadata, isImporting]);
|
||||
|
||||
const { isMd: isDesktop } = useBreakpoint('md');
|
||||
|
||||
const renderPagination = useCallback(
|
||||
() => (
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
onClick={handlePrevPage}
|
||||
className={cn(
|
||||
'cursor-pointer',
|
||||
currentPage === 1 &&
|
||||
'pointer-events-none opacity-50'
|
||||
)}
|
||||
/>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<span className="px-3 text-sm text-muted-foreground">
|
||||
Page {currentPage} of {totalPages}
|
||||
</span>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
onClick={handleNextPage}
|
||||
className={cn(
|
||||
'cursor-pointer',
|
||||
(currentPage >= totalPages ||
|
||||
filteredTables.length === 0) &&
|
||||
'pointer-events-none opacity-50'
|
||||
)}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
),
|
||||
[
|
||||
currentPage,
|
||||
totalPages,
|
||||
handlePrevPage,
|
||||
handleNextPage,
|
||||
filteredTables.length,
|
||||
]
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-[400px] items-center justify-center">
|
||||
<div className="text-center">
|
||||
<Spinner className="mb-4" />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Parsing database metadata...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Select Tables to Import</DialogTitle>
|
||||
<DialogDescription>
|
||||
{tableCount} {tableCount === 1 ? 'table' : 'tables'}
|
||||
{viewCount > 0 && (
|
||||
<>
|
||||
{' and '}
|
||||
{viewCount} {viewCount === 1 ? 'view' : 'views'}
|
||||
</>
|
||||
)}
|
||||
{' found. '}
|
||||
{allTables.length > MAX_TABLES_IN_DIAGRAM
|
||||
? `Select up to ${MAX_TABLES_IN_DIAGRAM} to import.`
|
||||
: 'Choose which ones to import.'}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogInternalContent>
|
||||
<div className="flex h-full flex-col space-y-4">
|
||||
{/* Warning/Info Banner */}
|
||||
{allTables.length > MAX_TABLES_IN_DIAGRAM ? (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded-lg p-3 text-sm',
|
||||
'bg-amber-50 text-amber-800 dark:bg-amber-950 dark:text-amber-200'
|
||||
)}
|
||||
>
|
||||
<AlertCircle className="size-4 shrink-0" />
|
||||
<span>
|
||||
Due to performance limitations, you can import a
|
||||
maximum of {MAX_TABLES_IN_DIAGRAM} tables.
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
{/* Search Input */}
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search tables..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="px-9"
|
||||
/>
|
||||
{searchTerm && (
|
||||
<button
|
||||
onClick={() => setSearchTerm('')}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
<X className="size-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Selection Status and Actions - Responsive layout */}
|
||||
<div className="flex flex-col items-center gap-3 sm:flex-row sm:items-center sm:justify-between sm:gap-4">
|
||||
{/* Left side: selection count -> checkboxes -> results found */}
|
||||
<div className="flex flex-col items-center gap-3 text-sm sm:flex-row sm:items-center sm:gap-4">
|
||||
<div className="flex flex-col items-center gap-1 sm:flex-row sm:items-center sm:gap-4">
|
||||
<span className="text-center font-medium">
|
||||
{selectedTables.size} /{' '}
|
||||
{Math.min(
|
||||
MAX_TABLES_IN_DIAGRAM,
|
||||
allTables.length
|
||||
)}{' '}
|
||||
items selected
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 sm:border-x sm:px-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={showTables}
|
||||
onCheckedChange={(checked) => {
|
||||
// Prevent unchecking if it's the only one checked
|
||||
if (!checked && !showViews) return;
|
||||
setShowTables(!!checked);
|
||||
}}
|
||||
/>
|
||||
<Table
|
||||
className="size-4"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<span>tables</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={showViews}
|
||||
onCheckedChange={(checked) => {
|
||||
// Prevent unchecking if it's the only one checked
|
||||
if (!checked && !showTables) return;
|
||||
setShowViews(!!checked);
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
className="size-4"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<span>views</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span className="hidden text-muted-foreground sm:inline">
|
||||
{filteredTables.length}{' '}
|
||||
{filteredTables.length === 1
|
||||
? 'result'
|
||||
: 'results'}{' '}
|
||||
found
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Right side: action buttons */}
|
||||
<div className="flex flex-wrap items-center justify-center gap-2">
|
||||
{hasSearchResults && (
|
||||
<>
|
||||
{/* Show page selection button when not searching and no selection */}
|
||||
{!searchTerm &&
|
||||
selectedTables.size === 0 && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={
|
||||
handleTogglePageSelection
|
||||
}
|
||||
disabled={
|
||||
paginatedTables.length === 0
|
||||
}
|
||||
>
|
||||
{allVisibleSelected
|
||||
? 'Deselect'
|
||||
: 'Select'}{' '}
|
||||
page
|
||||
</Button>
|
||||
)}
|
||||
{/* Show Select all button when there are unselected tables */}
|
||||
{canSelectAllFiltered &&
|
||||
selectedTables.size === 0 && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={
|
||||
handleSelectAllFiltered
|
||||
}
|
||||
disabled={!canSelectAllFiltered}
|
||||
title={(() => {
|
||||
const unselectedCount =
|
||||
filteredTables.filter(
|
||||
(table) =>
|
||||
!selectedTables.has(
|
||||
table.key
|
||||
)
|
||||
).length;
|
||||
const remainingCapacity =
|
||||
MAX_TABLES_IN_DIAGRAM -
|
||||
selectedTables.size;
|
||||
if (
|
||||
unselectedCount >
|
||||
remainingCapacity
|
||||
) {
|
||||
return `Can only select ${remainingCapacity} more tables (${MAX_TABLES_IN_DIAGRAM} max limit)`;
|
||||
}
|
||||
return undefined;
|
||||
})()}
|
||||
>
|
||||
{(() => {
|
||||
const unselectedCount =
|
||||
filteredTables.filter(
|
||||
(table) =>
|
||||
!selectedTables.has(
|
||||
table.key
|
||||
)
|
||||
).length;
|
||||
const remainingCapacity =
|
||||
MAX_TABLES_IN_DIAGRAM -
|
||||
selectedTables.size;
|
||||
if (
|
||||
unselectedCount >
|
||||
remainingCapacity
|
||||
) {
|
||||
return `Select ${remainingCapacity} of ${unselectedCount}`;
|
||||
}
|
||||
return `Select all ${unselectedCount}`;
|
||||
})()}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{selectedTables.size > 0 && (
|
||||
<>
|
||||
{/* Show page selection/deselection button when user has selections */}
|
||||
{paginatedTables.length > 0 && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleTogglePageSelection}
|
||||
>
|
||||
{allVisibleSelected
|
||||
? 'Deselect'
|
||||
: 'Select'}{' '}
|
||||
page
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleClearSelection}
|
||||
>
|
||||
Clear selection
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table List */}
|
||||
<div className="flex min-h-[428px] flex-1 flex-col">
|
||||
{hasSearchResults ? (
|
||||
<>
|
||||
<div className="flex-1 py-4">
|
||||
<div className="space-y-1">
|
||||
{paginatedTables.map((table) => {
|
||||
const isSelected = selectedTables.has(
|
||||
table.key
|
||||
);
|
||||
const isDisabled =
|
||||
!isSelected &&
|
||||
selectedTables.size >=
|
||||
MAX_TABLES_IN_DIAGRAM;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={table.key}
|
||||
className={cn(
|
||||
'flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors',
|
||||
{
|
||||
'cursor-not-allowed':
|
||||
isDisabled,
|
||||
|
||||
'bg-muted hover:bg-muted/80':
|
||||
isSelected,
|
||||
'hover:bg-accent':
|
||||
!isSelected &&
|
||||
!isDisabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
disabled={isDisabled}
|
||||
onCheckedChange={() =>
|
||||
handleTableToggle(
|
||||
table.key
|
||||
)
|
||||
}
|
||||
/>
|
||||
{table.type === 'view' ? (
|
||||
<View
|
||||
className="size-4"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
) : (
|
||||
<Table
|
||||
className="size-4"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
)}
|
||||
<span className="flex-1">
|
||||
{table.schema ? (
|
||||
<span className="text-muted-foreground">
|
||||
{table.schema}.
|
||||
</span>
|
||||
) : null}
|
||||
<span className="font-medium">
|
||||
{table.tableName}
|
||||
</span>
|
||||
{table.type === 'view' && (
|
||||
<span className="ml-2 text-xs text-muted-foreground">
|
||||
(view)
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
{isSelected && (
|
||||
<Check className="size-4 text-pink-600" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex h-full items-center justify-center py-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{searchTerm
|
||||
? 'No tables found matching your search.'
|
||||
: 'Start typing to search for tables...'}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isDesktop ? renderPagination() : null}
|
||||
</DialogInternalContent>
|
||||
<DialogFooter className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:space-x-2 md:justify-between md:gap-0">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={onBack}
|
||||
disabled={isImporting}
|
||||
>
|
||||
{t('new_diagram_dialog.back')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={handleConfirm}
|
||||
disabled={selectedTables.size === 0 || isImporting}
|
||||
className="bg-pink-500 text-white hover:bg-pink-600"
|
||||
>
|
||||
{isImporting ? (
|
||||
<>
|
||||
<Spinner className="mr-2 size-4 text-white" />
|
||||
Importing...
|
||||
</>
|
||||
) : (
|
||||
`Import ${selectedTables.size} Tables`
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{!isDesktop ? renderPagination() : null}
|
||||
</DialogFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
export enum CreateDiagramDialogStep {
|
||||
SELECT_DATABASE = 'SELECT_DATABASE',
|
||||
IMPORT_DATABASE = 'IMPORT_DATABASE',
|
||||
SELECT_TABLES = 'SELECT_TABLES',
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Dialog, DialogContent } from '@/components/dialog/dialog';
|
||||
import { DatabaseType } from '@/lib/domain/database-type';
|
||||
import { useStorage } from '@/hooks/use-storage';
|
||||
import type { Diagram } from '@/lib/domain/diagram';
|
||||
import { loadFromDatabaseMetadata } from '@/lib/domain/diagram';
|
||||
import { loadFromDatabaseMetadata } from '@/lib/data/import-metadata/import';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useConfig } from '@/hooks/use-config';
|
||||
import type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
||||
@@ -15,9 +15,13 @@ import type { DatabaseEdition } from '@/lib/domain/database-edition';
|
||||
import { SelectDatabase } from './select-database/select-database';
|
||||
import { CreateDiagramDialogStep } from './create-diagram-dialog-step';
|
||||
import { ImportDatabase } from '../common/import-database/import-database';
|
||||
import { SelectTables } from '../common/select-tables/select-tables';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||
import { sqlImportToDiagram } from '@/lib/data/sql-import';
|
||||
import type { SelectedTable } from '@/lib/data/import-metadata/filter-metadata';
|
||||
import { filterMetadataByTables } from '@/lib/data/import-metadata/filter-metadata';
|
||||
import { MAX_TABLES_WITHOUT_SHOWING_FILTER } from '../common/select-tables/constants';
|
||||
|
||||
export interface CreateDiagramDialogProps extends BaseDialogProps {}
|
||||
|
||||
@@ -42,6 +46,8 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
||||
const { listDiagrams, addDiagram } = useStorage();
|
||||
const [diagramNumber, setDiagramNumber] = useState<number>(1);
|
||||
const navigate = useNavigate();
|
||||
const [parsedMetadata, setParsedMetadata] = useState<DatabaseMetadata>();
|
||||
const [isParsingMetadata, setIsParsingMetadata] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setDatabaseEdition(undefined);
|
||||
@@ -62,49 +68,72 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
||||
setDatabaseEdition(undefined);
|
||||
setScriptResult('');
|
||||
setImportMethod('query');
|
||||
setParsedMetadata(undefined);
|
||||
}, [dialog.open]);
|
||||
|
||||
const hasExistingDiagram = (diagramId ?? '').trim().length !== 0;
|
||||
|
||||
const importNewDiagram = useCallback(async () => {
|
||||
let diagram: Diagram | undefined;
|
||||
const importNewDiagram = useCallback(
|
||||
async ({
|
||||
selectedTables,
|
||||
databaseMetadata,
|
||||
}: {
|
||||
selectedTables?: SelectedTable[];
|
||||
databaseMetadata?: DatabaseMetadata;
|
||||
} = {}) => {
|
||||
let diagram: Diagram | undefined;
|
||||
|
||||
if (importMethod === 'ddl') {
|
||||
diagram = await sqlImportToDiagram({
|
||||
sqlContent: scriptResult,
|
||||
sourceDatabaseType: databaseType,
|
||||
targetDatabaseType: databaseType,
|
||||
if (importMethod === 'ddl') {
|
||||
diagram = await sqlImportToDiagram({
|
||||
sqlContent: scriptResult,
|
||||
sourceDatabaseType: databaseType,
|
||||
targetDatabaseType: databaseType,
|
||||
});
|
||||
} else {
|
||||
let metadata: DatabaseMetadata | undefined = databaseMetadata;
|
||||
|
||||
if (!metadata) {
|
||||
metadata = loadDatabaseMetadata(scriptResult);
|
||||
}
|
||||
|
||||
if (selectedTables && selectedTables.length > 0) {
|
||||
metadata = filterMetadataByTables({
|
||||
metadata,
|
||||
selectedTables,
|
||||
});
|
||||
}
|
||||
|
||||
diagram = await loadFromDatabaseMetadata({
|
||||
databaseType,
|
||||
databaseMetadata: metadata,
|
||||
diagramNumber,
|
||||
databaseEdition:
|
||||
databaseEdition?.trim().length === 0
|
||||
? undefined
|
||||
: databaseEdition,
|
||||
});
|
||||
}
|
||||
|
||||
await addDiagram({ diagram });
|
||||
await updateConfig({
|
||||
config: { defaultDiagramId: diagram.id },
|
||||
});
|
||||
} else {
|
||||
const databaseMetadata: DatabaseMetadata =
|
||||
loadDatabaseMetadata(scriptResult);
|
||||
|
||||
diagram = await loadFromDatabaseMetadata({
|
||||
databaseType,
|
||||
databaseMetadata,
|
||||
diagramNumber,
|
||||
databaseEdition:
|
||||
databaseEdition?.trim().length === 0
|
||||
? undefined
|
||||
: databaseEdition,
|
||||
});
|
||||
}
|
||||
|
||||
await addDiagram({ diagram });
|
||||
await updateConfig({ config: { defaultDiagramId: diagram.id } });
|
||||
closeCreateDiagramDialog();
|
||||
navigate(`/diagrams/${diagram.id}`);
|
||||
}, [
|
||||
importMethod,
|
||||
databaseType,
|
||||
addDiagram,
|
||||
databaseEdition,
|
||||
closeCreateDiagramDialog,
|
||||
navigate,
|
||||
updateConfig,
|
||||
scriptResult,
|
||||
diagramNumber,
|
||||
]);
|
||||
closeCreateDiagramDialog();
|
||||
navigate(`/diagrams/${diagram.id}`);
|
||||
},
|
||||
[
|
||||
importMethod,
|
||||
databaseType,
|
||||
addDiagram,
|
||||
databaseEdition,
|
||||
closeCreateDiagramDialog,
|
||||
navigate,
|
||||
updateConfig,
|
||||
scriptResult,
|
||||
diagramNumber,
|
||||
]
|
||||
);
|
||||
|
||||
const createEmptyDiagram = useCallback(async () => {
|
||||
const diagram: Diagram = {
|
||||
@@ -138,10 +167,56 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
||||
openImportDBMLDialog,
|
||||
]);
|
||||
|
||||
const importNewDiagramOrFilterTables = useCallback(async () => {
|
||||
try {
|
||||
setIsParsingMetadata(true);
|
||||
|
||||
if (importMethod === 'ddl') {
|
||||
await importNewDiagram();
|
||||
} else {
|
||||
// Parse metadata asynchronously to avoid blocking the UI
|
||||
const metadata = await new Promise<DatabaseMetadata>(
|
||||
(resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const result =
|
||||
loadDatabaseMetadata(scriptResult);
|
||||
resolve(result);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
);
|
||||
|
||||
const totalTablesAndViews =
|
||||
metadata.tables.length + (metadata.views?.length || 0);
|
||||
|
||||
setParsedMetadata(metadata);
|
||||
|
||||
// Check if it's a large database that needs table selection
|
||||
if (totalTablesAndViews > MAX_TABLES_WITHOUT_SHOWING_FILTER) {
|
||||
setStep(CreateDiagramDialogStep.SELECT_TABLES);
|
||||
} else {
|
||||
await importNewDiagram({
|
||||
databaseMetadata: metadata,
|
||||
});
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
setIsParsingMetadata(false);
|
||||
}
|
||||
}, [importMethod, scriptResult, importNewDiagram]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
{...dialog}
|
||||
onOpenChange={(open) => {
|
||||
// Don't allow closing while parsing metadata
|
||||
if (isParsingMetadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasExistingDiagram) {
|
||||
return;
|
||||
}
|
||||
@@ -154,6 +229,8 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
||||
<DialogContent
|
||||
className="flex max-h-dvh w-full flex-col md:max-w-[900px]"
|
||||
showClose={hasExistingDiagram}
|
||||
onInteractOutside={(e) => e.preventDefault()}
|
||||
onEscapeKeyDown={(e) => e.preventDefault()}
|
||||
>
|
||||
{step === CreateDiagramDialogStep.SELECT_DATABASE ? (
|
||||
<SelectDatabase
|
||||
@@ -165,9 +242,9 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
||||
setStep(CreateDiagramDialogStep.IMPORT_DATABASE)
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
) : step === CreateDiagramDialogStep.IMPORT_DATABASE ? (
|
||||
<ImportDatabase
|
||||
onImport={importNewDiagram}
|
||||
onImport={importNewDiagramOrFilterTables}
|
||||
onCreateEmptyDiagram={createEmptyDiagram}
|
||||
databaseEdition={databaseEdition}
|
||||
databaseType={databaseType}
|
||||
@@ -180,8 +257,18 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
||||
title={t('new_diagram_dialog.import_database.title')}
|
||||
importMethod={importMethod}
|
||||
setImportMethod={setImportMethod}
|
||||
keepDialogAfterImport={true}
|
||||
/>
|
||||
)}
|
||||
) : step === CreateDiagramDialogStep.SELECT_TABLES ? (
|
||||
<SelectTables
|
||||
isLoading={isParsingMetadata || !parsedMetadata}
|
||||
databaseMetadata={parsedMetadata}
|
||||
onImport={importNewDiagram}
|
||||
onBack={() =>
|
||||
setStep(CreateDiagramDialogStep.IMPORT_DATABASE)
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -69,6 +69,7 @@ export const SelectDatabase: React.FC<SelectDatabaseProps> = ({
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={createNewDiagram}
|
||||
disabled={databaseType === DatabaseType.GENERIC}
|
||||
>
|
||||
{t('new_diagram_dialog.empty_diagram')}
|
||||
</Button>
|
||||
|
||||
@@ -218,8 +218,14 @@ export const CreateRelationshipDialog: React.FC<
|
||||
closeCreateRelationshipDialog();
|
||||
}
|
||||
}}
|
||||
modal={false}
|
||||
>
|
||||
<DialogContent className="flex flex-col overflow-y-auto" showClose>
|
||||
<DialogContent
|
||||
className="flex flex-col overflow-y-auto"
|
||||
showClose
|
||||
forceOverlay
|
||||
onInteractOutside={(e) => e.preventDefault()}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t('create_relationship_dialog.title')}
|
||||
|
||||
@@ -17,15 +17,21 @@ import { useDialog } from '@/hooks/use-dialog';
|
||||
import {
|
||||
exportBaseSQL,
|
||||
exportSQL,
|
||||
} from '@/lib/data/export-metadata/export-sql-script';
|
||||
} from '@/lib/data/sql-export/export-sql-script';
|
||||
import { databaseTypeToLabelMap } from '@/lib/databases';
|
||||
import { DatabaseType } from '@/lib/domain/database-type';
|
||||
import { shouldShowTablesBySchemaFilter } from '@/lib/domain/db-table';
|
||||
import { Annoyed, Sparkles } from 'lucide-react';
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||
import type { Diagram } from '@/lib/domain/diagram';
|
||||
import { useDiagramFilter } from '@/context/diagram-filter-context/use-diagram-filter';
|
||||
import {
|
||||
filterDependency,
|
||||
filterRelationship,
|
||||
filterTable,
|
||||
} from '@/lib/domain/diagram-filter/filter';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
|
||||
export interface ExportSQLDialogProps extends BaseDialogProps {
|
||||
targetDatabaseType: DatabaseType;
|
||||
@@ -36,7 +42,8 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
||||
targetDatabaseType,
|
||||
}) => {
|
||||
const { closeExportSQLDialog } = useDialog();
|
||||
const { currentDiagram, filteredSchemas } = useChartDB();
|
||||
const { currentDiagram } = useChartDB();
|
||||
const { filter } = useDiagramFilter();
|
||||
const { t } = useTranslation();
|
||||
const [script, setScript] = React.useState<string>();
|
||||
const [error, setError] = React.useState<boolean>(false);
|
||||
@@ -48,7 +55,16 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
||||
const filteredDiagram: Diagram = {
|
||||
...currentDiagram,
|
||||
tables: currentDiagram.tables?.filter((table) =>
|
||||
shouldShowTablesBySchemaFilter(table, filteredSchemas)
|
||||
filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[targetDatabaseType],
|
||||
},
|
||||
})
|
||||
),
|
||||
relationships: currentDiagram.relationships?.filter((rel) => {
|
||||
const sourceTable = currentDiagram.tables?.find(
|
||||
@@ -60,11 +76,20 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
||||
return (
|
||||
sourceTable &&
|
||||
targetTable &&
|
||||
shouldShowTablesBySchemaFilter(
|
||||
sourceTable,
|
||||
filteredSchemas
|
||||
) &&
|
||||
shouldShowTablesBySchemaFilter(targetTable, filteredSchemas)
|
||||
filterRelationship({
|
||||
tableA: {
|
||||
id: sourceTable.id,
|
||||
schema: sourceTable.schema,
|
||||
},
|
||||
tableB: {
|
||||
id: targetTable.id,
|
||||
schema: targetTable.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[targetDatabaseType],
|
||||
},
|
||||
})
|
||||
);
|
||||
}),
|
||||
dependencies: currentDiagram.dependencies?.filter((dep) => {
|
||||
@@ -77,11 +102,20 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
||||
return (
|
||||
table &&
|
||||
dependentTable &&
|
||||
shouldShowTablesBySchemaFilter(table, filteredSchemas) &&
|
||||
shouldShowTablesBySchemaFilter(
|
||||
dependentTable,
|
||||
filteredSchemas
|
||||
)
|
||||
filterDependency({
|
||||
tableA: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
tableB: {
|
||||
id: dependentTable.id,
|
||||
schema: dependentTable.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[targetDatabaseType],
|
||||
},
|
||||
})
|
||||
);
|
||||
}),
|
||||
};
|
||||
@@ -101,7 +135,7 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
||||
signal: abortControllerRef.current?.signal,
|
||||
});
|
||||
}
|
||||
}, [targetDatabaseType, currentDiagram, filteredSchemas]);
|
||||
}, [targetDatabaseType, currentDiagram, filter]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dialog.open) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { DatabaseEdition } from '@/lib/domain/database-edition';
|
||||
import type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
||||
import { loadDatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
||||
import type { Diagram } from '@/lib/domain/diagram';
|
||||
import { loadFromDatabaseMetadata } from '@/lib/domain/diagram';
|
||||
import { loadFromDatabaseMetadata } from '@/lib/data/import-metadata/import';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -5,7 +5,7 @@ import React, {
|
||||
Suspense,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import type * as monaco from 'monaco-editor';
|
||||
import { useDialog } from '@/hooks/use-dialog';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -23,53 +23,24 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Editor } from '@/components/code-snippet/code-snippet';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
import { AlertCircle } from 'lucide-react';
|
||||
import { importDBMLToDiagram, sanitizeDBML } from '@/lib/dbml-import';
|
||||
import {
|
||||
importDBMLToDiagram,
|
||||
sanitizeDBML,
|
||||
preprocessDBML,
|
||||
} from '@/lib/dbml/dbml-import/dbml-import';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { Parser } from '@dbml/core';
|
||||
import { useCanvas } from '@/hooks/use-canvas';
|
||||
import { setupDBMLLanguage } from '@/components/code-snippet/languages/dbml-language';
|
||||
import type { DBTable } from '@/lib/domain/db-table';
|
||||
import { useToast } from '@/components/toast/use-toast';
|
||||
import { Spinner } from '@/components/spinner/spinner';
|
||||
import { debounce } from '@/lib/utils';
|
||||
|
||||
interface DBMLError {
|
||||
message: string;
|
||||
line: number;
|
||||
column: number;
|
||||
}
|
||||
|
||||
function parseDBMLError(error: unknown): DBMLError | null {
|
||||
try {
|
||||
if (typeof error === 'string') {
|
||||
const parsed = JSON.parse(error);
|
||||
if (parsed.diags?.[0]) {
|
||||
const diag = parsed.diags[0];
|
||||
return {
|
||||
message: diag.message,
|
||||
line: diag.location.start.line,
|
||||
column: diag.location.start.column,
|
||||
};
|
||||
}
|
||||
} else if (error && typeof error === 'object' && 'diags' in error) {
|
||||
const parsed = error as {
|
||||
diags: Array<{
|
||||
message: string;
|
||||
location: { start: { line: number; column: number } };
|
||||
}>;
|
||||
};
|
||||
if (parsed.diags?.[0]) {
|
||||
return {
|
||||
message: parsed.diags[0].message,
|
||||
line: parsed.diags[0].location.start.line,
|
||||
column: parsed.diags[0].location.start.column,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing DBML error:', e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
import { parseDBMLError } from '@/lib/dbml/dbml-import/dbml-import-error';
|
||||
import {
|
||||
clearErrorHighlight,
|
||||
highlightErrorLine,
|
||||
} from '@/components/code-snippet/dbml/utils';
|
||||
|
||||
export interface ImportDBMLDialogProps extends BaseDialogProps {
|
||||
withCreateEmptyDiagram?: boolean;
|
||||
@@ -145,39 +116,8 @@ Ref: comments.user_id > users.id // Each comment is written by one user`;
|
||||
}
|
||||
}, [reorder, reorderTables]);
|
||||
|
||||
const highlightErrorLine = useCallback((error: DBMLError) => {
|
||||
if (!editorRef.current) return;
|
||||
|
||||
const model = editorRef.current.getModel();
|
||||
if (!model) return;
|
||||
|
||||
const decorations = [
|
||||
{
|
||||
range: new monaco.Range(
|
||||
error.line,
|
||||
1,
|
||||
error.line,
|
||||
model.getLineMaxColumn(error.line)
|
||||
),
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
className: 'dbml-error-line',
|
||||
glyphMarginClassName: 'dbml-error-glyph',
|
||||
hoverMessage: { value: error.message },
|
||||
overviewRuler: {
|
||||
color: '#ff0000',
|
||||
position: monaco.editor.OverviewRulerLane.Right,
|
||||
darkColor: '#ff0000',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
decorationsCollection.current?.set(decorations);
|
||||
}, []);
|
||||
|
||||
const clearDecorations = useCallback(() => {
|
||||
decorationsCollection.current?.clear();
|
||||
clearErrorHighlight(decorationsCollection.current);
|
||||
}, []);
|
||||
|
||||
const validateDBML = useCallback(
|
||||
@@ -189,9 +129,10 @@ Ref: comments.user_id > users.id // Each comment is written by one user`;
|
||||
if (!content.trim()) return;
|
||||
|
||||
try {
|
||||
const sanitizedContent = sanitizeDBML(content);
|
||||
const preprocessedContent = preprocessDBML(content);
|
||||
const sanitizedContent = sanitizeDBML(preprocessedContent);
|
||||
const parser = new Parser();
|
||||
parser.parse(sanitizedContent, 'dbml');
|
||||
parser.parse(sanitizedContent, 'dbmlv2');
|
||||
} catch (e) {
|
||||
const parsedError = parseDBMLError(e);
|
||||
if (parsedError) {
|
||||
@@ -199,7 +140,12 @@ Ref: comments.user_id > users.id // Each comment is written by one user`;
|
||||
t('import_dbml_dialog.error.description') +
|
||||
` (1 error found - in line ${parsedError.line})`
|
||||
);
|
||||
highlightErrorLine(parsedError);
|
||||
highlightErrorLine({
|
||||
error: parsedError,
|
||||
model: editorRef.current?.getModel(),
|
||||
editorDecorationsCollection:
|
||||
decorationsCollection.current,
|
||||
});
|
||||
} else {
|
||||
setErrorMessage(
|
||||
e instanceof Error ? e.message : JSON.stringify(e)
|
||||
@@ -207,7 +153,7 @@ Ref: comments.user_id > users.id // Each comment is written by one user`;
|
||||
}
|
||||
}
|
||||
},
|
||||
[clearDecorations, highlightErrorLine, t]
|
||||
[clearDecorations, t]
|
||||
);
|
||||
|
||||
const debouncedValidateRef = useRef<((value: string) => void) | null>(null);
|
||||
@@ -242,13 +188,11 @@ Ref: comments.user_id > users.id // Each comment is written by one user`;
|
||||
if (!dbmlContent.trim() || errorMessage) return;
|
||||
|
||||
try {
|
||||
// Sanitize DBML content before importing
|
||||
const sanitizedContent = sanitizeDBML(dbmlContent);
|
||||
const importedDiagram = await importDBMLToDiagram(sanitizedContent);
|
||||
const importedDiagram = await importDBMLToDiagram(dbmlContent);
|
||||
const tableIdsToRemove = tables
|
||||
.filter((table) =>
|
||||
importedDiagram.tables?.some(
|
||||
(t) =>
|
||||
(t: DBTable) =>
|
||||
t.name === table.name && t.schema === table.schema
|
||||
)
|
||||
)
|
||||
@@ -257,19 +201,21 @@ Ref: comments.user_id > users.id // Each comment is written by one user`;
|
||||
const relationshipIdsToRemove = relationships
|
||||
.filter((relationship) => {
|
||||
const sourceTable = tables.find(
|
||||
(table) => table.id === relationship.sourceTableId
|
||||
(table: DBTable) =>
|
||||
table.id === relationship.sourceTableId
|
||||
);
|
||||
const targetTable = tables.find(
|
||||
(table) => table.id === relationship.targetTableId
|
||||
(table: DBTable) =>
|
||||
table.id === relationship.targetTableId
|
||||
);
|
||||
if (!sourceTable || !targetTable) return true;
|
||||
const replacementSourceTable = importedDiagram.tables?.find(
|
||||
(table) =>
|
||||
(table: DBTable) =>
|
||||
table.name === sourceTable.name &&
|
||||
table.schema === sourceTable.schema
|
||||
);
|
||||
const replacementTargetTable = importedDiagram.tables?.find(
|
||||
(table) =>
|
||||
(table: DBTable) =>
|
||||
table.name === targetTable.name &&
|
||||
table.schema === targetTable.schema
|
||||
);
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/dropdown-menu/dropdown-menu';
|
||||
import { Button } from '@/components/button/button';
|
||||
import { Ellipsis, Layers2, SquareArrowOutUpRight, Trash2 } from 'lucide-react';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import type { Diagram } from '@/lib/domain';
|
||||
import { useStorage } from '@/hooks/use-storage';
|
||||
import { cloneDiagram } from '@/lib/clone';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface DiagramRowActionsMenuProps {
|
||||
diagram: Diagram;
|
||||
onOpen: () => void;
|
||||
refetch: () => void;
|
||||
numberOfDiagrams: number;
|
||||
}
|
||||
|
||||
export const DiagramRowActionsMenu: React.FC<DiagramRowActionsMenuProps> = ({
|
||||
diagram,
|
||||
onOpen,
|
||||
refetch,
|
||||
numberOfDiagrams,
|
||||
}) => {
|
||||
const { diagramId } = useChartDB();
|
||||
const { deleteDiagram, addDiagram } = useStorage();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onDelete = useCallback(async () => {
|
||||
deleteDiagram(diagram.id);
|
||||
refetch();
|
||||
|
||||
if (diagram.id === diagramId || numberOfDiagrams <= 1) {
|
||||
window.location.href = '/';
|
||||
}
|
||||
}, [deleteDiagram, diagram.id, diagramId, refetch, numberOfDiagrams]);
|
||||
|
||||
const onDuplicate = useCallback(async () => {
|
||||
const duplicatedDiagram = cloneDiagram(diagram);
|
||||
|
||||
const diagramToAdd = duplicatedDiagram.diagram;
|
||||
|
||||
if (!diagramToAdd) {
|
||||
return;
|
||||
}
|
||||
|
||||
diagramToAdd.name = `${diagram.name} (Copy)`;
|
||||
|
||||
addDiagram({ diagram: diagramToAdd });
|
||||
refetch();
|
||||
}, [addDiagram, refetch, diagram]);
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="size-8 p-0"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Ellipsis className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onClick={onOpen}
|
||||
className="flex justify-between gap-4"
|
||||
>
|
||||
{t('open_diagram_dialog.diagram_actions.open')}
|
||||
<SquareArrowOutUpRight className="size-3.5" />
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem
|
||||
onClick={onDuplicate}
|
||||
className="flex justify-between gap-4"
|
||||
>
|
||||
{t('open_diagram_dialog.diagram_actions.duplicate')}
|
||||
<Layers2 className="size-3.5" />
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={onDelete}
|
||||
className="flex justify-between gap-4 text-red-700"
|
||||
>
|
||||
{t('open_diagram_dialog.diagram_actions.delete')}
|
||||
<Trash2 className="size-3.5 text-red-700" />
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
@@ -27,6 +27,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||
import { useDebounce } from '@/hooks/use-debounce';
|
||||
import { DiagramRowActionsMenu } from './diagram-row-actions-menu/diagram-row-actions-menu';
|
||||
|
||||
export interface OpenDiagramDialogProps extends BaseDialogProps {
|
||||
canClose?: boolean;
|
||||
@@ -46,21 +47,22 @@ export const OpenDiagramDialog: React.FC<OpenDiagramDialogProps> = ({
|
||||
string | undefined
|
||||
>();
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedDiagramId(undefined);
|
||||
}, [dialog.open]);
|
||||
const fetchDiagrams = useCallback(async () => {
|
||||
const diagrams = await listDiagrams({ includeTables: true });
|
||||
setDiagrams(
|
||||
diagrams.sort(
|
||||
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
||||
)
|
||||
);
|
||||
}, [listDiagrams]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDiagrams = async () => {
|
||||
const diagrams = await listDiagrams({ includeTables: true });
|
||||
setDiagrams(
|
||||
diagrams.sort(
|
||||
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
||||
)
|
||||
);
|
||||
};
|
||||
if (!dialog.open) {
|
||||
return;
|
||||
}
|
||||
setSelectedDiagramId(undefined);
|
||||
fetchDiagrams();
|
||||
}, [listDiagrams, setDiagrams, dialog.open]);
|
||||
}, [dialog.open, fetchDiagrams]);
|
||||
|
||||
const openDiagram = useCallback(
|
||||
(diagramId: string) => {
|
||||
@@ -166,6 +168,7 @@ export const OpenDiagramDialog: React.FC<OpenDiagramDialogProps> = ({
|
||||
'open_diagram_dialog.table_columns.tables_count'
|
||||
)}
|
||||
</TableHead>
|
||||
<TableHead />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -221,6 +224,19 @@ export const OpenDiagramDialog: React.FC<OpenDiagramDialogProps> = ({
|
||||
<TableCell className="text-center">
|
||||
{diagram.tables?.length}
|
||||
</TableCell>
|
||||
<TableCell className="items-center p-0 pr-1 text-right">
|
||||
<DiagramRowActionsMenu
|
||||
diagram={diagram}
|
||||
onOpen={() => {
|
||||
openDiagram(diagram.id);
|
||||
closeOpenDiagramDialog();
|
||||
}}
|
||||
numberOfDiagrams={
|
||||
diagrams.length
|
||||
}
|
||||
refetch={fetchDiagrams}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDialog } from '@/hooks/use-dialog';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -17,11 +17,23 @@ import type { DBSchema } from '@/lib/domain/db-schema';
|
||||
import { schemaNameToSchemaId } from '@/lib/domain/db-schema';
|
||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Input } from '@/components/input/input';
|
||||
import { Separator } from '@/components/separator/separator';
|
||||
import { Group, SquarePlus } from 'lucide-react';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/tooltip/tooltip';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
import { Label } from '@/components/label/label';
|
||||
|
||||
export interface TableSchemaDialogProps extends BaseDialogProps {
|
||||
table?: DBTable;
|
||||
schemas: DBSchema[];
|
||||
onConfirm: (schema: string) => void;
|
||||
onConfirm: ({ schema }: { schema: DBSchema }) => void;
|
||||
allowSchemaCreation?: boolean;
|
||||
}
|
||||
|
||||
export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
||||
@@ -29,27 +41,73 @@ export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
||||
table,
|
||||
schemas,
|
||||
onConfirm,
|
||||
allowSchemaCreation = false,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [selectedSchema, setSelectedSchema] = React.useState<string>(
|
||||
const { databaseType } = useChartDB();
|
||||
const [selectedSchemaId, setSelectedSchemaId] = useState<string>(
|
||||
table?.schema
|
||||
? schemaNameToSchemaId(table.schema)
|
||||
: (schemas?.[0]?.id ?? '')
|
||||
);
|
||||
const allowSchemaSelection = useMemo(
|
||||
() => schemas && schemas.length > 0,
|
||||
[schemas]
|
||||
);
|
||||
|
||||
const defaultSchemaName = useMemo(
|
||||
() => defaultSchemas?.[databaseType],
|
||||
[databaseType]
|
||||
);
|
||||
|
||||
const [isCreatingNew, setIsCreatingNew] =
|
||||
useState<boolean>(!allowSchemaSelection);
|
||||
const [newSchemaName, setNewSchemaName] = useState<string>(
|
||||
allowSchemaCreation && !allowSchemaSelection
|
||||
? (defaultSchemaName ?? '')
|
||||
: ''
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dialog.open) return;
|
||||
setSelectedSchema(
|
||||
setSelectedSchemaId(
|
||||
table?.schema
|
||||
? schemaNameToSchemaId(table.schema)
|
||||
: (schemas?.[0]?.id ?? '')
|
||||
);
|
||||
}, [dialog.open, schemas, table?.schema]);
|
||||
setIsCreatingNew(!allowSchemaSelection);
|
||||
setNewSchemaName(
|
||||
allowSchemaCreation && !allowSchemaSelection
|
||||
? (defaultSchemaName ?? '')
|
||||
: ''
|
||||
);
|
||||
}, [
|
||||
defaultSchemaName,
|
||||
dialog.open,
|
||||
schemas,
|
||||
table?.schema,
|
||||
allowSchemaSelection,
|
||||
allowSchemaCreation,
|
||||
]);
|
||||
|
||||
const { closeTableSchemaDialog } = useDialog();
|
||||
|
||||
const handleConfirm = useCallback(() => {
|
||||
onConfirm(selectedSchema);
|
||||
}, [onConfirm, selectedSchema]);
|
||||
if (isCreatingNew && newSchemaName.trim()) {
|
||||
const newSchema: DBSchema = {
|
||||
id: schemaNameToSchemaId(newSchemaName.trim()),
|
||||
name: newSchemaName.trim(),
|
||||
tableCount: 0,
|
||||
};
|
||||
|
||||
onConfirm({ schema: newSchema });
|
||||
} else {
|
||||
const schema = schemas.find((s) => s.id === selectedSchemaId);
|
||||
if (!schema) return;
|
||||
|
||||
onConfirm({ schema });
|
||||
}
|
||||
}, [onConfirm, selectedSchemaId, schemas, isCreatingNew, newSchemaName]);
|
||||
|
||||
const schemaOptions: SelectBoxOption[] = useMemo(
|
||||
() =>
|
||||
@@ -60,6 +118,25 @@ export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
||||
[schemas]
|
||||
);
|
||||
|
||||
const renderSwitchCreateOrSelectButton = useCallback(
|
||||
() => (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start"
|
||||
onClick={() => setIsCreatingNew(!isCreatingNew)}
|
||||
disabled={!allowSchemaSelection || !allowSchemaCreation}
|
||||
>
|
||||
{!isCreatingNew ? (
|
||||
<SquarePlus className="mr-2 size-4 " />
|
||||
) : (
|
||||
<Group className="mr-2 size-4 " />
|
||||
)}
|
||||
{isCreatingNew ? 'Select existing schema' : 'Create new schema'}
|
||||
</Button>
|
||||
),
|
||||
[isCreatingNew, allowSchemaSelection, allowSchemaCreation]
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
{...dialog}
|
||||
@@ -67,48 +144,106 @@ export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
||||
if (!open) {
|
||||
closeTableSchemaDialog();
|
||||
}
|
||||
|
||||
setTimeout(() => (document.body.style.pointerEvents = ''), 500);
|
||||
}}
|
||||
>
|
||||
<DialogContent className="flex flex-col" showClose>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{table
|
||||
? t('update_table_schema_dialog.title')
|
||||
: t('new_table_schema_dialog.title')}
|
||||
{!allowSchemaSelection && allowSchemaCreation
|
||||
? t('create_table_schema_dialog.title')
|
||||
: table
|
||||
? t('update_table_schema_dialog.title')
|
||||
: t('new_table_schema_dialog.title')}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{table
|
||||
? t('update_table_schema_dialog.description', {
|
||||
tableName: table.name,
|
||||
})
|
||||
: t('new_table_schema_dialog.description')}
|
||||
{!allowSchemaSelection && allowSchemaCreation
|
||||
? t('create_table_schema_dialog.description')
|
||||
: table
|
||||
? t('update_table_schema_dialog.description', {
|
||||
tableName: table.name,
|
||||
})
|
||||
: t('new_table_schema_dialog.description')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4 py-1">
|
||||
<div className="grid w-full items-center gap-4">
|
||||
<SelectBox
|
||||
options={schemaOptions}
|
||||
multiple={false}
|
||||
value={selectedSchema}
|
||||
onChange={(value) =>
|
||||
setSelectedSchema(value as string)
|
||||
}
|
||||
/>
|
||||
{!isCreatingNew ? (
|
||||
<SelectBox
|
||||
options={schemaOptions}
|
||||
multiple={false}
|
||||
value={selectedSchemaId}
|
||||
onChange={(value) =>
|
||||
setSelectedSchemaId(value as string)
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col gap-2">
|
||||
{allowSchemaCreation &&
|
||||
!allowSchemaSelection ? (
|
||||
<Label htmlFor="new-schema-name">
|
||||
Schema Name
|
||||
</Label>
|
||||
) : null}
|
||||
<Input
|
||||
id="new-schema-name"
|
||||
value={newSchemaName}
|
||||
onChange={(e) =>
|
||||
setNewSchemaName(e.target.value)
|
||||
}
|
||||
placeholder={`Enter schema name.${defaultSchemaName ? ` e.g. ${defaultSchemaName}.` : ''}`}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{allowSchemaCreation && allowSchemaSelection ? (
|
||||
<>
|
||||
<div className="relative">
|
||||
<Separator className="my-2" />
|
||||
<span className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-background px-2 text-xs text-muted-foreground">
|
||||
or
|
||||
</span>
|
||||
</div>
|
||||
{allowSchemaSelection ? (
|
||||
renderSwitchCreateOrSelectButton()
|
||||
) : (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
{renderSwitchCreateOrSelectButton()}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>No existing schemas available</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="flex gap-1 md:justify-between">
|
||||
<DialogClose asChild>
|
||||
<Button variant="secondary">
|
||||
{table
|
||||
? t('update_table_schema_dialog.cancel')
|
||||
: t('new_table_schema_dialog.cancel')}
|
||||
{isCreatingNew
|
||||
? t('create_table_schema_dialog.cancel')
|
||||
: table
|
||||
? t('update_table_schema_dialog.cancel')
|
||||
: t('new_table_schema_dialog.cancel')}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button onClick={handleConfirm}>
|
||||
{table
|
||||
? t('update_table_schema_dialog.confirm')
|
||||
: t('new_table_schema_dialog.confirm')}
|
||||
<Button
|
||||
onClick={handleConfirm}
|
||||
disabled={isCreatingNew && !newSchemaName.trim()}
|
||||
>
|
||||
{isCreatingNew
|
||||
? t('create_table_schema_dialog.create')
|
||||
: table
|
||||
? t('update_table_schema_dialog.confirm')
|
||||
: t('new_table_schema_dialog.confirm')}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
overscroll-behavior-x: none;
|
||||
}
|
||||
|
||||
.text-editable {
|
||||
@@ -154,3 +155,29 @@
|
||||
background-size: 650%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Edit button emphasis animation */
|
||||
@keyframes dbml_edit-button-emphasis {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7);
|
||||
background-color: rgba(59, 130, 246, 0);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
|
||||
background-color: rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
|
||||
background-color: rgba(59, 130, 246, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.dbml-edit-button-emphasis {
|
||||
animation: dbml_edit-button-emphasis 0.6s ease-in-out;
|
||||
animation-iteration-count: 1;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
142
src/hooks/use-focus-on.ts
Normal file
142
src/hooks/use-focus-on.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useReactFlow } from '@xyflow/react';
|
||||
import { useLayout } from '@/hooks/use-layout';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
|
||||
interface FocusOptions {
|
||||
select?: boolean;
|
||||
}
|
||||
|
||||
export const useFocusOn = () => {
|
||||
const { fitView, setNodes, setEdges } = useReactFlow();
|
||||
const { hideSidePanel } = useLayout();
|
||||
const { isMd: isDesktop } = useBreakpoint('md');
|
||||
|
||||
const focusOnArea = useCallback(
|
||||
(areaId: string, options: FocusOptions = {}) => {
|
||||
const { select = true } = options;
|
||||
|
||||
if (select) {
|
||||
setNodes((nodes) =>
|
||||
nodes.map((node) =>
|
||||
node.id === areaId
|
||||
? {
|
||||
...node,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...node,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: areaId,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDesktop) {
|
||||
hideSidePanel();
|
||||
}
|
||||
},
|
||||
[fitView, setNodes, hideSidePanel, isDesktop]
|
||||
);
|
||||
|
||||
const focusOnTable = useCallback(
|
||||
(tableId: string, options: FocusOptions = {}) => {
|
||||
const { select = true } = options;
|
||||
|
||||
if (select) {
|
||||
setNodes((nodes) =>
|
||||
nodes.map((node) =>
|
||||
node.id === tableId
|
||||
? {
|
||||
...node,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...node,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: tableId,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDesktop) {
|
||||
hideSidePanel();
|
||||
}
|
||||
},
|
||||
[fitView, setNodes, hideSidePanel, isDesktop]
|
||||
);
|
||||
|
||||
const focusOnRelationship = useCallback(
|
||||
(
|
||||
relationshipId: string,
|
||||
sourceTableId: string,
|
||||
targetTableId: string,
|
||||
options: FocusOptions = {}
|
||||
) => {
|
||||
const { select = true } = options;
|
||||
|
||||
if (select) {
|
||||
setEdges((edges) =>
|
||||
edges.map((edge) =>
|
||||
edge.id === relationshipId
|
||||
? {
|
||||
...edge,
|
||||
selected: true,
|
||||
}
|
||||
: {
|
||||
...edge,
|
||||
selected: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fitView({
|
||||
duration: 500,
|
||||
maxZoom: 1,
|
||||
minZoom: 1,
|
||||
nodes: [
|
||||
{
|
||||
id: sourceTableId,
|
||||
},
|
||||
{
|
||||
id: targetTableId,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDesktop) {
|
||||
hideSidePanel();
|
||||
}
|
||||
},
|
||||
[fitView, setEdges, hideSidePanel, isDesktop]
|
||||
);
|
||||
|
||||
return {
|
||||
focusOnArea,
|
||||
focusOnTable,
|
||||
focusOnRelationship,
|
||||
};
|
||||
};
|
||||
@@ -23,23 +23,25 @@ import { bn, bnMetadata } from './locales/bn';
|
||||
import { gu, guMetadata } from './locales/gu';
|
||||
import { vi, viMetadata } from './locales/vi';
|
||||
import { ar, arMetadata } from './locales/ar';
|
||||
import { hr, hrMetadata } from './locales/hr';
|
||||
|
||||
export const languages: LanguageMetadata[] = [
|
||||
enMetadata,
|
||||
esMetadata,
|
||||
frMetadata,
|
||||
deMetadata,
|
||||
esMetadata,
|
||||
ukMetadata,
|
||||
ruMetadata,
|
||||
trMetadata,
|
||||
hrMetadata,
|
||||
pt_BRMetadata,
|
||||
hiMetadata,
|
||||
jaMetadata,
|
||||
ko_KRMetadata,
|
||||
pt_BRMetadata,
|
||||
ukMetadata,
|
||||
ruMetadata,
|
||||
zh_CNMetadata,
|
||||
zh_TWMetadata,
|
||||
neMetadata,
|
||||
mrMetadata,
|
||||
trMetadata,
|
||||
id_IDMetadata,
|
||||
teMetadata,
|
||||
bnMetadata,
|
||||
@@ -70,6 +72,7 @@ const resources = {
|
||||
gu,
|
||||
vi,
|
||||
ar,
|
||||
hr,
|
||||
};
|
||||
|
||||
i18n.use(LanguageDetector)
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const ar: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'جديد',
|
||||
browse: 'تصفح',
|
||||
tables: 'الجداول',
|
||||
refs: 'المراجع',
|
||||
areas: 'المناطق',
|
||||
dependencies: 'التبعيات',
|
||||
custom_types: 'الأنواع المخصصة',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'ملف',
|
||||
new: 'جديد',
|
||||
open: 'فتح',
|
||||
actions: {
|
||||
actions: 'الإجراءات',
|
||||
new: 'جديد...',
|
||||
browse: 'تصفح...',
|
||||
save: 'حفظ',
|
||||
import: 'استيراد قاعدة بيانات',
|
||||
export_sql: 'SQL تصدير',
|
||||
export_as: 'تصدير كـ',
|
||||
delete_diagram: 'حذف الرسم البياني',
|
||||
exit: 'خروج',
|
||||
delete_diagram: 'حذف',
|
||||
},
|
||||
edit: {
|
||||
edit: 'تحرير',
|
||||
@@ -26,7 +34,10 @@ export const ar: LanguageTranslation = {
|
||||
hide_sidebar: 'إخفاء الشريط الجانبي',
|
||||
hide_cardinality: 'إخفاء الكاردينالية',
|
||||
show_cardinality: 'إظهار الكاردينالية',
|
||||
hide_field_attributes: 'إخفاء خصائص الحقل',
|
||||
show_field_attributes: 'إظهار خصائص الحقل',
|
||||
zoom_on_scroll: 'تكبير/تصغير عند التمرير',
|
||||
show_views: 'عروض قاعدة البيانات',
|
||||
theme: 'المظهر',
|
||||
show_dependencies: 'إظهار الاعتمادات',
|
||||
hide_dependencies: 'إخفاء الاعتمادات',
|
||||
@@ -63,22 +74,13 @@ export const ar: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'إعادة ترتيب الرسم البياني',
|
||||
title: 'ترتيب تلقائي للرسم البياني',
|
||||
description:
|
||||
'هذا الإجراء سيقوم بإعادة ترتيب الجداول في المخطط بشكل تلقائي. هل تريد المتابعة؟',
|
||||
reorder: 'إعادة ترتيب',
|
||||
reorder: 'ترتيب تلقائي',
|
||||
cancel: 'إلغاء',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'مخططات متعددة',
|
||||
description:
|
||||
'{{formattedSchemas}} :مخططات في هذا الرسم البياني. يتم حاليا عرض {{schemasCount}} هناك',
|
||||
dont_show_again: 'لا تظهره مجدداً',
|
||||
change_schema: 'تغيير',
|
||||
none: 'لا شيء',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'فشل النسخ',
|
||||
@@ -113,14 +115,11 @@ export const ar: LanguageTranslation = {
|
||||
copied: '!تم النسخ',
|
||||
|
||||
side_panel: {
|
||||
schema: ':المخطط',
|
||||
filter_by_schema: 'تصفية حسب المخطط',
|
||||
search_schema: '...بحث في المخطط',
|
||||
no_schemas_found: '.لم يتم العثور على مخططات',
|
||||
view_all_options: '...عرض جميع الخيارات',
|
||||
tables_section: {
|
||||
tables: 'الجداول',
|
||||
add_table: 'إضافة جدول',
|
||||
add_view: 'إضافة عرض',
|
||||
filter: 'تصفية',
|
||||
collapse: 'طي الكل',
|
||||
// TODO: Translate
|
||||
@@ -146,16 +145,22 @@ export const ar: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'خصائص الحقل',
|
||||
unique: 'فريد',
|
||||
auto_increment: 'زيادة تلقائية',
|
||||
comments: 'تعليقات',
|
||||
no_comments: 'لا يوجد تعليقات',
|
||||
delete_field: 'حذف الحقل',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'الدقة',
|
||||
scale: 'النطاق',
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'خصائص الفهرس',
|
||||
name: 'الإسم',
|
||||
unique: 'فريد',
|
||||
index_type: 'نوع الفهرس',
|
||||
delete_index: 'حذف الفهرس',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -172,12 +177,15 @@ export const ar: LanguageTranslation = {
|
||||
description: 'أنشئ جدولاً للبدء',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'العلاقات',
|
||||
refs_section: {
|
||||
refs: 'المراجع',
|
||||
filter: 'تصفية',
|
||||
add_relationship: 'إضافة علاقة',
|
||||
collapse: 'طي الكل',
|
||||
add_relationship: 'إضافة علاقة',
|
||||
relationships: 'العلاقات',
|
||||
dependencies: 'الاعتمادات',
|
||||
relationship: {
|
||||
relationship: 'العلاقة',
|
||||
primary: 'الجدول الأساسي',
|
||||
foreign: 'الجدول المرتبط',
|
||||
cardinality: 'الكاردينالية',
|
||||
@@ -187,16 +195,8 @@ export const ar: LanguageTranslation = {
|
||||
delete_relationship: 'حذف',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'لا توجد علاقات',
|
||||
description: 'إنشئ علاقة لربط الجداول',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'الاعتمادات',
|
||||
filter: 'تصفية',
|
||||
collapse: 'طي الكل',
|
||||
dependency: {
|
||||
dependency: 'الاعتماد',
|
||||
table: 'الجدول',
|
||||
dependent_table: 'عرض الاعتمادات',
|
||||
delete_dependency: 'حذف',
|
||||
@@ -206,8 +206,8 @@ export const ar: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'لا توجد اعتمادات',
|
||||
description: 'إنشاء اعتماد للبدء',
|
||||
title: 'لا توجد علاقات',
|
||||
description: 'إنشاء علاقة للبدء',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -248,12 +248,16 @@ export const ar: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'لم يتم تحديد قيم التعداد',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -267,8 +271,13 @@ export const ar: LanguageTranslation = {
|
||||
show_all: 'عرض الكل',
|
||||
undo: 'تراجع',
|
||||
redo: 'إعادة',
|
||||
reorder_diagram: 'إعادة ترتيب الرسم البياني',
|
||||
reorder_diagram: 'ترتيب تلقائي للرسم البياني',
|
||||
highlight_overlapping_tables: 'تمييز الجداول المتداخلة',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -305,7 +314,7 @@ export const ar: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'فتح مخطط',
|
||||
title: 'فتح قاعدة بيانات',
|
||||
description: 'اختر مخططًا لفتحه من القائمة ادناه',
|
||||
table_columns: {
|
||||
name: 'الإسم',
|
||||
@@ -315,6 +324,12 @@ export const ar: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'إلغاء',
|
||||
open: 'فتح',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'فتح',
|
||||
duplicate: 'تكرار',
|
||||
delete: 'حذف',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -400,6 +415,13 @@ export const ar: LanguageTranslation = {
|
||||
cancel: 'إلغاء',
|
||||
confirm: 'تغيير',
|
||||
},
|
||||
create_table_schema_dialog: {
|
||||
title: 'إنشاء مخطط جديد',
|
||||
description:
|
||||
'لا توجد مخططات حتى الآن. قم بإنشاء أول مخطط لتنظيم جداولك.',
|
||||
create: 'إنشاء',
|
||||
cancel: 'إلغاء',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: '!ساعدنا على التحسن',
|
||||
@@ -453,6 +475,7 @@ export const ar: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'جدول جديد',
|
||||
new_view: 'عرض جديد',
|
||||
new_relationship: 'علاقة جديدة',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -474,6 +497,8 @@ export const ar: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'اللغة',
|
||||
},
|
||||
on: 'تشغيل',
|
||||
off: 'إيقاف',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const bn: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'নতুন',
|
||||
browse: 'ব্রাউজ',
|
||||
tables: 'টেবিল',
|
||||
refs: 'রেফস',
|
||||
areas: 'এলাকা',
|
||||
dependencies: 'নির্ভরতা',
|
||||
custom_types: 'কাস্টম টাইপ',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'ফাইল',
|
||||
new: 'নতুন',
|
||||
open: 'খুলুন',
|
||||
actions: {
|
||||
actions: 'কার্য',
|
||||
new: 'নতুন...',
|
||||
browse: 'ব্রাউজ করুন...',
|
||||
save: 'সংরক্ষণ করুন',
|
||||
import: 'ডাটাবেস আমদানি করুন',
|
||||
export_sql: 'SQL রপ্তানি করুন',
|
||||
export_as: 'রূপে রপ্তানি করুন',
|
||||
delete_diagram: 'ডায়াগ্রাম মুছুন',
|
||||
exit: 'প্রস্থান করুন',
|
||||
delete_diagram: 'মুছুন',
|
||||
},
|
||||
edit: {
|
||||
edit: 'সম্পাদনা',
|
||||
@@ -26,7 +34,10 @@ export const bn: LanguageTranslation = {
|
||||
hide_sidebar: 'সাইডবার লুকান',
|
||||
hide_cardinality: 'কার্ডিনালিটি লুকান',
|
||||
show_cardinality: 'কার্ডিনালিটি দেখান',
|
||||
hide_field_attributes: 'ফিল্ড অ্যাট্রিবিউট লুকান',
|
||||
show_field_attributes: 'ফিল্ড অ্যাট্রিবিউট দেখান',
|
||||
zoom_on_scroll: 'স্ক্রলে জুম করুন',
|
||||
show_views: 'ডাটাবেস ভিউ',
|
||||
theme: 'থিম',
|
||||
show_dependencies: 'নির্ভরতাগুলি দেখান',
|
||||
hide_dependencies: 'নির্ভরতাগুলি লুকান',
|
||||
@@ -64,22 +75,13 @@ export const bn: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'ডায়াগ্রাম পুনর্বিন্যাস করুন',
|
||||
title: 'স্বয়ংক্রিয় ডায়াগ্রাম সাজান',
|
||||
description:
|
||||
'এই কাজটি ডায়াগ্রামের সমস্ত টেবিল পুনর্বিন্যাস করবে। আপনি কি চালিয়ে যেতে চান?',
|
||||
reorder: 'পুনর্বিন্যাস করুন',
|
||||
reorder: 'স্বয়ংক্রিয় সাজান',
|
||||
cancel: 'বাতিল করুন',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'বহু স্কিমা',
|
||||
description:
|
||||
'{{schemasCount}} স্কিমা এই ডায়াগ্রামে রয়েছে। বর্তমানে প্রদর্শিত: {{formattedSchemas}}।',
|
||||
dont_show_again: 'পুনরায় দেখাবেন না',
|
||||
change_schema: 'পরিবর্তন করুন',
|
||||
none: 'কিছুই না',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'কপি ব্যর্থ হয়েছে',
|
||||
@@ -114,14 +116,11 @@ export const bn: LanguageTranslation = {
|
||||
copied: 'অনুলিপি সম্পন্ন!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'স্কিমা:',
|
||||
filter_by_schema: 'স্কিমা দ্বারা ফিল্টার করুন',
|
||||
search_schema: 'স্কিমা খুঁজুন...',
|
||||
no_schemas_found: 'কোনো স্কিমা পাওয়া যায়নি।',
|
||||
view_all_options: 'সমস্ত বিকল্প দেখুন...',
|
||||
tables_section: {
|
||||
tables: 'টেবিল',
|
||||
add_table: 'টেবিল যোগ করুন',
|
||||
add_view: 'ভিউ যোগ করুন',
|
||||
filter: 'ফিল্টার',
|
||||
collapse: 'সব ভাঁজ করুন',
|
||||
// TODO: Translate
|
||||
@@ -147,16 +146,23 @@ export const bn: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'ফিল্ড কর্ম',
|
||||
unique: 'অদ্বিতীয়',
|
||||
auto_increment: 'স্বয়ংক্রিয় বৃদ্ধি',
|
||||
comments: 'মন্তব্য',
|
||||
no_comments: 'কোনো মন্তব্য নেই',
|
||||
delete_field: 'ফিল্ড মুছুন',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'নির্ভুলতা',
|
||||
scale: 'স্কেল',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'ইনডেক্স কর্ম',
|
||||
name: 'নাম',
|
||||
unique: 'অদ্বিতীয়',
|
||||
index_type: 'ইনডেক্স ধরন',
|
||||
delete_index: 'ইনডেক্স মুছুন',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -173,14 +179,17 @@ export const bn: LanguageTranslation = {
|
||||
description: 'শুরু করতে একটি টেবিল তৈরি করুন',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'সম্পর্ক',
|
||||
refs_section: {
|
||||
refs: 'রেফস',
|
||||
filter: 'ফিল্টার',
|
||||
add_relationship: 'সম্পর্ক যোগ করুন',
|
||||
collapse: 'সব ভাঁজ করুন',
|
||||
add_relationship: 'সম্পর্ক যোগ করুন',
|
||||
relationships: 'সম্পর্ক',
|
||||
dependencies: 'নির্ভরতাগুলি',
|
||||
relationship: {
|
||||
relationship: 'সম্পর্ক',
|
||||
primary: 'প্রাথমিক টেবিল',
|
||||
foreign: 'বিদেশি টেবিল',
|
||||
foreign: 'রেফারেন্স করা টেবিল',
|
||||
cardinality: 'কার্ডিনালিটি',
|
||||
delete_relationship: 'মুছুন',
|
||||
relationship_actions: {
|
||||
@@ -188,27 +197,19 @@ export const bn: LanguageTranslation = {
|
||||
delete_relationship: 'মুছুন',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'কোনো সম্পর্ক নেই',
|
||||
description: 'টেবিল সংযোগ করতে একটি সম্পর্ক তৈরি করুন',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'নির্ভরতাগুলি',
|
||||
filter: 'ফিল্টার',
|
||||
collapse: 'ভাঁজ করুন',
|
||||
dependency: {
|
||||
dependency: 'নির্ভরতা',
|
||||
table: 'টেবিল',
|
||||
dependent_table: 'নির্ভরশীল টেবিল',
|
||||
delete_dependency: 'নির্ভরতা মুছুন',
|
||||
dependent_table: 'নির্ভরশীল ভিউ',
|
||||
delete_dependency: 'মুছুন',
|
||||
dependency_actions: {
|
||||
title: 'কর্ম',
|
||||
delete_dependency: 'নির্ভরতা মুছুন',
|
||||
delete_dependency: 'মুছুন',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'কোনো নির্ভরতাগুলি নেই',
|
||||
description: 'এই অংশে কোনো নির্ভরতা উপলব্ধ নেই।',
|
||||
title: 'কোনো সম্পর্ক নেই',
|
||||
description: 'শুরু করতে একটি সম্পর্ক তৈরি করুন',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -248,12 +249,16 @@ export const bn: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'কোন enum মান সংজ্ঞায়িত নেই',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -267,8 +272,14 @@ export const bn: LanguageTranslation = {
|
||||
show_all: 'সব দেখান',
|
||||
undo: 'পূর্বাবস্থায় ফিরুন',
|
||||
redo: 'পুনরায় করুন',
|
||||
reorder_diagram: 'ডায়াগ্রাম পুনর্বিন্যাস করুন',
|
||||
reorder_diagram: 'স্বয়ংক্রিয় ডায়াগ্রাম সাজান',
|
||||
highlight_overlapping_tables: 'ওভারল্যাপিং টেবিল হাইলাইট করুন',
|
||||
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -305,7 +316,7 @@ export const bn: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'চিত্র খুলুন',
|
||||
title: 'ডেটাবেস খুলুন',
|
||||
description: 'নিচের তালিকা থেকে একটি চিত্র নির্বাচন করুন।',
|
||||
table_columns: {
|
||||
name: 'নাম',
|
||||
@@ -315,6 +326,12 @@ export const bn: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'বাতিল করুন',
|
||||
open: 'খুলুন',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'খুলুন',
|
||||
duplicate: 'ডুপ্লিকেট',
|
||||
delete: 'মুছুন',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -400,6 +417,13 @@ export const bn: LanguageTranslation = {
|
||||
cancel: 'বাতিল করুন',
|
||||
confirm: 'পরিবর্তন করুন',
|
||||
},
|
||||
create_table_schema_dialog: {
|
||||
title: 'নতুন স্কিমা তৈরি করুন',
|
||||
description:
|
||||
'এখনও কোনো স্কিমা নেই। আপনার টেবিলগুলি সংগঠিত করতে আপনার প্রথম স্কিমা তৈরি করুন।',
|
||||
create: 'তৈরি করুন',
|
||||
cancel: 'বাতিল করুন',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'আমাদের উন্নত করতে সাহায্য করুন!',
|
||||
@@ -456,6 +480,7 @@ export const bn: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'নতুন টেবিল',
|
||||
new_view: 'নতুন ভিউ',
|
||||
new_relationship: 'নতুন সম্পর্ক',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -477,6 +502,9 @@ export const bn: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'ভাষা পরিবর্তন করুন',
|
||||
},
|
||||
|
||||
on: 'চালু',
|
||||
off: 'বন্ধ',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const de: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Neu',
|
||||
browse: 'Durchsuchen',
|
||||
tables: 'Tabellen',
|
||||
refs: 'Refs',
|
||||
areas: 'Bereiche',
|
||||
dependencies: 'Abhängigkeiten',
|
||||
custom_types: 'Benutzerdefinierte Typen',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'Datei',
|
||||
new: 'Neu',
|
||||
open: 'Öffnen',
|
||||
actions: {
|
||||
actions: 'Aktionen',
|
||||
new: 'Neu...',
|
||||
browse: 'Durchsuchen...',
|
||||
save: 'Speichern',
|
||||
import: 'Datenbank importieren',
|
||||
export_sql: 'SQL exportieren',
|
||||
export_as: 'Exportieren als',
|
||||
delete_diagram: 'Diagramm löschen',
|
||||
exit: 'Beenden',
|
||||
delete_diagram: 'Löschen',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Bearbeiten',
|
||||
@@ -26,7 +34,10 @@ export const de: LanguageTranslation = {
|
||||
hide_sidebar: 'Seitenleiste ausblenden',
|
||||
hide_cardinality: 'Kardinalität ausblenden',
|
||||
show_cardinality: 'Kardinalität anzeigen',
|
||||
hide_field_attributes: 'Feldattribute ausblenden',
|
||||
show_field_attributes: 'Feldattribute anzeigen',
|
||||
zoom_on_scroll: 'Zoom beim Scrollen',
|
||||
show_views: 'Datenbankansichten',
|
||||
theme: 'Stil',
|
||||
show_dependencies: 'Abhängigkeiten anzeigen',
|
||||
hide_dependencies: 'Abhängigkeiten ausblenden',
|
||||
@@ -64,22 +75,13 @@ export const de: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Diagramm neu anordnen',
|
||||
title: 'Diagramm automatisch anordnen',
|
||||
description:
|
||||
'Diese Aktion wird alle Tabellen im Diagramm neu anordnen. Möchten Sie fortfahren?',
|
||||
reorder: 'Neu anordnen',
|
||||
reorder: 'Automatisch anordnen',
|
||||
cancel: 'Abbrechen',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Mehrere Schemas',
|
||||
description:
|
||||
'{{schemasCount}} Schemas in diesem Diagramm. Derzeit angezeigt: {{formattedSchemas}}.',
|
||||
dont_show_again: 'Nicht erneut anzeigen',
|
||||
change_schema: 'Schema ändern',
|
||||
none: 'Keine',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'Kopieren fehlgeschlagen',
|
||||
@@ -115,14 +117,11 @@ export const de: LanguageTranslation = {
|
||||
copied: 'Kopiert!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Schema:',
|
||||
filter_by_schema: 'Nach Schema filtern',
|
||||
search_schema: 'Schema suchen...',
|
||||
no_schemas_found: 'Keine Schemas gefunden.',
|
||||
view_all_options: 'Alle Optionen anzeigen...',
|
||||
tables_section: {
|
||||
tables: 'Tabellen',
|
||||
add_table: 'Tabelle hinzufügen',
|
||||
add_view: 'Ansicht hinzufügen',
|
||||
filter: 'Filter',
|
||||
collapse: 'Alle einklappen',
|
||||
// TODO: Translate
|
||||
@@ -148,16 +147,23 @@ export const de: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'Feldattribute',
|
||||
unique: 'Eindeutig',
|
||||
auto_increment: 'Automatisch hochzählen',
|
||||
comments: 'Kommentare',
|
||||
no_comments: 'Keine Kommentare',
|
||||
delete_field: 'Feld löschen',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'Präzision',
|
||||
scale: 'Skalierung',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Indexattribute',
|
||||
name: 'Name',
|
||||
unique: 'Eindeutig',
|
||||
index_type: 'Indextyp',
|
||||
delete_index: 'Index löschen',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -174,32 +180,26 @@ export const de: LanguageTranslation = {
|
||||
description: 'Erstellen Sie eine Tabelle, um zu beginnen',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'Beziehungen',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Filter',
|
||||
add_relationship: 'Beziehung hinzufügen',
|
||||
collapse: 'Alle einklappen',
|
||||
add_relationship: 'Beziehung hinzufügen',
|
||||
relationships: 'Beziehungen',
|
||||
dependencies: 'Abhängigkeiten',
|
||||
relationship: {
|
||||
relationship: 'Beziehung',
|
||||
primary: 'Primäre Tabelle',
|
||||
foreign: 'Referenzierte Tabelle',
|
||||
cardinality: 'Kardinalität',
|
||||
delete_relationship: 'Beziehung löschen',
|
||||
delete_relationship: 'Löschen',
|
||||
relationship_actions: {
|
||||
title: 'Aktionen',
|
||||
delete_relationship: 'Beziehung löschen',
|
||||
delete_relationship: 'Löschen',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Keine Beziehungen',
|
||||
description:
|
||||
'Erstellen Sie eine Beziehung, um Tabellen zu verbinden',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Abhängigkeiten',
|
||||
filter: 'Filter',
|
||||
collapse: 'Alle einklappen',
|
||||
dependency: {
|
||||
dependency: 'Abhängigkeit',
|
||||
table: 'Tabelle',
|
||||
dependent_table: 'Abhängige Ansicht',
|
||||
delete_dependency: 'Löschen',
|
||||
@@ -209,8 +209,8 @@ export const de: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Keine Abhängigkeiten',
|
||||
description: 'Erstellen Sie eine Ansicht, um zu beginnen',
|
||||
title: 'Keine Beziehungen',
|
||||
description: 'Erstellen Sie eine Beziehung, um zu beginnen',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -250,12 +250,16 @@ export const de: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'Keine Enum-Werte definiert',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -269,8 +273,15 @@ export const de: LanguageTranslation = {
|
||||
show_all: 'Alle anzeigen',
|
||||
undo: 'Rückgängig',
|
||||
redo: 'Wiederholen',
|
||||
reorder_diagram: 'Diagramm neu anordnen',
|
||||
reorder_diagram: 'Diagramm automatisch anordnen',
|
||||
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'Überlappende Tabellen hervorheben',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -308,7 +319,7 @@ export const de: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Diagramm öffnen',
|
||||
title: 'Datenbank öffnen',
|
||||
description: 'Wählen Sie ein Diagramm aus der Liste unten aus.',
|
||||
table_columns: {
|
||||
name: 'Name',
|
||||
@@ -318,6 +329,12 @@ export const de: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'Abbrechen',
|
||||
open: 'Öffnen',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Öffnen',
|
||||
duplicate: 'Duplizieren',
|
||||
delete: 'Löschen',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -403,6 +420,13 @@ export const de: LanguageTranslation = {
|
||||
cancel: 'Abbrechen',
|
||||
confirm: 'Ändern',
|
||||
},
|
||||
create_table_schema_dialog: {
|
||||
title: 'Neues Schema erstellen',
|
||||
description:
|
||||
'Es existieren noch keine Schemas. Erstellen Sie Ihr erstes Schema, um Ihre Tabellen zu organisieren.',
|
||||
create: 'Erstellen',
|
||||
cancel: 'Abbrechen',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'Hilf uns, uns zu verbessern!',
|
||||
@@ -459,6 +483,7 @@ export const de: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'Neue Tabelle',
|
||||
new_view: 'Neue Ansicht',
|
||||
new_relationship: 'Neue Beziehung',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -481,6 +506,9 @@ export const de: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'Sprache',
|
||||
},
|
||||
|
||||
on: 'Ein',
|
||||
off: 'Aus',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata } from '../types';
|
||||
|
||||
export const en = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'New',
|
||||
browse: 'Browse',
|
||||
tables: 'Tables',
|
||||
refs: 'Refs',
|
||||
areas: 'Areas',
|
||||
dependencies: 'Dependencies',
|
||||
custom_types: 'Custom Types',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'File',
|
||||
new: 'New',
|
||||
open: 'Open',
|
||||
actions: {
|
||||
actions: 'Actions',
|
||||
new: 'New...',
|
||||
browse: 'Browse...',
|
||||
save: 'Save',
|
||||
import: 'Import',
|
||||
export_sql: 'Export SQL',
|
||||
export_as: 'Export as',
|
||||
delete_diagram: 'Delete Diagram',
|
||||
exit: 'Exit',
|
||||
delete_diagram: 'Delete',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Edit',
|
||||
@@ -26,7 +34,10 @@ export const en = {
|
||||
hide_sidebar: 'Hide Sidebar',
|
||||
hide_cardinality: 'Hide Cardinality',
|
||||
show_cardinality: 'Show Cardinality',
|
||||
hide_field_attributes: 'Hide Field Attributes',
|
||||
show_field_attributes: 'Show Field Attributes',
|
||||
zoom_on_scroll: 'Zoom on Scroll',
|
||||
show_views: 'Database Views',
|
||||
theme: 'Theme',
|
||||
show_dependencies: 'Show Dependencies',
|
||||
hide_dependencies: 'Hide Dependencies',
|
||||
@@ -62,22 +73,13 @@ export const en = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Reorder Diagram',
|
||||
title: 'Auto Arrange Diagram',
|
||||
description:
|
||||
'This action will rearrange all tables in the diagram. Do you want to continue?',
|
||||
reorder: 'Reorder',
|
||||
reorder: 'Auto Arrange',
|
||||
cancel: 'Cancel',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Multiple Schemas',
|
||||
description:
|
||||
'{{schemasCount}} schemas in this diagram. Currently displaying: {{formattedSchemas}}.',
|
||||
dont_show_again: "Don't show again",
|
||||
change_schema: 'Change',
|
||||
none: 'none',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'Copy failed',
|
||||
@@ -112,14 +114,11 @@ export const en = {
|
||||
copied: 'Copied!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Schema:',
|
||||
filter_by_schema: 'Filter by schema',
|
||||
search_schema: 'Search schema...',
|
||||
no_schemas_found: 'No schemas found.',
|
||||
view_all_options: 'View all Options...',
|
||||
tables_section: {
|
||||
tables: 'Tables',
|
||||
add_table: 'Add Table',
|
||||
add_view: 'Add View',
|
||||
filter: 'Filter',
|
||||
collapse: 'Collapse All',
|
||||
clear: 'Clear Filter',
|
||||
@@ -143,15 +142,21 @@ export const en = {
|
||||
field_actions: {
|
||||
title: 'Field Attributes',
|
||||
unique: 'Unique',
|
||||
auto_increment: 'Auto Increment',
|
||||
character_length: 'Max Length',
|
||||
precision: 'Precision',
|
||||
scale: 'Scale',
|
||||
comments: 'Comments',
|
||||
no_comments: 'No comments',
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
delete_field: 'Delete Field',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Index Attributes',
|
||||
name: 'Name',
|
||||
unique: 'Unique',
|
||||
index_type: 'Index Type',
|
||||
delete_index: 'Delete Index',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -168,12 +173,15 @@ export const en = {
|
||||
description: 'Create a table to get started',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'Relationships',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Filter',
|
||||
add_relationship: 'Add Relationship',
|
||||
collapse: 'Collapse All',
|
||||
add_relationship: 'Add Relationship',
|
||||
relationships: 'Relationships',
|
||||
dependencies: 'Dependencies',
|
||||
relationship: {
|
||||
relationship: 'Relationship',
|
||||
primary: 'Primary Table',
|
||||
foreign: 'Referenced Table',
|
||||
cardinality: 'Cardinality',
|
||||
@@ -183,16 +191,8 @@ export const en = {
|
||||
delete_relationship: 'Delete',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'No relationships',
|
||||
description: 'Create a relationship to connect tables',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Dependencies',
|
||||
filter: 'Filter',
|
||||
collapse: 'Collapse All',
|
||||
dependency: {
|
||||
dependency: 'Dependency',
|
||||
table: 'Table',
|
||||
dependent_table: 'Dependent View',
|
||||
delete_dependency: 'Delete',
|
||||
@@ -202,8 +202,8 @@ export const en = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'No dependencies',
|
||||
description: 'Create a view to get started',
|
||||
title: 'No relationships',
|
||||
description: 'Create a relationship to get started',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -242,11 +242,15 @@ export const en = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'No enum values defined',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
delete_custom_type: 'Delete',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
@@ -261,8 +265,12 @@ export const en = {
|
||||
show_all: 'Show All',
|
||||
undo: 'Undo',
|
||||
redo: 'Redo',
|
||||
reorder_diagram: 'Reorder Diagram',
|
||||
reorder_diagram: 'Auto Arrange Diagram',
|
||||
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -299,7 +307,7 @@ export const en = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Open Diagram',
|
||||
title: 'Open Database',
|
||||
description: 'Select a diagram to open from the list below.',
|
||||
table_columns: {
|
||||
name: 'Name',
|
||||
@@ -309,6 +317,12 @@ export const en = {
|
||||
},
|
||||
cancel: 'Cancel',
|
||||
open: 'Open',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Open',
|
||||
duplicate: 'Duplicate',
|
||||
delete: 'Delete',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -394,6 +408,14 @@ export const en = {
|
||||
confirm: 'Change',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'Create New Schema',
|
||||
description:
|
||||
'No schemas exist yet. Create your first schema to organize your tables.',
|
||||
create: 'Create',
|
||||
cancel: 'Cancel',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'Help us improve!',
|
||||
description:
|
||||
@@ -448,6 +470,7 @@ export const en = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'New Table',
|
||||
new_view: 'New View',
|
||||
new_relationship: 'New Relationship',
|
||||
new_area: 'New Area',
|
||||
},
|
||||
@@ -468,6 +491,9 @@ export const en = {
|
||||
language_select: {
|
||||
change_language: 'Language',
|
||||
},
|
||||
|
||||
on: 'On',
|
||||
off: 'Off',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const es: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Nuevo',
|
||||
browse: 'Examinar',
|
||||
tables: 'Tablas',
|
||||
refs: 'Refs',
|
||||
areas: 'Áreas',
|
||||
dependencies: 'Dependencias',
|
||||
custom_types: 'Tipos Personalizados',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'Archivo',
|
||||
new: 'Nuevo',
|
||||
open: 'Abrir',
|
||||
actions: {
|
||||
actions: 'Acciones',
|
||||
new: 'Nuevo...',
|
||||
browse: 'Examinar...',
|
||||
save: 'Guardar',
|
||||
import: 'Importar Base de Datos',
|
||||
export_sql: 'Exportar SQL',
|
||||
export_as: 'Exportar como',
|
||||
delete_diagram: 'Eliminar Diagrama',
|
||||
exit: 'Salir',
|
||||
delete_diagram: 'Eliminar',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Editar',
|
||||
@@ -24,9 +32,12 @@ export const es: LanguageTranslation = {
|
||||
view: 'Ver',
|
||||
hide_cardinality: 'Ocultar Cardinalidad',
|
||||
show_cardinality: 'Mostrar Cardinalidad',
|
||||
show_field_attributes: 'Mostrar Atributos de Campo',
|
||||
hide_field_attributes: 'Ocultar Atributos de Campo',
|
||||
show_sidebar: 'Mostrar Barra Lateral',
|
||||
hide_sidebar: 'Ocultar Barra Lateral',
|
||||
zoom_on_scroll: 'Zoom al Desplazarse',
|
||||
show_views: 'Vistas de Base de Datos',
|
||||
theme: 'Tema',
|
||||
show_dependencies: 'Mostrar dependencias',
|
||||
hide_dependencies: 'Ocultar dependencias',
|
||||
@@ -63,10 +74,10 @@ export const es: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Reordenar Diagrama',
|
||||
title: 'Organizar Diagrama Automáticamente',
|
||||
description:
|
||||
'Esta acción reorganizará todas las tablas en el diagrama. ¿Deseas continuar?',
|
||||
reorder: 'Reordenar',
|
||||
reorder: 'Organizar Automáticamente',
|
||||
cancel: 'Cancelar',
|
||||
},
|
||||
|
||||
@@ -104,14 +115,11 @@ export const es: LanguageTranslation = {
|
||||
copied: 'Copied!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Esquema:',
|
||||
filter_by_schema: 'Filtrar por esquema',
|
||||
search_schema: 'Buscar esquema...',
|
||||
no_schemas_found: 'No se encontraron esquemas.',
|
||||
view_all_options: 'Ver todas las opciones...',
|
||||
tables_section: {
|
||||
tables: 'Tablas',
|
||||
add_table: 'Agregar Tabla',
|
||||
add_view: 'Agregar Vista',
|
||||
filter: 'Filtrar',
|
||||
collapse: 'Colapsar Todo',
|
||||
// TODO: Translate
|
||||
@@ -137,16 +145,23 @@ export const es: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'Atributos del Campo',
|
||||
unique: 'Único',
|
||||
auto_increment: 'Autoincremento',
|
||||
comments: 'Comentarios',
|
||||
no_comments: 'Sin comentarios',
|
||||
delete_field: 'Eliminar Campo',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'Precisión',
|
||||
scale: 'Escala',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Atributos del Índice',
|
||||
name: 'Nombre',
|
||||
unique: 'Único',
|
||||
index_type: 'Tipo de Índice',
|
||||
delete_index: 'Eliminar Índice',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -163,14 +178,17 @@ export const es: LanguageTranslation = {
|
||||
description: 'Crea una tabla para comenzar',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'Relaciones',
|
||||
add_relationship: 'Agregar Relación',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Filtrar',
|
||||
collapse: 'Colapsar Todo',
|
||||
add_relationship: 'Agregar Relación',
|
||||
relationships: 'Relaciones',
|
||||
dependencies: 'Dependencias',
|
||||
relationship: {
|
||||
primary: 'Primaria',
|
||||
foreign: 'Foránea',
|
||||
relationship: 'Relación',
|
||||
primary: 'Tabla Primaria',
|
||||
foreign: 'Tabla Referenciada',
|
||||
cardinality: 'Cardinalidad',
|
||||
delete_relationship: 'Eliminar',
|
||||
relationship_actions: {
|
||||
@@ -178,18 +196,10 @@ export const es: LanguageTranslation = {
|
||||
delete_relationship: 'Eliminar',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'No hay relaciones',
|
||||
description: 'Crea una relación para conectar tablas',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Dependencias',
|
||||
filter: 'Filtro',
|
||||
collapse: 'Colapsar todo',
|
||||
dependency: {
|
||||
dependency: 'Dependencia',
|
||||
table: 'Tabla',
|
||||
dependent_table: 'Vista dependiente',
|
||||
dependent_table: 'Vista Dependiente',
|
||||
delete_dependency: 'Eliminar',
|
||||
dependency_actions: {
|
||||
title: 'Acciones',
|
||||
@@ -197,8 +207,8 @@ export const es: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Sin dependencias',
|
||||
description: 'Crea una vista para comenzar',
|
||||
title: 'Sin relaciones',
|
||||
description: 'Crea una relación para comenzar',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -238,12 +248,16 @@ export const es: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'No hay valores de enum definidos',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -257,8 +271,14 @@ export const es: LanguageTranslation = {
|
||||
show_all: 'Mostrar Todo',
|
||||
undo: 'Deshacer',
|
||||
redo: 'Rehacer',
|
||||
reorder_diagram: 'Reordenar Diagrama',
|
||||
reorder_diagram: 'Organizar Diagrama Automáticamente',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'Resaltar tablas superpuestas',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -296,7 +316,7 @@ export const es: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Abrir Diagrama',
|
||||
title: 'Abrir Base de Datos',
|
||||
description:
|
||||
'Selecciona un diagrama para abrir de la lista a continuación.',
|
||||
table_columns: {
|
||||
@@ -307,6 +327,12 @@ export const es: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'Cancelar',
|
||||
open: 'Abrir',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Abrir',
|
||||
duplicate: 'Duplicar',
|
||||
delete: 'Eliminar',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -392,6 +418,13 @@ export const es: LanguageTranslation = {
|
||||
cancel: 'Cancelar',
|
||||
confirm: 'Cambiar',
|
||||
},
|
||||
create_table_schema_dialog: {
|
||||
title: 'Crear Nuevo Esquema',
|
||||
description:
|
||||
'Aún no existen esquemas. Crea tu primer esquema para organizar tus tablas.',
|
||||
create: 'Crear',
|
||||
cancel: 'Cancelar',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: '¡Ayúdanos a mejorar!',
|
||||
@@ -401,14 +434,6 @@ export const es: LanguageTranslation = {
|
||||
confirm: '¡Claro!',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Múltiples Esquemas',
|
||||
description:
|
||||
'{{schemasCount}} esquemas en este diagrama. Actualmente mostrando: {{formattedSchemas}}.',
|
||||
dont_show_again: 'No mostrar de nuevo',
|
||||
change_schema: 'Cambiar',
|
||||
none: 'nada',
|
||||
},
|
||||
// TODO: Translate
|
||||
export_diagram_dialog: {
|
||||
title: 'Export Diagram',
|
||||
@@ -457,6 +482,7 @@ export const es: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'Nueva Tabla',
|
||||
new_view: 'Nueva Vista',
|
||||
new_relationship: 'Nueva Relación',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -479,6 +505,9 @@ export const es: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'Idioma',
|
||||
},
|
||||
|
||||
on: 'Encendido',
|
||||
off: 'Apagado',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const fr: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Nouveau',
|
||||
browse: 'Parcourir',
|
||||
tables: 'Tables',
|
||||
refs: 'Refs',
|
||||
areas: 'Zones',
|
||||
dependencies: 'Dépendances',
|
||||
custom_types: 'Types Personnalisés',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'Fichier',
|
||||
new: 'Nouveau',
|
||||
open: 'Ouvrir',
|
||||
actions: {
|
||||
actions: 'Actions',
|
||||
new: 'Nouveau...',
|
||||
browse: 'Parcourir...',
|
||||
save: 'Enregistrer',
|
||||
import: 'Importer Base de Données',
|
||||
export_sql: 'Exporter SQL',
|
||||
export_as: 'Exporter en tant que',
|
||||
delete_diagram: 'Supprimer le Diagramme',
|
||||
exit: 'Quitter',
|
||||
delete_diagram: 'Supprimer',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Édition',
|
||||
@@ -26,7 +34,10 @@ export const fr: LanguageTranslation = {
|
||||
hide_sidebar: 'Cacher la Barre Latérale',
|
||||
hide_cardinality: 'Cacher la Cardinalité',
|
||||
show_cardinality: 'Afficher la Cardinalité',
|
||||
hide_field_attributes: 'Masquer les Attributs de Champ',
|
||||
show_field_attributes: 'Afficher les Attributs de Champ',
|
||||
zoom_on_scroll: 'Zoom sur le Défilement',
|
||||
show_views: 'Vues de Base de Données',
|
||||
theme: 'Thème',
|
||||
show_dependencies: 'Afficher les Dépendances',
|
||||
hide_dependencies: 'Masquer les Dépendances',
|
||||
@@ -62,10 +73,10 @@ export const fr: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Réorganiser le Diagramme',
|
||||
title: 'Organiser Automatiquement le Diagramme',
|
||||
description:
|
||||
'Cette action réorganisera toutes les tables dans le diagramme. Voulez-vous continuer ?',
|
||||
reorder: 'Réorganiser',
|
||||
reorder: 'Organiser Automatiquement',
|
||||
cancel: 'Annuler',
|
||||
},
|
||||
|
||||
@@ -103,14 +114,11 @@ export const fr: LanguageTranslation = {
|
||||
copied: 'Copié !',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Schéma:',
|
||||
filter_by_schema: 'Filtrer par schéma',
|
||||
search_schema: 'Rechercher un schéma...',
|
||||
no_schemas_found: 'Aucun schéma trouvé.',
|
||||
view_all_options: 'Voir toutes les Options...',
|
||||
tables_section: {
|
||||
tables: 'Tables',
|
||||
add_table: 'Ajouter une Table',
|
||||
add_view: 'Ajouter une Vue',
|
||||
filter: 'Filtrer',
|
||||
collapse: 'Réduire Tout',
|
||||
clear: 'Effacer le Filtre',
|
||||
@@ -135,16 +143,23 @@ export const fr: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'Attributs du Champ',
|
||||
unique: 'Unique',
|
||||
auto_increment: 'Auto-incrément',
|
||||
comments: 'Commentaires',
|
||||
no_comments: 'Pas de commentaires',
|
||||
delete_field: 'Supprimer le Champ',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'Précision',
|
||||
scale: 'Échelle',
|
||||
},
|
||||
index_actions: {
|
||||
title: "Attributs de l'Index",
|
||||
name: 'Nom',
|
||||
unique: 'Unique',
|
||||
index_type: "Type d'index",
|
||||
delete_index: "Supprimer l'Index",
|
||||
},
|
||||
table_actions: {
|
||||
@@ -161,12 +176,15 @@ export const fr: LanguageTranslation = {
|
||||
description: 'Créez une table pour commencer',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'Relations',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Filtrer',
|
||||
add_relationship: 'Ajouter une Relation',
|
||||
collapse: 'Réduire Tout',
|
||||
add_relationship: 'Ajouter une Relation',
|
||||
relationships: 'Relations',
|
||||
dependencies: 'Dépendances',
|
||||
relationship: {
|
||||
relationship: 'Relation',
|
||||
primary: 'Table Principale',
|
||||
foreign: 'Table Référencée',
|
||||
cardinality: 'Cardinalité',
|
||||
@@ -176,16 +194,8 @@ export const fr: LanguageTranslation = {
|
||||
delete_relationship: 'Supprimer',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Aucune relation',
|
||||
description: 'Créez une relation pour connecter les tables',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Dépendances',
|
||||
filter: 'Filtrer',
|
||||
collapse: 'Réduire Tout',
|
||||
dependency: {
|
||||
dependency: 'Dépendance',
|
||||
table: 'Table',
|
||||
dependent_table: 'Vue Dépendante',
|
||||
delete_dependency: 'Supprimer',
|
||||
@@ -195,8 +205,8 @@ export const fr: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Aucune dépendance',
|
||||
description: 'Créez une vue pour commencer',
|
||||
title: 'Aucune relation',
|
||||
description: 'Créez une relation pour commencer',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -236,12 +246,16 @@ export const fr: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: "Aucune valeur d'énumération définie",
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -255,8 +269,14 @@ export const fr: LanguageTranslation = {
|
||||
show_all: 'Afficher Tout',
|
||||
undo: 'Annuler',
|
||||
redo: 'Rétablir',
|
||||
reorder_diagram: 'Réorganiser le Diagramme',
|
||||
reorder_diagram: 'Organiser Automatiquement le Diagramme',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'Surligner les tables chevauchées',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -293,7 +313,7 @@ export const fr: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Ouvrir Diagramme',
|
||||
title: 'Ouvrir Base de Données',
|
||||
description:
|
||||
'Sélectionnez un diagramme à ouvrir dans la liste ci-dessous.',
|
||||
table_columns: {
|
||||
@@ -304,6 +324,12 @@ export const fr: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'Annuler',
|
||||
open: 'Ouvrir',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Ouvrir',
|
||||
duplicate: 'Dupliquer',
|
||||
delete: 'Supprimer',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -341,15 +367,6 @@ export const fr: LanguageTranslation = {
|
||||
transparent_description: 'Remove background color from image.',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Schémas Multiples',
|
||||
description:
|
||||
'{{schemasCount}} schémas dans ce diagramme. Actuellement affiché(s) : {{formattedSchemas}}.',
|
||||
dont_show_again: 'Ne plus afficher',
|
||||
change_schema: 'Changer',
|
||||
none: 'Aucun',
|
||||
},
|
||||
|
||||
new_table_schema_dialog: {
|
||||
title: 'Sélectionner un Schéma',
|
||||
description:
|
||||
@@ -372,6 +389,13 @@ export const fr: LanguageTranslation = {
|
||||
cancel: 'Annuler',
|
||||
confirm: 'Modifier',
|
||||
},
|
||||
create_table_schema_dialog: {
|
||||
title: 'Créer un Nouveau Schéma',
|
||||
description:
|
||||
"Aucun schéma n'existe encore. Créez votre premier schéma pour organiser vos tables.",
|
||||
create: 'Créer',
|
||||
cancel: 'Annuler',
|
||||
},
|
||||
|
||||
create_relationship_dialog: {
|
||||
title: 'Créer une Relation',
|
||||
@@ -454,6 +478,7 @@ export const fr: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'Nouvelle Table',
|
||||
new_view: 'Nouvelle Vue',
|
||||
new_relationship: 'Nouvelle Relation',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -476,6 +501,9 @@ export const fr: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'Langue',
|
||||
},
|
||||
|
||||
on: 'Activé',
|
||||
off: 'Désactivé',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const gu: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'નવું',
|
||||
browse: 'બ્રાઉજ',
|
||||
tables: 'ટેબલો',
|
||||
refs: 'રેફ્સ',
|
||||
areas: 'ક્ષેત્રો',
|
||||
dependencies: 'નિર્ભરતાઓ',
|
||||
custom_types: 'કસ્ટમ ટાઇપ',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'ફાઇલ',
|
||||
new: 'નવું',
|
||||
open: 'ખોલો',
|
||||
actions: {
|
||||
actions: 'ક્રિયાઓ',
|
||||
new: 'નવું...',
|
||||
browse: 'બ્રાઉજ કરો...',
|
||||
save: 'સાચવો',
|
||||
import: 'ડેટાબેસ આયાત કરો',
|
||||
export_sql: 'SQL નિકાસ કરો',
|
||||
export_as: 'રૂપે નિકાસ કરો',
|
||||
delete_diagram: 'ડાયાગ્રામ કાઢી નાખો',
|
||||
exit: 'બહાર જાઓ',
|
||||
delete_diagram: 'કાઢી નાખો',
|
||||
},
|
||||
edit: {
|
||||
edit: 'ફેરફાર',
|
||||
@@ -26,7 +34,10 @@ export const gu: LanguageTranslation = {
|
||||
hide_sidebar: 'સાઇડબાર છુપાવો',
|
||||
hide_cardinality: 'કાર્ડિનાલિટી છુપાવો',
|
||||
show_cardinality: 'કાર્ડિનાલિટી બતાવો',
|
||||
hide_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ છુપાવો',
|
||||
show_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ બતાવો',
|
||||
zoom_on_scroll: 'સ્ક્રોલ પર ઝૂમ કરો',
|
||||
show_views: 'ડેટાબેઝ વ્યૂઝ',
|
||||
theme: 'થિમ',
|
||||
show_dependencies: 'નિર્ભરતાઓ બતાવો',
|
||||
hide_dependencies: 'નિર્ભરતાઓ છુપાવો',
|
||||
@@ -64,22 +75,13 @@ export const gu: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'ડાયાગ્રામ ફરી વ્યવસ્થિત કરો',
|
||||
title: 'ડાયાગ્રામ ઑટોમેટિક ગોઠવો',
|
||||
description:
|
||||
'આ ક્રિયા ડાયાગ્રામમાં બધી ટેબલ્સને ફરીથી વ્યવસ્થિત કરશે. શું તમે ચાલુ રાખવા માંગો છો?',
|
||||
reorder: 'ફરી વ્યવસ્થિત કરો',
|
||||
reorder: 'ઑટોમેટિક ગોઠવો',
|
||||
cancel: 'રદ કરો',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'કઈંક વધારે સ્કીમા',
|
||||
description:
|
||||
'{{schemasCount}} સ્કીમા આ ડાયાગ્રામમાં છે. હાલમાં દર્શાવેલ છે: {{formattedSchemas}}.',
|
||||
dont_show_again: 'ફરીથી ન બતાવો',
|
||||
change_schema: 'બદલો',
|
||||
none: 'કઈ નહીં',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'નકલ નિષ્ફળ',
|
||||
@@ -114,14 +116,11 @@ export const gu: LanguageTranslation = {
|
||||
copied: 'નકલ થયું!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'સ્કીમા:',
|
||||
filter_by_schema: 'સ્કીમા દ્વારા ફિલ્ટર કરો',
|
||||
search_schema: 'સ્કીમા શોધો...',
|
||||
no_schemas_found: 'કોઈ સ્કીમા મળ્યા નથી.',
|
||||
view_all_options: 'બધા વિકલ્પો જુઓ...',
|
||||
tables_section: {
|
||||
tables: 'ટેબલ્સ',
|
||||
add_table: 'ટેબલ ઉમેરો',
|
||||
add_view: 'વ્યૂ ઉમેરો',
|
||||
filter: 'ફિલ્ટર',
|
||||
collapse: 'બધાને સકુચિત કરો',
|
||||
// TODO: Translate
|
||||
@@ -148,16 +147,23 @@ export const gu: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'ફીલ્ડ લક્ષણો',
|
||||
unique: 'અદ્વિતીય',
|
||||
auto_increment: 'ઑટો ઇન્ક્રિમેન્ટ',
|
||||
comments: 'ટિપ્પણીઓ',
|
||||
no_comments: 'કોઈ ટિપ્પણીઓ નથી',
|
||||
delete_field: 'ફીલ્ડ કાઢી નાખો',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'ચોકસાઈ',
|
||||
scale: 'માપ',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'ઇન્ડેક્સ લક્ષણો',
|
||||
name: 'નામ',
|
||||
unique: 'અદ્વિતીય',
|
||||
index_type: 'ઇન્ડેક્સ પ્રકાર',
|
||||
delete_index: 'ઇન્ડેક્સ કાઢી નાખો',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -174,14 +180,17 @@ export const gu: LanguageTranslation = {
|
||||
description: 'શરૂ કરવા માટે એક ટેબલ બનાવો',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'સંબંધો',
|
||||
refs_section: {
|
||||
refs: 'રેફ્સ',
|
||||
filter: 'ફિલ્ટર',
|
||||
add_relationship: 'સંબંધ ઉમેરો',
|
||||
collapse: 'બધાને સકુચિત કરો',
|
||||
add_relationship: 'સંબંધ ઉમેરો',
|
||||
relationships: 'સંબંધો',
|
||||
dependencies: 'નિર્ભરતાઓ',
|
||||
relationship: {
|
||||
relationship: 'સંબંધ',
|
||||
primary: 'પ્રાથમિક ટેબલ',
|
||||
foreign: 'સંદર્ભ ટેબલ',
|
||||
foreign: 'સંદર્ભિત ટેબલ',
|
||||
cardinality: 'કાર્ડિનાલિટી',
|
||||
delete_relationship: 'કાઢી નાખો',
|
||||
relationship_actions: {
|
||||
@@ -189,27 +198,19 @@ export const gu: LanguageTranslation = {
|
||||
delete_relationship: 'કાઢી નાખો',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'કોઈ સંબંધો નથી',
|
||||
description: 'ટેબલ્સ કનેક્ટ કરવા માટે એક સંબંધ બનાવો',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'નિર્ભરતાઓ',
|
||||
filter: 'ફિલ્ટર',
|
||||
collapse: 'સિકોડો',
|
||||
dependency: {
|
||||
dependency: 'નિર્ભરતા',
|
||||
table: 'ટેબલ',
|
||||
dependent_table: 'આધાર રાખેલું ટેબલ',
|
||||
delete_dependency: 'નિર્ભરતા કાઢી નાખો',
|
||||
dependent_table: 'નિર્ભરશીલ વ્યૂ',
|
||||
delete_dependency: 'કાઢી નાખો',
|
||||
dependency_actions: {
|
||||
title: 'ક્રિયાઓ',
|
||||
delete_dependency: 'નિર્ભરતા કાઢી નાખો',
|
||||
delete_dependency: 'કાઢી નાખો',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'કોઈ નિર્ભરતાઓ નથી',
|
||||
description: 'આ વિભાગમાં કોઈ નિર્ભરતા ઉપલબ્ધ નથી.',
|
||||
title: 'કોઈ સંબંધો નથી',
|
||||
description: 'શરૂ કરવા માટે એક સંબંધ બનાવો',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -249,12 +250,16 @@ export const gu: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'કોઈ enum મૂલ્યો વ્યાખ્યાયિત નથી',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -268,8 +273,14 @@ export const gu: LanguageTranslation = {
|
||||
show_all: 'બધું બતાવો',
|
||||
undo: 'અનડુ',
|
||||
redo: 'રીડુ',
|
||||
reorder_diagram: 'ડાયાગ્રામ ફરીથી વ્યવસ્થિત કરો',
|
||||
reorder_diagram: 'ડાયાગ્રામ ઑટોમેટિક ગોઠવો',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'ઓવરલેપ કરતો ટેબલ હાઇલાઇટ કરો',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -305,7 +316,7 @@ export const gu: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'ડાયાગ્રામ ખોલો',
|
||||
title: 'ડેટાબેસ ખોલો',
|
||||
description: 'નીચેની યાદીમાંથી એક ડાયાગ્રામ પસંદ કરો.',
|
||||
table_columns: {
|
||||
name: 'નામ',
|
||||
@@ -315,6 +326,12 @@ export const gu: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'રદ કરો',
|
||||
open: 'ખોલો',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'ખોલો',
|
||||
duplicate: 'ડુપ્લિકેટ',
|
||||
delete: 'કાઢી નાખો',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -401,6 +418,14 @@ export const gu: LanguageTranslation = {
|
||||
confirm: 'બદલો',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'નવું સ્કીમા બનાવો',
|
||||
description:
|
||||
'હજી સુધી કોઈ સ્કીમા અસ્તિત્વમાં નથી. તમારા ટેબલ્સ ને વ્યવસ્થિત કરવા માટે તમારું પહેલું સ્કીમા બનાવો.',
|
||||
create: 'બનાવો',
|
||||
cancel: 'રદ કરો',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'અમને સુધારવામાં મદદ કરો!',
|
||||
description:
|
||||
@@ -456,6 +481,7 @@ export const gu: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'નવું ટેબલ',
|
||||
new_view: 'નવું વ્યૂ',
|
||||
new_relationship: 'નવો સંબંધ',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -477,6 +503,9 @@ export const gu: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'ભાષા બદલો',
|
||||
},
|
||||
|
||||
on: 'ચાલુ',
|
||||
off: 'બંધ',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const hi: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'नया',
|
||||
browse: 'ब्राउज़',
|
||||
tables: 'टेबल',
|
||||
refs: 'रेफ्स',
|
||||
areas: 'क्षेत्र',
|
||||
dependencies: 'निर्भरताएं',
|
||||
custom_types: 'कस्टम टाइप',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'फ़ाइल',
|
||||
new: 'नया',
|
||||
open: 'खोलें',
|
||||
actions: {
|
||||
actions: 'कार्य',
|
||||
new: 'नया...',
|
||||
browse: 'ब्राउज़ करें...',
|
||||
save: 'सहेजें',
|
||||
import: 'डेटाबेस आयात करें',
|
||||
export_sql: 'SQL निर्यात करें',
|
||||
export_as: 'के रूप में निर्यात करें',
|
||||
delete_diagram: 'आरेख हटाएँ',
|
||||
exit: 'बाहर जाएँ',
|
||||
delete_diagram: 'हटाएँ',
|
||||
},
|
||||
edit: {
|
||||
edit: 'संपादित करें',
|
||||
@@ -26,7 +34,10 @@ export const hi: LanguageTranslation = {
|
||||
hide_sidebar: 'साइडबार छिपाएँ',
|
||||
hide_cardinality: 'कार्डिनैलिटी छिपाएँ',
|
||||
show_cardinality: 'कार्डिनैलिटी दिखाएँ',
|
||||
hide_field_attributes: 'फ़ील्ड विशेषताएँ छिपाएँ',
|
||||
show_field_attributes: 'फ़ील्ड विशेषताएँ दिखाएँ',
|
||||
zoom_on_scroll: 'स्क्रॉल पर ज़ूम',
|
||||
show_views: 'डेटाबेस व्यू',
|
||||
theme: 'थीम',
|
||||
show_dependencies: 'निर्भरता दिखाएँ',
|
||||
hide_dependencies: 'निर्भरता छिपाएँ',
|
||||
@@ -63,22 +74,13 @@ export const hi: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'आरेख पुनः व्यवस्थित करें',
|
||||
title: 'आरेख स्वचालित व्यवस्थित करें',
|
||||
description:
|
||||
'यह क्रिया आरेख में सभी तालिकाओं को पुनः व्यवस्थित कर देगी। क्या आप जारी रखना चाहते हैं?',
|
||||
reorder: 'पुनः व्यवस्थित करें',
|
||||
reorder: 'स्वचालित व्यवस्थित करें',
|
||||
cancel: 'रद्द करें',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'एकाधिक स्कीमा',
|
||||
description:
|
||||
'{{schemasCount}} स्कीमा इस आरेख में हैं। वर्तमान में प्रदर्शित: {{formattedSchemas}}।',
|
||||
dont_show_again: 'फिर से न दिखाएँ',
|
||||
change_schema: 'बदलें',
|
||||
none: 'कोई नहीं',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'कॉपी असफल',
|
||||
@@ -114,14 +116,11 @@ export const hi: LanguageTranslation = {
|
||||
copied: 'Copied!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'स्कीमा:',
|
||||
filter_by_schema: 'स्कीमा द्वारा फ़िल्टर करें',
|
||||
search_schema: 'स्कीमा खोजें...',
|
||||
no_schemas_found: 'कोई स्कीमा नहीं मिला।',
|
||||
view_all_options: 'सभी विकल्प देखें...',
|
||||
tables_section: {
|
||||
tables: 'तालिकाएँ',
|
||||
add_table: 'तालिका जोड़ें',
|
||||
add_view: 'व्यू जोड़ें',
|
||||
filter: 'फ़िल्टर',
|
||||
collapse: 'सभी को संक्षिप्त करें',
|
||||
// TODO: Translate
|
||||
@@ -147,16 +146,23 @@ export const hi: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'फ़ील्ड विशेषताएँ',
|
||||
unique: 'अद्वितीय',
|
||||
auto_increment: 'ऑटो इंक्रीमेंट',
|
||||
comments: 'टिप्पणियाँ',
|
||||
no_comments: 'कोई टिप्पणी नहीं',
|
||||
delete_field: 'फ़ील्ड हटाएँ',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'Precision',
|
||||
scale: 'Scale',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'सूचकांक विशेषताएँ',
|
||||
name: 'नाम',
|
||||
unique: 'अद्वितीय',
|
||||
index_type: 'इंडेक्स प्रकार',
|
||||
delete_index: 'सूचकांक हटाएँ',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -173,12 +179,15 @@ export const hi: LanguageTranslation = {
|
||||
description: 'शुरू करने के लिए एक तालिका बनाएँ',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'संबंध',
|
||||
refs_section: {
|
||||
refs: 'रेफ्स',
|
||||
filter: 'फ़िल्टर',
|
||||
add_relationship: 'संबंध जोड़ें',
|
||||
collapse: 'सभी को संक्षिप्त करें',
|
||||
add_relationship: 'संबंध जोड़ें',
|
||||
relationships: 'संबंध',
|
||||
dependencies: 'निर्भरताएँ',
|
||||
relationship: {
|
||||
relationship: 'संबंध',
|
||||
primary: 'प्राथमिक तालिका',
|
||||
foreign: 'संदर्भित तालिका',
|
||||
cardinality: 'कार्डिनैलिटी',
|
||||
@@ -188,28 +197,19 @@ export const hi: LanguageTranslation = {
|
||||
delete_relationship: 'हटाएँ',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'कोई संबंध नहीं',
|
||||
description:
|
||||
'तालिकाओं को कनेक्ट करने के लिए एक संबंध बनाएँ',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'निर्भरताएँ',
|
||||
filter: 'फ़िल्टर',
|
||||
collapse: 'सिकोड़ें',
|
||||
dependency: {
|
||||
dependency: 'निर्भरता',
|
||||
table: 'तालिका',
|
||||
dependent_table: 'आश्रित तालिका',
|
||||
delete_dependency: 'निर्भरता हटाएँ',
|
||||
dependent_table: 'आश्रित दृश्य',
|
||||
delete_dependency: 'हटाएँ',
|
||||
dependency_actions: {
|
||||
title: 'कार्रवाइयाँ',
|
||||
delete_dependency: 'निर्भरता हटाएँ',
|
||||
title: 'क्रियाएँ',
|
||||
delete_dependency: 'हटाएँ',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'कोई निर्भरता नहीं',
|
||||
description: 'इस अनुभाग में कोई निर्भरता उपलब्ध नहीं है।',
|
||||
title: 'कोई संबंध नहीं',
|
||||
description: 'शुरू करने के लिए एक संबंध बनाएँ',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -249,12 +249,16 @@ export const hi: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'कोई enum मान परिभाषित नहीं',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -268,8 +272,14 @@ export const hi: LanguageTranslation = {
|
||||
show_all: 'सभी दिखाएँ',
|
||||
undo: 'पूर्ववत करें',
|
||||
redo: 'पुनः करें',
|
||||
reorder_diagram: 'आरेख पुनः व्यवस्थित करें',
|
||||
reorder_diagram: 'आरेख स्वचालित व्यवस्थित करें',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'ओवरलैपिंग तालिकाओं को हाइलाइट करें',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -308,7 +318,7 @@ export const hi: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'आरेख खोलें',
|
||||
title: 'डेटाबेस खोलें',
|
||||
description: 'नीचे दी गई सूची से एक आरेख चुनें।',
|
||||
table_columns: {
|
||||
name: 'नाम',
|
||||
@@ -318,6 +328,12 @@ export const hi: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'रद्द करें',
|
||||
open: 'खोलें',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'खोलें',
|
||||
duplicate: 'डुप्लिकेट',
|
||||
delete: 'हटाएं',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -404,6 +420,14 @@ export const hi: LanguageTranslation = {
|
||||
confirm: 'बदलें',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'नया स्कीमा बनाएं',
|
||||
description:
|
||||
'अभी तक कोई स्कीमा मौजूद नहीं है। अपनी तालिकाओं को व्यवस्थित करने के लिए अपना पहला स्कीमा बनाएं।',
|
||||
create: 'बनाएं',
|
||||
cancel: 'रद्द करें',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'हमें सुधारने में मदद करें!',
|
||||
description:
|
||||
@@ -459,6 +483,7 @@ export const hi: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'नई तालिका',
|
||||
new_view: 'नया व्यू',
|
||||
new_relationship: 'नया संबंध',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -481,6 +506,9 @@ export const hi: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'भाषा बदलें',
|
||||
},
|
||||
|
||||
on: 'चालू',
|
||||
off: 'बंद',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
509
src/i18n/locales/hr.ts
Normal file
509
src/i18n/locales/hr.ts
Normal file
@@ -0,0 +1,509 @@
|
||||
import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const hr: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Novi',
|
||||
browse: 'Pregledaj',
|
||||
tables: 'Tablice',
|
||||
refs: 'Refs',
|
||||
areas: 'Područja',
|
||||
dependencies: 'Ovisnosti',
|
||||
custom_types: 'Prilagođeni Tipovi',
|
||||
},
|
||||
menu: {
|
||||
actions: {
|
||||
actions: 'Akcije',
|
||||
new: 'Novi...',
|
||||
browse: 'Pregledaj...',
|
||||
save: 'Spremi',
|
||||
import: 'Uvezi',
|
||||
export_sql: 'Izvezi SQL',
|
||||
export_as: 'Izvezi kao',
|
||||
delete_diagram: 'Izbriši',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Uredi',
|
||||
undo: 'Poništi',
|
||||
redo: 'Ponovi',
|
||||
clear: 'Očisti',
|
||||
},
|
||||
view: {
|
||||
view: 'Prikaz',
|
||||
show_sidebar: 'Prikaži bočnu traku',
|
||||
hide_sidebar: 'Sakrij bočnu traku',
|
||||
hide_cardinality: 'Sakrij kardinalnost',
|
||||
show_cardinality: 'Prikaži kardinalnost',
|
||||
hide_field_attributes: 'Sakrij atribute polja',
|
||||
show_field_attributes: 'Prikaži atribute polja',
|
||||
zoom_on_scroll: 'Zumiranje pri skrolanju',
|
||||
show_views: 'Pogledi Baze Podataka',
|
||||
theme: 'Tema',
|
||||
show_dependencies: 'Prikaži ovisnosti',
|
||||
hide_dependencies: 'Sakrij ovisnosti',
|
||||
show_minimap: 'Prikaži mini kartu',
|
||||
hide_minimap: 'Sakrij mini kartu',
|
||||
},
|
||||
backup: {
|
||||
backup: 'Sigurnosna kopija',
|
||||
export_diagram: 'Izvezi dijagram',
|
||||
restore_diagram: 'Vrati dijagram',
|
||||
},
|
||||
help: {
|
||||
help: 'Pomoć',
|
||||
docs_website: 'Dokumentacija',
|
||||
join_discord: 'Pridružite nam se na Discordu',
|
||||
},
|
||||
},
|
||||
|
||||
delete_diagram_alert: {
|
||||
title: 'Izbriši dijagram',
|
||||
description:
|
||||
'Ova radnja se ne može poništiti. Ovo će trajno izbrisati dijagram.',
|
||||
cancel: 'Odustani',
|
||||
delete: 'Izbriši',
|
||||
},
|
||||
|
||||
clear_diagram_alert: {
|
||||
title: 'Očisti dijagram',
|
||||
description:
|
||||
'Ova radnja se ne može poništiti. Ovo će trajno izbrisati sve podatke u dijagramu.',
|
||||
cancel: 'Odustani',
|
||||
clear: 'Očisti',
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Automatski preuredi dijagram',
|
||||
description:
|
||||
'Ova radnja će preurediti sve tablice u dijagramu. Želite li nastaviti?',
|
||||
reorder: 'Automatski preuredi',
|
||||
cancel: 'Odustani',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'Kopiranje neuspješno',
|
||||
description: 'Međuspremnik nije podržan.',
|
||||
},
|
||||
failed: {
|
||||
title: 'Kopiranje neuspješno',
|
||||
description: 'Nešto je pošlo po zlu. Molimo pokušajte ponovno.',
|
||||
},
|
||||
},
|
||||
|
||||
theme: {
|
||||
system: 'Sustav',
|
||||
light: 'Svijetla',
|
||||
dark: 'Tamna',
|
||||
},
|
||||
|
||||
zoom: {
|
||||
on: 'Uključeno',
|
||||
off: 'Isključeno',
|
||||
},
|
||||
|
||||
last_saved: 'Zadnje spremljeno',
|
||||
saved: 'Spremljeno',
|
||||
loading_diagram: 'Učitavanje dijagrama...',
|
||||
deselect_all: 'Odznači sve',
|
||||
select_all: 'Označi sve',
|
||||
clear: 'Očisti',
|
||||
show_more: 'Prikaži više',
|
||||
show_less: 'Prikaži manje',
|
||||
copy_to_clipboard: 'Kopiraj u međuspremnik',
|
||||
copied: 'Kopirano!',
|
||||
|
||||
side_panel: {
|
||||
view_all_options: 'Prikaži sve opcije...',
|
||||
tables_section: {
|
||||
tables: 'Tablice',
|
||||
add_table: 'Dodaj tablicu',
|
||||
add_view: 'Dodaj Pogled',
|
||||
filter: 'Filtriraj',
|
||||
collapse: 'Sažmi sve',
|
||||
clear: 'Očisti filter',
|
||||
no_results:
|
||||
'Nema pronađenih tablica koje odgovaraju vašem filteru.',
|
||||
show_list: 'Prikaži popis tablica',
|
||||
show_dbml: 'Prikaži DBML uređivač',
|
||||
|
||||
table: {
|
||||
fields: 'Polja',
|
||||
nullable: 'Može biti null?',
|
||||
primary_key: 'Primarni ključ',
|
||||
indexes: 'Indeksi',
|
||||
comments: 'Komentari',
|
||||
no_comments: 'Nema komentara',
|
||||
add_field: 'Dodaj polje',
|
||||
add_index: 'Dodaj indeks',
|
||||
index_select_fields: 'Odaberi polja',
|
||||
no_types_found: 'Nema pronađenih tipova',
|
||||
field_name: 'Naziv',
|
||||
field_type: 'Tip',
|
||||
field_actions: {
|
||||
title: 'Atributi polja',
|
||||
unique: 'Jedinstven',
|
||||
auto_increment: 'Automatsko povećavanje',
|
||||
character_length: 'Maksimalna dužina',
|
||||
precision: 'Preciznost',
|
||||
scale: 'Skala',
|
||||
comments: 'Komentari',
|
||||
no_comments: 'Nema komentara',
|
||||
default_value: 'Zadana vrijednost',
|
||||
no_default: 'Nema zadane vrijednosti',
|
||||
delete_field: 'Izbriši polje',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Atributi indeksa',
|
||||
name: 'Naziv',
|
||||
unique: 'Jedinstven',
|
||||
index_type: 'Vrsta indeksa',
|
||||
delete_index: 'Izbriši indeks',
|
||||
},
|
||||
table_actions: {
|
||||
title: 'Radnje nad tablicom',
|
||||
change_schema: 'Promijeni shemu',
|
||||
add_field: 'Dodaj polje',
|
||||
add_index: 'Dodaj indeks',
|
||||
duplicate_table: 'Dupliciraj tablicu',
|
||||
delete_table: 'Izbriši tablicu',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Nema tablica',
|
||||
description: 'Stvorite tablicu za početak',
|
||||
},
|
||||
},
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Filtriraj',
|
||||
collapse: 'Sažmi sve',
|
||||
add_relationship: 'Dodaj vezu',
|
||||
relationships: 'Veze',
|
||||
dependencies: 'Ovisnosti',
|
||||
relationship: {
|
||||
relationship: 'Veza',
|
||||
primary: 'Primarna tablica',
|
||||
foreign: 'Referentna tablica',
|
||||
cardinality: 'Kardinalnost',
|
||||
delete_relationship: 'Izbriši',
|
||||
relationship_actions: {
|
||||
title: 'Radnje',
|
||||
delete_relationship: 'Izbriši',
|
||||
},
|
||||
},
|
||||
dependency: {
|
||||
dependency: 'Ovisnost',
|
||||
table: 'Tablica',
|
||||
dependent_table: 'Ovisni pogled',
|
||||
delete_dependency: 'Izbriši',
|
||||
dependency_actions: {
|
||||
title: 'Radnje',
|
||||
delete_dependency: 'Izbriši',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Nema veze',
|
||||
description: 'Stvorite vezu za početak',
|
||||
},
|
||||
},
|
||||
|
||||
areas_section: {
|
||||
areas: 'Područja',
|
||||
add_area: 'Dodaj područje',
|
||||
filter: 'Filtriraj',
|
||||
clear: 'Očisti filter',
|
||||
no_results:
|
||||
'Nema pronađenih područja koja odgovaraju vašem filteru.',
|
||||
|
||||
area: {
|
||||
area_actions: {
|
||||
title: 'Radnje nad područjem',
|
||||
edit_name: 'Uredi naziv',
|
||||
delete_area: 'Izbriši područje',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Nema područja',
|
||||
description: 'Stvorite područje za početak',
|
||||
},
|
||||
},
|
||||
|
||||
custom_types_section: {
|
||||
custom_types: 'Prilagođeni tipovi',
|
||||
filter: 'Filtriraj',
|
||||
clear: 'Očisti filter',
|
||||
no_results:
|
||||
'Nema pronađenih prilagođenih tipova koji odgovaraju vašem filteru.',
|
||||
empty_state: {
|
||||
title: 'Nema prilagođenih tipova',
|
||||
description:
|
||||
'Prilagođeni tipovi će se pojaviti ovdje kada budu dostupni u vašoj bazi podataka',
|
||||
},
|
||||
custom_type: {
|
||||
kind: 'Vrsta',
|
||||
enum_values: 'Enum vrijednosti',
|
||||
composite_fields: 'Polja',
|
||||
no_fields: 'Nema definiranih polja',
|
||||
no_values: 'Nema definiranih enum vrijednosti',
|
||||
field_name_placeholder: 'Naziv polja',
|
||||
field_type_placeholder: 'Odaberi tip',
|
||||
add_field: 'Dodaj polje',
|
||||
no_fields_tooltip:
|
||||
'Nema definiranih polja za ovaj prilagođeni tip',
|
||||
custom_type_actions: {
|
||||
title: 'Radnje',
|
||||
highlight_fields: 'Istakni polja',
|
||||
clear_field_highlight: 'Ukloni isticanje',
|
||||
delete_custom_type: 'Izbriši',
|
||||
},
|
||||
delete_custom_type: 'Izbriši tip',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
toolbar: {
|
||||
zoom_in: 'Uvećaj',
|
||||
zoom_out: 'Smanji',
|
||||
save: 'Spremi',
|
||||
show_all: 'Prikaži sve',
|
||||
undo: 'Poništi',
|
||||
redo: 'Ponovi',
|
||||
reorder_diagram: 'Automatski preuredi dijagram',
|
||||
highlight_overlapping_tables: 'Istakni preklapajuće tablice',
|
||||
clear_custom_type_highlight: 'Ukloni isticanje za "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Isticanje "{{typeName}}" - Kliknite za uklanjanje',
|
||||
filter: 'Filtriraj tablice',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
database_selection: {
|
||||
title: 'Koja je vaša baza podataka?',
|
||||
description:
|
||||
'Svaka baza podataka ima svoje jedinstvene značajke i mogućnosti.',
|
||||
check_examples_long: 'Pogledaj primjere',
|
||||
check_examples_short: 'Primjeri',
|
||||
},
|
||||
|
||||
import_database: {
|
||||
title: 'Uvezite svoju bazu podataka',
|
||||
database_edition: 'Verzija baze podataka:',
|
||||
step_1: 'Pokrenite ovu skriptu u svojoj bazi podataka:',
|
||||
step_2: 'Zalijepite rezultat skripte u ovaj dio →',
|
||||
script_results_placeholder: 'Rezultati skripte ovdje...',
|
||||
ssms_instructions: {
|
||||
button_text: 'SSMS upute',
|
||||
title: 'Upute',
|
||||
step_1: 'Idite na Tools > Options > Query Results > SQL Server.',
|
||||
step_2: 'Ako koristite "Results to Grid," promijenite Maximum Characters Retrieved za Non-XML podatke (postavite na 9999999).',
|
||||
},
|
||||
instructions_link: 'Trebate pomoć? Pogledajte kako',
|
||||
check_script_result: 'Provjeri rezultat skripte',
|
||||
},
|
||||
|
||||
cancel: 'Odustani',
|
||||
import_from_file: 'Uvezi iz datoteke',
|
||||
back: 'Natrag',
|
||||
empty_diagram: 'Prazan dijagram',
|
||||
continue: 'Nastavi',
|
||||
import: 'Uvezi',
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Otvori bazu podataka',
|
||||
description: 'Odaberite dijagram za otvaranje iz popisa ispod.',
|
||||
table_columns: {
|
||||
name: 'Naziv',
|
||||
created_at: 'Stvoreno',
|
||||
last_modified: 'Zadnje izmijenjeno',
|
||||
tables_count: 'Tablice',
|
||||
},
|
||||
cancel: 'Odustani',
|
||||
open: 'Otvori',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Otvori',
|
||||
duplicate: 'Dupliciraj',
|
||||
delete: 'Obriši',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
title: 'Izvezi SQL',
|
||||
description:
|
||||
'Izvezite shemu vašeg dijagrama u {{databaseType}} skriptu',
|
||||
close: 'Zatvori',
|
||||
loading: {
|
||||
text: 'AI generira SQL za {{databaseType}}...',
|
||||
description: 'Ovo bi trebalo potrajati do 30 sekundi.',
|
||||
},
|
||||
error: {
|
||||
message:
|
||||
'Greška pri generiranju SQL skripte. Molimo pokušajte ponovno kasnije ili <0>kontaktirajte nas</0>.',
|
||||
description:
|
||||
'Slobodno koristite svoj OPENAI_TOKEN, pogledajte priručnik <0>ovdje</0>.',
|
||||
},
|
||||
},
|
||||
|
||||
create_relationship_dialog: {
|
||||
title: 'Kreiraj vezu',
|
||||
primary_table: 'Primarna tablica',
|
||||
primary_field: 'Primarno polje',
|
||||
referenced_table: 'Referentna tablica',
|
||||
referenced_field: 'Referentno polje',
|
||||
primary_table_placeholder: 'Odaberi tablicu',
|
||||
primary_field_placeholder: 'Odaberi polje',
|
||||
referenced_table_placeholder: 'Odaberi tablicu',
|
||||
referenced_field_placeholder: 'Odaberi polje',
|
||||
no_tables_found: 'Nema pronađenih tablica',
|
||||
no_fields_found: 'Nema pronađenih polja',
|
||||
create: 'Kreiraj',
|
||||
cancel: 'Odustani',
|
||||
},
|
||||
|
||||
import_database_dialog: {
|
||||
title: 'Uvezi u trenutni dijagram',
|
||||
override_alert: {
|
||||
title: 'Uvezi bazu podataka',
|
||||
content: {
|
||||
alert: 'Uvoz ovog dijagrama će utjecati na postojeće tablice i veze.',
|
||||
new_tables:
|
||||
'<bold>{{newTablesNumber}}</bold> novih tablica će biti dodano.',
|
||||
new_relationships:
|
||||
'<bold>{{newRelationshipsNumber}}</bold> novih veza će biti stvoreno.',
|
||||
tables_override:
|
||||
'<bold>{{tablesOverrideNumber}}</bold> tablica će biti prepisano.',
|
||||
proceed: 'Želite li nastaviti?',
|
||||
},
|
||||
import: 'Uvezi',
|
||||
cancel: 'Odustani',
|
||||
},
|
||||
},
|
||||
|
||||
export_image_dialog: {
|
||||
title: 'Izvezi sliku',
|
||||
description: 'Odaberite faktor veličine za izvoz:',
|
||||
scale_1x: '1x Obično',
|
||||
scale_2x: '2x (Preporučeno)',
|
||||
scale_3x: '3x',
|
||||
scale_4x: '4x',
|
||||
cancel: 'Odustani',
|
||||
export: 'Izvezi',
|
||||
advanced_options: 'Napredne opcije',
|
||||
pattern: 'Uključi pozadinski uzorak',
|
||||
pattern_description: 'Dodaj suptilni mrežni uzorak u pozadinu.',
|
||||
transparent: 'Prozirna pozadina',
|
||||
transparent_description: 'Ukloni boju pozadine iz slike.',
|
||||
},
|
||||
|
||||
new_table_schema_dialog: {
|
||||
title: 'Odaberi shemu',
|
||||
description:
|
||||
'Trenutno je prikazano više shema. Odaberite jednu za novu tablicu.',
|
||||
cancel: 'Odustani',
|
||||
confirm: 'Potvrdi',
|
||||
},
|
||||
|
||||
update_table_schema_dialog: {
|
||||
title: 'Promijeni shemu',
|
||||
description: 'Ažuriraj shemu tablice "{{tableName}}"',
|
||||
cancel: 'Odustani',
|
||||
confirm: 'Promijeni',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'Stvori novu shemu',
|
||||
description:
|
||||
'Još ne postoje sheme. Stvorite svoju prvu shemu za organiziranje tablica.',
|
||||
create: 'Stvori',
|
||||
cancel: 'Odustani',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'Pomozite nam da se poboljšamo!',
|
||||
description:
|
||||
'Želite li nam dati zvjezdicu na GitHubu? Samo je jedan klik!',
|
||||
close: 'Ne sada',
|
||||
confirm: 'Naravno!',
|
||||
},
|
||||
export_diagram_dialog: {
|
||||
title: 'Izvezi dijagram',
|
||||
description: 'Odaberite format za izvoz:',
|
||||
format_json: 'JSON',
|
||||
cancel: 'Odustani',
|
||||
export: 'Izvezi',
|
||||
error: {
|
||||
title: 'Greška pri izvozu dijagrama',
|
||||
description:
|
||||
'Nešto je pošlo po zlu. Trebate pomoć? support@chartdb.io',
|
||||
},
|
||||
},
|
||||
|
||||
import_diagram_dialog: {
|
||||
title: 'Uvezi dijagram',
|
||||
description: 'Uvezite dijagram iz JSON datoteke.',
|
||||
cancel: 'Odustani',
|
||||
import: 'Uvezi',
|
||||
error: {
|
||||
title: 'Greška pri uvozu dijagrama',
|
||||
description:
|
||||
'JSON dijagrama je nevažeći. Molimo provjerite JSON i pokušajte ponovno. Trebate pomoć? support@chartdb.io',
|
||||
},
|
||||
},
|
||||
|
||||
import_dbml_dialog: {
|
||||
example_title: 'Uvezi primjer DBML-a',
|
||||
title: 'Uvezi DBML',
|
||||
description: 'Uvezite shemu baze podataka iz DBML formata.',
|
||||
import: 'Uvezi',
|
||||
cancel: 'Odustani',
|
||||
skip_and_empty: 'Preskoči i isprazni',
|
||||
show_example: 'Prikaži primjer',
|
||||
error: {
|
||||
title: 'Greška pri uvozu DBML-a',
|
||||
description:
|
||||
'Neuspješno parsiranje DBML-a. Molimo provjerite sintaksu.',
|
||||
},
|
||||
},
|
||||
relationship_type: {
|
||||
one_to_one: 'Jedan na jedan',
|
||||
one_to_many: 'Jedan na više',
|
||||
many_to_one: 'Više na jedan',
|
||||
many_to_many: 'Više na više',
|
||||
},
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'Nova tablica',
|
||||
new_view: 'Novi Pogled',
|
||||
new_relationship: 'Nova veza',
|
||||
new_area: 'Novo područje',
|
||||
},
|
||||
|
||||
table_node_context_menu: {
|
||||
edit_table: 'Uredi tablicu',
|
||||
duplicate_table: 'Dupliciraj tablicu',
|
||||
delete_table: 'Izbriši tablicu',
|
||||
add_relationship: 'Dodaj vezu',
|
||||
},
|
||||
|
||||
snap_to_grid_tooltip: 'Priljepljivanje na mrežu (Drži {{key}})',
|
||||
|
||||
tool_tips: {
|
||||
double_click_to_edit: 'Dvostruki klik za uređivanje',
|
||||
},
|
||||
|
||||
language_select: {
|
||||
change_language: 'Jezik',
|
||||
},
|
||||
|
||||
on: 'Uključeno',
|
||||
off: 'Isključeno',
|
||||
},
|
||||
};
|
||||
|
||||
export const hrMetadata: LanguageMetadata = {
|
||||
name: 'Croatian',
|
||||
nativeName: 'Hrvatski',
|
||||
code: 'hr',
|
||||
};
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const id_ID: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Baru',
|
||||
browse: 'Jelajahi',
|
||||
tables: 'Tabel',
|
||||
refs: 'Refs',
|
||||
areas: 'Area',
|
||||
dependencies: 'Ketergantungan',
|
||||
custom_types: 'Tipe Kustom',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'Berkas',
|
||||
new: 'Buat Baru',
|
||||
open: 'Buka',
|
||||
actions: {
|
||||
actions: 'Aksi',
|
||||
new: 'Baru...',
|
||||
browse: 'Jelajahi...',
|
||||
save: 'Simpan',
|
||||
import: 'Impor Database',
|
||||
export_sql: 'Ekspor SQL',
|
||||
export_as: 'Ekspor Sebagai',
|
||||
delete_diagram: 'Hapus Diagram',
|
||||
exit: 'Keluar',
|
||||
delete_diagram: 'Hapus',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Ubah',
|
||||
@@ -26,7 +34,10 @@ export const id_ID: LanguageTranslation = {
|
||||
hide_sidebar: 'Sembunyikan Sidebar',
|
||||
hide_cardinality: 'Sembunyikan Kardinalitas',
|
||||
show_cardinality: 'Tampilkan Kardinalitas',
|
||||
hide_field_attributes: 'Sembunyikan Atribut Kolom',
|
||||
show_field_attributes: 'Tampilkan Atribut Kolom',
|
||||
zoom_on_scroll: 'Perbesar saat Scroll',
|
||||
show_views: 'Tampilan Database',
|
||||
theme: 'Tema',
|
||||
show_dependencies: 'Tampilkan Dependensi',
|
||||
hide_dependencies: 'Sembunyikan Dependensi',
|
||||
@@ -63,22 +74,13 @@ export const id_ID: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Atur Ulang Diagram',
|
||||
title: 'Atur Otomatis Diagram',
|
||||
description:
|
||||
'Tindakan ini akan mengatur ulang semua tabel di diagram. Apakah Anda ingin melanjutkan?',
|
||||
reorder: 'Atur Ulang',
|
||||
reorder: 'Atur Otomatis',
|
||||
cancel: 'Batal',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Schema Lebih dari satu',
|
||||
description:
|
||||
'{{schemasCount}} schema di diagram ini. Sedang ditampilkan: {{formattedSchemas}}.',
|
||||
dont_show_again: 'Jangan tampilkan lagi',
|
||||
change_schema: 'Ubah',
|
||||
none: 'Tidak ada',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'Gagal menyalin',
|
||||
@@ -113,14 +115,11 @@ export const id_ID: LanguageTranslation = {
|
||||
copied: 'Tersalin!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Skema:',
|
||||
filter_by_schema: 'Saring berdasarkan skema',
|
||||
search_schema: 'Cari skema...',
|
||||
no_schemas_found: 'Tidak ada skema yang ditemukan.',
|
||||
view_all_options: 'Tampilkan Semua Pilihan...',
|
||||
tables_section: {
|
||||
tables: 'Tabel',
|
||||
add_table: 'Tambah Tabel',
|
||||
add_view: 'Tambah Tampilan',
|
||||
filter: 'Saring',
|
||||
collapse: 'Lipat Semua',
|
||||
// TODO: Translate
|
||||
@@ -146,16 +145,23 @@ export const id_ID: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'Atribut Kolom',
|
||||
unique: 'Unik',
|
||||
auto_increment: 'Kenaikan Otomatis',
|
||||
comments: 'Komentar',
|
||||
no_comments: 'Tidak ada komentar',
|
||||
delete_field: 'Hapus Kolom',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'Presisi',
|
||||
scale: 'Skala',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Atribut Indeks',
|
||||
name: 'Nama',
|
||||
unique: 'Unik',
|
||||
index_type: 'Tipe Indeks',
|
||||
delete_index: 'Hapus Indeks',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -172,12 +178,15 @@ export const id_ID: LanguageTranslation = {
|
||||
description: 'Buat tabel untuk memulai',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'Hubungan',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Saring',
|
||||
add_relationship: 'Tambah Hubungan',
|
||||
collapse: 'Lipat Semua',
|
||||
add_relationship: 'Tambah Hubungan',
|
||||
relationships: 'Hubungan',
|
||||
dependencies: 'Dependensi',
|
||||
relationship: {
|
||||
relationship: 'Hubungan',
|
||||
primary: 'Tabel Primer',
|
||||
foreign: 'Tabel Referensi',
|
||||
cardinality: 'Kardinalitas',
|
||||
@@ -187,16 +196,8 @@ export const id_ID: LanguageTranslation = {
|
||||
delete_relationship: 'Hapus',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Tidak ada hubungan',
|
||||
description: 'Buat hubungan untuk menghubungkan tabel',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Dependensi',
|
||||
filter: 'Saring',
|
||||
collapse: 'Lipat Semua',
|
||||
dependency: {
|
||||
dependency: 'Dependensi',
|
||||
table: 'Tabel',
|
||||
dependent_table: 'Tampilan Dependen',
|
||||
delete_dependency: 'Hapus',
|
||||
@@ -206,8 +207,8 @@ export const id_ID: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Tidak ada dependensi',
|
||||
description: 'Buat tampilan untuk memulai',
|
||||
title: 'Tidak ada hubungan',
|
||||
description: 'Buat hubungan untuk memulai',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -247,12 +248,16 @@ export const id_ID: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'Tidak ada nilai enum yang ditentukan',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -266,8 +271,14 @@ export const id_ID: LanguageTranslation = {
|
||||
show_all: 'Tampilkan Semua',
|
||||
undo: 'Undo',
|
||||
redo: 'Redo',
|
||||
reorder_diagram: 'Atur Ulang Diagram',
|
||||
reorder_diagram: 'Atur Otomatis Diagram',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'Sorot Tabel yang Tumpang Tindih',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -304,7 +315,7 @@ export const id_ID: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Buka Diagram',
|
||||
title: 'Buka Database',
|
||||
description: 'Pilih diagram untuk dibuka dari daftar di bawah.',
|
||||
table_columns: {
|
||||
name: 'Name',
|
||||
@@ -314,6 +325,12 @@ export const id_ID: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'Batal',
|
||||
open: 'Buka',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Buka',
|
||||
duplicate: 'Duplikat',
|
||||
delete: 'Hapus',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -399,6 +416,14 @@ export const id_ID: LanguageTranslation = {
|
||||
confirm: 'Ubah',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'Buat Skema Baru',
|
||||
description:
|
||||
'Belum ada skema yang tersedia. Buat skema pertama Anda untuk mengatur tabel-tabel Anda.',
|
||||
create: 'Buat',
|
||||
cancel: 'Batal',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'Bantu kami meningkatkan!',
|
||||
description:
|
||||
@@ -455,6 +480,7 @@ export const id_ID: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'Tabel Baru',
|
||||
new_view: 'Tampilan Baru',
|
||||
new_relationship: 'Hubungan Baru',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -476,6 +502,9 @@ export const id_ID: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'Bahasa',
|
||||
},
|
||||
|
||||
on: 'Aktif',
|
||||
off: 'Nonaktif',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const ja: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: '新規',
|
||||
browse: '参照',
|
||||
tables: 'テーブル',
|
||||
refs: '参照',
|
||||
areas: 'エリア',
|
||||
dependencies: '依存関係',
|
||||
custom_types: 'カスタムタイプ',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'ファイル',
|
||||
new: '新規',
|
||||
open: '開く',
|
||||
actions: {
|
||||
actions: 'アクション',
|
||||
new: '新規...',
|
||||
browse: '参照...',
|
||||
save: '保存',
|
||||
import: 'データベースをインポート',
|
||||
export_sql: 'SQLをエクスポート',
|
||||
export_as: '形式を指定してエクスポート',
|
||||
delete_diagram: 'ダイアグラムを削除',
|
||||
exit: '終了',
|
||||
delete_diagram: '削除',
|
||||
},
|
||||
edit: {
|
||||
edit: '編集',
|
||||
@@ -26,7 +34,10 @@ export const ja: LanguageTranslation = {
|
||||
hide_sidebar: 'サイドバーを非表示',
|
||||
hide_cardinality: 'カーディナリティを非表示',
|
||||
show_cardinality: 'カーディナリティを表示',
|
||||
hide_field_attributes: 'フィールド属性を非表示',
|
||||
show_field_attributes: 'フィールド属性を表示',
|
||||
zoom_on_scroll: 'スクロールでズーム',
|
||||
show_views: 'データベースビュー',
|
||||
theme: 'テーマ',
|
||||
// TODO: Translate
|
||||
show_dependencies: 'Show Dependencies',
|
||||
@@ -65,22 +76,13 @@ export const ja: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'ダイアグラムを並べ替え',
|
||||
title: 'ダイアグラムを自動配置',
|
||||
description:
|
||||
'この操作によりダイアグラム内のすべてのテーブルが再配置されます。続行しますか?',
|
||||
reorder: '並べ替え',
|
||||
reorder: '自動配置',
|
||||
cancel: 'キャンセル',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: '複数のスキーマ',
|
||||
description:
|
||||
'このダイアグラムには{{schemasCount}}個のスキーマがあります。現在表示中: {{formattedSchemas}}。',
|
||||
dont_show_again: '再表示しない',
|
||||
change_schema: '変更',
|
||||
none: 'なし',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'コピー失敗',
|
||||
@@ -117,14 +119,11 @@ export const ja: LanguageTranslation = {
|
||||
copied: 'Copied!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'スキーマ:',
|
||||
filter_by_schema: 'スキーマでフィルタ',
|
||||
search_schema: 'スキーマを検索...',
|
||||
no_schemas_found: 'スキーマが見つかりません。',
|
||||
view_all_options: 'すべてのオプションを表示...',
|
||||
tables_section: {
|
||||
tables: 'テーブル',
|
||||
add_table: 'テーブルを追加',
|
||||
add_view: 'ビューを追加',
|
||||
filter: 'フィルタ',
|
||||
collapse: 'すべて折りたたむ',
|
||||
// TODO: Translate
|
||||
@@ -150,16 +149,23 @@ export const ja: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'フィールド属性',
|
||||
unique: 'ユニーク',
|
||||
auto_increment: 'オートインクリメント',
|
||||
comments: 'コメント',
|
||||
no_comments: 'コメントがありません',
|
||||
delete_field: 'フィールドを削除',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: '精度',
|
||||
scale: '小数点以下桁数',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'インデックス属性',
|
||||
name: '名前',
|
||||
unique: 'ユニーク',
|
||||
index_type: 'インデックスタイプ',
|
||||
delete_index: 'インデックスを削除',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -176,12 +182,15 @@ export const ja: LanguageTranslation = {
|
||||
description: 'テーブルを作成して開始してください',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'リレーションシップ',
|
||||
refs_section: {
|
||||
refs: '参照',
|
||||
filter: 'フィルタ',
|
||||
add_relationship: 'リレーションシップを追加',
|
||||
collapse: 'すべて折りたたむ',
|
||||
add_relationship: 'リレーションシップを追加',
|
||||
relationships: 'リレーションシップ',
|
||||
dependencies: '依存関係',
|
||||
relationship: {
|
||||
relationship: 'リレーションシップ',
|
||||
primary: '主テーブル',
|
||||
foreign: '参照テーブル',
|
||||
cardinality: 'カーディナリティ',
|
||||
@@ -191,29 +200,20 @@ export const ja: LanguageTranslation = {
|
||||
delete_relationship: '削除',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'リレーションシップがありません',
|
||||
description:
|
||||
'テーブルを接続するためにリレーションシップを作成してください',
|
||||
},
|
||||
},
|
||||
// TODO: Translate
|
||||
dependencies_section: {
|
||||
dependencies: 'Dependencies',
|
||||
filter: 'Filter',
|
||||
collapse: 'Collapse All',
|
||||
dependency: {
|
||||
table: 'Table',
|
||||
dependent_table: 'Dependent View',
|
||||
delete_dependency: 'Delete',
|
||||
dependency: '依存関係',
|
||||
table: 'テーブル',
|
||||
dependent_table: '依存ビュー',
|
||||
delete_dependency: '削除',
|
||||
dependency_actions: {
|
||||
title: 'Actions',
|
||||
delete_dependency: 'Delete',
|
||||
title: '操作',
|
||||
delete_dependency: '削除',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'No dependencies',
|
||||
description: 'Create a view to get started',
|
||||
title: 'リレーションシップがありません',
|
||||
description:
|
||||
'開始するためにリレーションシップを作成してください',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -253,12 +253,16 @@ export const ja: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: '列挙値が定義されていません',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -272,9 +276,13 @@ export const ja: LanguageTranslation = {
|
||||
show_all: 'すべて表示',
|
||||
undo: '元に戻す',
|
||||
redo: 'やり直し',
|
||||
reorder_diagram: 'ダイアグラムを並べ替え',
|
||||
reorder_diagram: 'ダイアグラムを自動配置',
|
||||
// TODO: Translate
|
||||
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear', // TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -312,7 +320,7 @@ export const ja: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'ダイアグラムを開く',
|
||||
title: 'データベースを開く',
|
||||
description: '以下のリストからダイアグラムを選択してください。',
|
||||
table_columns: {
|
||||
name: '名前',
|
||||
@@ -322,6 +330,12 @@ export const ja: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'キャンセル',
|
||||
open: '開く',
|
||||
|
||||
diagram_actions: {
|
||||
open: '開く',
|
||||
duplicate: '複製',
|
||||
delete: '削除',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -408,6 +422,14 @@ export const ja: LanguageTranslation = {
|
||||
confirm: '変更',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: '新しいスキーマを作成',
|
||||
description:
|
||||
'スキーマがまだ存在しません。テーブルを整理するために最初のスキーマを作成してください。',
|
||||
create: '作成',
|
||||
cancel: 'キャンセル',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: '改善をサポートしてください!',
|
||||
description:
|
||||
@@ -463,6 +485,7 @@ export const ja: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: '新しいテーブル',
|
||||
new_view: '新しいビュー',
|
||||
new_relationship: '新しいリレーションシップ',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -485,6 +508,9 @@ export const ja: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: '言語',
|
||||
},
|
||||
|
||||
on: 'オン',
|
||||
off: 'オフ',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const ko_KR: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: '새로 만들기',
|
||||
browse: '찾아보기',
|
||||
tables: '테이블',
|
||||
refs: 'Refs',
|
||||
areas: '영역',
|
||||
dependencies: '종속성',
|
||||
custom_types: '사용자 지정 타입',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: '파일',
|
||||
new: '새 다이어그램',
|
||||
open: '열기',
|
||||
actions: {
|
||||
actions: '작업',
|
||||
new: '새로 만들기...',
|
||||
browse: '찾아보기...',
|
||||
save: '저장',
|
||||
import: '데이터베이스 가져오기',
|
||||
export_sql: 'SQL로 저장',
|
||||
export_as: '다른 형식으로 저장',
|
||||
delete_diagram: '다이어그램 삭제',
|
||||
exit: '종료',
|
||||
delete_diagram: '삭제',
|
||||
},
|
||||
edit: {
|
||||
edit: '편집',
|
||||
@@ -26,7 +34,10 @@ export const ko_KR: LanguageTranslation = {
|
||||
hide_sidebar: '사이드바 숨기기',
|
||||
hide_cardinality: '카디널리티 숨기기',
|
||||
show_cardinality: '카디널리티 보이기',
|
||||
hide_field_attributes: '필드 속성 숨기기',
|
||||
show_field_attributes: '필드 속성 보이기',
|
||||
zoom_on_scroll: '스크롤 시 확대',
|
||||
show_views: '데이터베이스 뷰',
|
||||
theme: '테마',
|
||||
show_dependencies: '종속성 보이기',
|
||||
hide_dependencies: '종속성 숨기기',
|
||||
@@ -63,22 +74,13 @@ export const ko_KR: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: '다이어그램 재정렬',
|
||||
title: '다이어그램 자동 정렬',
|
||||
description:
|
||||
'이 작업은 모든 다이어그램이 재정렬됩니다. 계속하시겠습니까?',
|
||||
reorder: '재정렬',
|
||||
reorder: '자동 정렬',
|
||||
cancel: '취소',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: '다중 스키마',
|
||||
description:
|
||||
'현재 다이어그램에 {{schemasCount}}개의 스키마가 있습니다. Currently displaying: {{formattedSchemas}}.',
|
||||
dont_show_again: '다시 보여주지 마세요',
|
||||
change_schema: '변경',
|
||||
none: '없음',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: '복사 실패',
|
||||
@@ -113,14 +115,11 @@ export const ko_KR: LanguageTranslation = {
|
||||
copied: '복사됨!',
|
||||
|
||||
side_panel: {
|
||||
schema: '스키마:',
|
||||
filter_by_schema: '스키마로 필터링',
|
||||
search_schema: '스키마 검색...',
|
||||
no_schemas_found: '스키마를 찾을 수 없습니다.',
|
||||
view_all_options: '전체 옵션 보기...',
|
||||
tables_section: {
|
||||
tables: '테이블',
|
||||
add_table: '테이블 추가',
|
||||
add_view: '뷰 추가',
|
||||
filter: '필터',
|
||||
collapse: '모두 접기',
|
||||
// TODO: Translate
|
||||
@@ -146,16 +145,23 @@ export const ko_KR: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: '필드 속성',
|
||||
unique: '유니크 여부',
|
||||
auto_increment: '자동 증가',
|
||||
comments: '주석',
|
||||
no_comments: '주석 없음',
|
||||
delete_field: '필드 삭제',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: '정밀도',
|
||||
scale: '소수점 자릿수',
|
||||
},
|
||||
index_actions: {
|
||||
title: '인덱스 속성',
|
||||
name: '인덱스 명',
|
||||
unique: '유니크 여부',
|
||||
index_type: '인덱스 타입',
|
||||
delete_index: '인덱스 삭제',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -172,12 +178,15 @@ export const ko_KR: LanguageTranslation = {
|
||||
description: '테이블을 만들어 시작하세요.',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: '연관 관계',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: '필터',
|
||||
add_relationship: '연관 관계 추가',
|
||||
collapse: '모두 접기',
|
||||
add_relationship: '연관 관계 추가',
|
||||
relationships: '연관 관계',
|
||||
dependencies: '종속성',
|
||||
relationship: {
|
||||
relationship: '연관 관계',
|
||||
primary: '주 테이블',
|
||||
foreign: '참조 테이블',
|
||||
cardinality: '카디널리티',
|
||||
@@ -187,16 +196,8 @@ export const ko_KR: LanguageTranslation = {
|
||||
delete_relationship: '연관 관계 삭제',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: '연관 관계',
|
||||
description: '테이블 연결을 위해 연관 관계를 생성하세요',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: '종속성',
|
||||
filter: '필터',
|
||||
collapse: '모두 접기',
|
||||
dependency: {
|
||||
dependency: '종속성',
|
||||
table: '테이블',
|
||||
dependent_table: '뷰 테이블',
|
||||
delete_dependency: '삭제',
|
||||
@@ -206,8 +207,8 @@ export const ko_KR: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: '뷰 테이블 없음',
|
||||
description: '뷰 테이블을 만들어 시작하세요.',
|
||||
title: '연관 관계 없음',
|
||||
description: '연관 관계를 만들어 시작하세요.',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -247,12 +248,16 @@ export const ko_KR: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: '정의된 열거형 값이 없습니다',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -266,8 +271,14 @@ export const ko_KR: LanguageTranslation = {
|
||||
show_all: '전체 저장',
|
||||
undo: '실행 취소',
|
||||
redo: '다시 실행',
|
||||
reorder_diagram: '다이어그램 재정렬',
|
||||
reorder_diagram: '다이어그램 자동 정렬',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: '겹치는 테이블 강조 표시',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -304,7 +315,7 @@ export const ko_KR: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: '다이어그램 열기',
|
||||
title: '데이터베이스 열기',
|
||||
description: '아래의 목록에서 다이어그램을 선택하세요.',
|
||||
table_columns: {
|
||||
name: '이름',
|
||||
@@ -314,6 +325,12 @@ export const ko_KR: LanguageTranslation = {
|
||||
},
|
||||
cancel: '취소',
|
||||
open: '열기',
|
||||
|
||||
diagram_actions: {
|
||||
open: '열기',
|
||||
duplicate: '복제',
|
||||
delete: '삭제',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -399,6 +416,14 @@ export const ko_KR: LanguageTranslation = {
|
||||
confirm: '변경',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: '새 스키마 생성',
|
||||
description:
|
||||
'아직 스키마가 없습니다. 테이블을 정리하기 위해 첫 번째 스키마를 생성하세요.',
|
||||
create: '생성',
|
||||
cancel: '취소',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: '개선할 수 있도록 도와주세요!',
|
||||
description:
|
||||
@@ -452,6 +477,7 @@ export const ko_KR: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: '새 테이블',
|
||||
new_view: '새 뷰',
|
||||
new_relationship: '새 연관관계',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -473,6 +499,9 @@ export const ko_KR: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: '언어',
|
||||
},
|
||||
|
||||
on: '켜기',
|
||||
off: '끄기',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const mr: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'नवीन',
|
||||
browse: 'ब्राउज',
|
||||
tables: 'टेबल',
|
||||
refs: 'Refs',
|
||||
areas: 'क्षेत्रे',
|
||||
dependencies: 'अवलंबने',
|
||||
custom_types: 'कस्टम प्रकार',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'फाइल',
|
||||
new: 'नवीन',
|
||||
open: 'उघडा',
|
||||
actions: {
|
||||
actions: 'क्रिया',
|
||||
new: 'नवीन...',
|
||||
browse: 'ब्राउज करा...',
|
||||
save: 'जतन करा',
|
||||
import: 'डेटाबेस इम्पोर्ट करा',
|
||||
export_sql: 'SQL एक्स्पोर्ट करा',
|
||||
export_as: 'म्हणून एक्स्पोर्ट करा',
|
||||
delete_diagram: 'आरेख हटवा',
|
||||
exit: 'बाहेर पडा',
|
||||
delete_diagram: 'हटवा',
|
||||
},
|
||||
edit: {
|
||||
edit: 'संपादन करा',
|
||||
@@ -26,7 +34,10 @@ export const mr: LanguageTranslation = {
|
||||
hide_sidebar: 'साइडबार लपवा',
|
||||
hide_cardinality: 'कार्डिनॅलिटी लपवा',
|
||||
show_cardinality: 'कार्डिनॅलिटी दाखवा',
|
||||
hide_field_attributes: 'फील्ड गुणधर्म लपवा',
|
||||
show_field_attributes: 'फील्ड गुणधर्म दाखवा',
|
||||
zoom_on_scroll: 'स्क्रोलवर झूम करा',
|
||||
show_views: 'डेटाबेस व्ह्यूज',
|
||||
theme: 'थीम',
|
||||
show_dependencies: 'डिपेंडेन्सि दाखवा',
|
||||
hide_dependencies: 'डिपेंडेन्सि लपवा',
|
||||
@@ -64,22 +75,13 @@ export const mr: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'आरेख पुनःक्रमित करा',
|
||||
title: 'आरेख स्वयंचलित व्यवस्थित करा',
|
||||
description:
|
||||
'ही क्रिया आरेखातील सर्व टेबल्सची पुनर्रचना करेल. तुम्हाला पुढे जायचे आहे का?',
|
||||
reorder: 'पुनःक्रमित करा',
|
||||
reorder: 'स्वयंचलित व्यवस्थित करा',
|
||||
cancel: 'रद्द करा',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'एकाधिक स्कीमा',
|
||||
description:
|
||||
'{{schemasCount}} स्कीमा या आरेखात आहेत. सध्या दाखवत आहोत: {{formattedSchemas}}.',
|
||||
dont_show_again: 'पुन्हा दाखवू नका',
|
||||
change_schema: 'बदला',
|
||||
none: 'काहीही नाही',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'कॉपी अयशस्वी',
|
||||
@@ -116,14 +118,11 @@ export const mr: LanguageTranslation = {
|
||||
copied: 'Copied!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'स्कीमा:',
|
||||
filter_by_schema: 'स्कीमा द्वारे फिल्टर करा',
|
||||
search_schema: 'स्कीमा शोधा...',
|
||||
no_schemas_found: 'कोणतेही स्कीमा सापडले नाहीत.',
|
||||
view_all_options: 'सर्व पर्याय पहा...',
|
||||
tables_section: {
|
||||
tables: 'टेबल्स',
|
||||
add_table: 'टेबल जोडा',
|
||||
add_view: 'व्ह्यू जोडा',
|
||||
filter: 'फिल्टर',
|
||||
collapse: 'सर्व संकुचित करा',
|
||||
// TODO: Translate
|
||||
@@ -149,16 +148,23 @@ export const mr: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'फील्ड गुणधर्म',
|
||||
unique: 'युनिक',
|
||||
auto_increment: 'ऑटो इंक्रिमेंट',
|
||||
comments: 'टिप्पण्या',
|
||||
no_comments: 'कोणत्याही टिप्पणी नाहीत',
|
||||
delete_field: 'फील्ड हटवा',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'अचूकता',
|
||||
scale: 'प्रमाण',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'इंडेक्स गुणधर्म',
|
||||
name: 'नाव',
|
||||
unique: 'युनिक',
|
||||
index_type: 'इंडेक्स प्रकार',
|
||||
delete_index: 'इंडेक्स हटवा',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -176,12 +182,15 @@ export const mr: LanguageTranslation = {
|
||||
description: 'सुरू करण्यासाठी एक टेबल तयार करा',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'रिलेशनशिप',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'फिल्टर',
|
||||
add_relationship: 'रिलेशनशिप जोडा',
|
||||
collapse: 'सर्व संकुचित करा',
|
||||
add_relationship: 'रिलेशनशिप जोडा',
|
||||
relationships: 'रिलेशनशिप',
|
||||
dependencies: 'डिपेंडेन्सि',
|
||||
relationship: {
|
||||
relationship: 'रिलेशनशिप',
|
||||
primary: 'प्राथमिक टेबल',
|
||||
foreign: 'रेफरंस टेबल',
|
||||
cardinality: 'कार्डिनॅलिटी',
|
||||
@@ -191,17 +200,8 @@ export const mr: LanguageTranslation = {
|
||||
delete_relationship: 'हटवा',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'कोणतेही रिलेशनशिप नाहीत',
|
||||
description:
|
||||
'टेबल्स कनेक्ट करण्यासाठी एक रिलेशनशिप तयार करा',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'डिपेंडेन्सि',
|
||||
filter: 'फिल्टर',
|
||||
collapse: 'सर्व संकुचित करा',
|
||||
dependency: {
|
||||
dependency: 'डिपेंडेन्सि',
|
||||
table: 'टेबल',
|
||||
dependent_table: 'डिपेंडेन्सि दृश्य',
|
||||
delete_dependency: 'हटवा',
|
||||
@@ -211,8 +211,8 @@ export const mr: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'कोणत्याही डिपेंडेन्सि नाहीत',
|
||||
description: 'सुरू करण्यासाठी एक दृश्य तयार करा',
|
||||
title: 'कोणतेही रिलेशनशिप नाहीत',
|
||||
description: 'सुरू करण्यासाठी एक रिलेशनशिप तयार करा',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -252,12 +252,16 @@ export const mr: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'कोणतीही enum मूल्ये परिभाषित नाहीत',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -271,8 +275,14 @@ export const mr: LanguageTranslation = {
|
||||
show_all: 'सर्व दाखवा',
|
||||
undo: 'पूर्ववत करा',
|
||||
redo: 'पुन्हा करा',
|
||||
reorder_diagram: 'आरेख पुनःक्रमित करा',
|
||||
reorder_diagram: 'आरेख स्वयंचलित व्यवस्थित करा',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'ओव्हरलॅपिंग टेबल्स हायलाइट करा',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -311,7 +321,7 @@ export const mr: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'आरेख उघडा',
|
||||
title: 'डेटाबेस उघडा',
|
||||
description: 'खालील यादीतून उघडण्यासाठी एक आरेख निवडा.',
|
||||
table_columns: {
|
||||
name: 'नाव',
|
||||
@@ -321,6 +331,12 @@ export const mr: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'रद्द करा',
|
||||
open: 'उघडा',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'उघडा',
|
||||
duplicate: 'डुप्लिकेट',
|
||||
delete: 'हटवा',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -407,6 +423,14 @@ export const mr: LanguageTranslation = {
|
||||
confirm: 'बदला',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'नवीन स्कीमा तयार करा',
|
||||
description:
|
||||
'अजून कोणतीही स्कीमा अस्तित्वात नाही. आपल्या टेबल्स व्यवस्थित करण्यासाठी आपली पहिली स्कीमा तयार करा.',
|
||||
create: 'तयार करा',
|
||||
cancel: 'रद्द करा',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'आम्हाला सुधारण्यास मदत करा!',
|
||||
description:
|
||||
@@ -465,6 +489,7 @@ export const mr: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'नवीन टेबल',
|
||||
new_view: 'नवीन व्ह्यू',
|
||||
new_relationship: 'नवीन रिलेशनशिप',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -488,6 +513,9 @@ export const mr: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'भाषा बदला',
|
||||
},
|
||||
|
||||
on: 'चालू',
|
||||
off: 'बंद',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const ne: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'नयाँ',
|
||||
browse: 'ब्राउज',
|
||||
tables: 'टेबलहरू',
|
||||
refs: 'Refs',
|
||||
areas: 'क्षेत्रहरू',
|
||||
dependencies: 'निर्भरताहरू',
|
||||
custom_types: 'कस्टम प्रकारहरू',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'फाइल',
|
||||
new: 'नयाँ',
|
||||
open: 'खोल्नुहोस्',
|
||||
actions: {
|
||||
actions: 'कार्यहरू',
|
||||
new: 'नयाँ...',
|
||||
browse: 'ब्राउज गर्नुहोस्...',
|
||||
save: 'सुरक्षित गर्नुहोस्',
|
||||
import: 'डाटाबेस आयात गर्नुहोस्',
|
||||
export_sql: 'SQL निर्यात गर्नुहोस्',
|
||||
export_as: 'निर्यात गर्नुहोस्',
|
||||
delete_diagram: 'डायाग्राम हटाउनुहोस्',
|
||||
exit: 'बाहिर निस्कनुहोस्',
|
||||
delete_diagram: 'हटाउनुहोस्',
|
||||
},
|
||||
edit: {
|
||||
edit: 'सम्पादन',
|
||||
@@ -26,7 +34,10 @@ export const ne: LanguageTranslation = {
|
||||
hide_sidebar: 'साइडबार लुकाउनुहोस्',
|
||||
hide_cardinality: 'कार्डिन्यालिटी लुकाउनुहोस्',
|
||||
show_cardinality: 'कार्डिन्यालिटी देखाउनुहोस्',
|
||||
hide_field_attributes: 'फिल्ड विशेषताहरू लुकाउनुहोस्',
|
||||
show_field_attributes: 'फिल्ड विशेषताहरू देखाउनुहोस्',
|
||||
zoom_on_scroll: 'स्क्रोलमा जुम गर्नुहोस्',
|
||||
show_views: 'डाटाबेस भ्यूहरू',
|
||||
theme: 'थिम',
|
||||
show_dependencies: 'डिपेन्डेन्सीहरू देखाउनुहोस्',
|
||||
hide_dependencies: 'डिपेन्डेन्सीहरू लुकाउनुहोस्',
|
||||
@@ -64,22 +75,13 @@ export const ne: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'डायाग्राम पुनः क्रमबद्ध गर्नुहोस्',
|
||||
title: 'डायाग्राम स्वचालित मिलाउनुहोस्',
|
||||
description:
|
||||
'यो कार्य पूर्ववत गर्न सकिँदैन। यो डायाग्राम स्थायी रूपमा हटाउनेछ।',
|
||||
reorder: 'पुनः क्रमबद्ध गर्नुहोस्',
|
||||
reorder: 'स्वचालित मिलाउनुहोस्',
|
||||
cancel: 'रद्द गर्नुहोस्',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'विविध स्कीमहरू',
|
||||
description:
|
||||
'{{schemasCount}} डायाग्राममा स्कीमहरू। हालको रूपमा देखाइएको छ: {{formattedSchemas}}।',
|
||||
dont_show_again: 'फेरि देखाउन नदिनुहोस्',
|
||||
change_schema: 'स्कीम परिवर्तन गर्नुहोस्',
|
||||
none: 'कुनै पनि छैन',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'प्रतिलिपि असफल',
|
||||
@@ -114,14 +116,11 @@ export const ne: LanguageTranslation = {
|
||||
copied: 'प्रतिलिपि गरियो!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'स्कीम:',
|
||||
filter_by_schema: 'स्कीम अनुसार फिल्टर गर्नुहोस्',
|
||||
search_schema: 'स्कीम खोज्नुहोस्...',
|
||||
no_schemas_found: 'कुनै स्कीमहरू फेला परेनन्',
|
||||
view_all_options: 'सबै विकल्पहरू हेर्नुहोस्',
|
||||
tables_section: {
|
||||
tables: 'तालिकाहरू',
|
||||
add_table: 'तालिका थप्नुहोस्',
|
||||
add_view: 'भ्यू थप्नुहोस्',
|
||||
filter: 'फिल्टर',
|
||||
collapse: 'सबै लुकाउनुहोस्',
|
||||
// TODO: Translate
|
||||
@@ -147,16 +146,23 @@ export const ne: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'क्षेत्र विशेषताहरू',
|
||||
unique: 'अनन्य',
|
||||
auto_increment: 'स्वचालित वृद्धि',
|
||||
comments: 'टिप्पणीहरू',
|
||||
no_comments: 'कुनै टिप्पणीहरू छैनन्',
|
||||
delete_field: 'क्षेत्र हटाउनुहोस्',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'परिशुद्धता',
|
||||
scale: 'स्केल',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'सूचक विशेषताहरू',
|
||||
name: 'नाम',
|
||||
unique: 'अनन्य',
|
||||
index_type: 'इन्डेक्स प्रकार',
|
||||
delete_index: 'सूचक हटाउनुहोस्',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -173,12 +179,15 @@ export const ne: LanguageTranslation = {
|
||||
description: 'सुरु गर्नका लागि एक तालिका बनाउनुहोस्',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'सम्बन्धहरू',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'फिल्टर',
|
||||
add_relationship: 'सम्बन्ध थप्नुहोस्',
|
||||
collapse: 'सबै लुकाउनुहोस्',
|
||||
add_relationship: 'सम्बन्ध थप्नुहोस्',
|
||||
relationships: 'सम्बन्धहरू',
|
||||
dependencies: 'डिपेन्डेन्सीहरू',
|
||||
relationship: {
|
||||
relationship: 'सम्बन्ध',
|
||||
primary: 'मुख्य तालिका',
|
||||
foreign: 'परिचित तालिका',
|
||||
cardinality: 'कार्डिन्यालिटी',
|
||||
@@ -188,16 +197,8 @@ export const ne: LanguageTranslation = {
|
||||
delete_relationship: 'हटाउनुहोस्',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'कुनै सम्बन्धहरू छैनन्',
|
||||
description: 'तालिकाहरू जोड्नका लागि एक सम्बन्ध बनाउनुहोस्',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'डिपेन्डेन्सीहरू',
|
||||
filter: 'फिल्टर',
|
||||
collapse: 'सबै लुकाउनुहोस्',
|
||||
dependency: {
|
||||
dependency: 'डिपेन्डेन्सी',
|
||||
table: 'तालिका',
|
||||
dependent_table: 'विचलित तालिका',
|
||||
delete_dependency: 'हटाउनुहोस्',
|
||||
@@ -207,9 +208,8 @@ export const ne: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'कुनै डिपेन्डेन्सीहरू छैनन्',
|
||||
description:
|
||||
'डिपेन्डेन्सीहरू देखाउनका लागि एक व्यू बनाउनुहोस्',
|
||||
title: 'कुनै सम्बन्धहरू छैनन्',
|
||||
description: 'सुरु गर्नका लागि एक सम्बन्ध बनाउनुहोस्',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -249,12 +249,16 @@ export const ne: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'कुनै enum मानहरू परिभाषित छैनन्',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -268,9 +272,15 @@ export const ne: LanguageTranslation = {
|
||||
show_all: 'सबै देखाउनुहोस्',
|
||||
undo: 'पूर्ववत',
|
||||
redo: 'पुनः गर्नुहोस्',
|
||||
reorder_diagram: 'पुनः क्रमबद्ध गर्नुहोस्',
|
||||
reorder_diagram: 'डायाग्राम स्वचालित मिलाउनुहोस्',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables:
|
||||
'अतिरिक्त तालिकाहरू हाइलाइट गर्नुहोस्',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -307,7 +317,7 @@ export const ne: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'डायाग्राम खोल्नुहोस्',
|
||||
title: 'डाटाबेस खोल्नुहोस्',
|
||||
description:
|
||||
'तलको सूचीबाट खोल्नका लागि एक डायाग्राम चयन गर्नुहोस्।',
|
||||
table_columns: {
|
||||
@@ -318,6 +328,12 @@ export const ne: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'रद्द गर्नुहोस्',
|
||||
open: 'खोल्नुहोस्',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'खोल्नुहोस्',
|
||||
duplicate: 'डुप्लिकेट',
|
||||
delete: 'मेटाउनुहोस्',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -404,6 +420,14 @@ export const ne: LanguageTranslation = {
|
||||
confirm: 'परिवर्तन गर्नुहोस्',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'नयाँ स्कीम सिर्जना गर्नुहोस्',
|
||||
description:
|
||||
'अहिलेसम्म कुनै स्कीम अस्तित्वमा छैन। आफ्ना तालिकाहरू व्यवस्थित गर्न आफ्नो पहिलो स्कीम सिर्जना गर्नुहोस्।',
|
||||
create: 'सिर्जना गर्नुहोस्',
|
||||
cancel: 'रद्द गर्नुहोस्',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'हामीलाई अझ राम्रो हुन मदत गर्नुहोस!',
|
||||
description:
|
||||
@@ -459,6 +483,7 @@ export const ne: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'नयाँ तालिका',
|
||||
new_view: 'नयाँ भ्यू',
|
||||
new_relationship: 'नयाँ सम्बन्ध',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -480,6 +505,9 @@ export const ne: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'भाषा परिवर्तन गर्नुहोस्',
|
||||
},
|
||||
|
||||
on: 'सक्रिय',
|
||||
off: 'निष्क्रिय',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const pt_BR: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Novo',
|
||||
browse: 'Navegar',
|
||||
tables: 'Tabelas',
|
||||
refs: 'Refs',
|
||||
areas: 'Áreas',
|
||||
dependencies: 'Dependências',
|
||||
custom_types: 'Tipos Personalizados',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'Arquivo',
|
||||
new: 'Novo',
|
||||
open: 'Abrir',
|
||||
actions: {
|
||||
actions: 'Ações',
|
||||
new: 'Novo...',
|
||||
browse: 'Navegar...',
|
||||
save: 'Salvar',
|
||||
import: 'Importar Banco de Dados',
|
||||
export_sql: 'Exportar SQL',
|
||||
export_as: 'Exportar como',
|
||||
delete_diagram: 'Excluir Diagrama',
|
||||
exit: 'Sair',
|
||||
delete_diagram: 'Excluir',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Editar',
|
||||
@@ -26,7 +34,10 @@ export const pt_BR: LanguageTranslation = {
|
||||
hide_sidebar: 'Ocultar Barra Lateral',
|
||||
hide_cardinality: 'Ocultar Cardinalidade',
|
||||
show_cardinality: 'Mostrar Cardinalidade',
|
||||
hide_field_attributes: 'Ocultar Atributos de Campo',
|
||||
show_field_attributes: 'Mostrar Atributos de Campo',
|
||||
zoom_on_scroll: 'Zoom ao Rolar',
|
||||
show_views: 'Visualizações do Banco de Dados',
|
||||
theme: 'Tema',
|
||||
show_dependencies: 'Mostrar Dependências',
|
||||
hide_dependencies: 'Ocultar Dependências',
|
||||
@@ -64,22 +75,13 @@ export const pt_BR: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Reordenar Diagrama',
|
||||
title: 'Organizar Diagrama Automaticamente',
|
||||
description:
|
||||
'Esta ação reorganizará todas as tabelas no diagrama. Deseja continuar?',
|
||||
reorder: 'Reordenar',
|
||||
reorder: 'Organizar Automaticamente',
|
||||
cancel: 'Cancelar',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Múltiplos Esquemas',
|
||||
description:
|
||||
'{{schemasCount}} esquemas neste diagrama. Atualmente exibindo: {{formattedSchemas}}.',
|
||||
dont_show_again: 'Não mostrar novamente',
|
||||
change_schema: 'Alterar',
|
||||
none: 'nenhum',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'Falha na cópia',
|
||||
@@ -114,14 +116,11 @@ export const pt_BR: LanguageTranslation = {
|
||||
copied: 'Copiado!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Esquema:',
|
||||
filter_by_schema: 'Filtrar por esquema',
|
||||
search_schema: 'Buscar esquema...',
|
||||
no_schemas_found: 'Nenhum esquema encontrado.',
|
||||
view_all_options: 'Ver todas as Opções...',
|
||||
tables_section: {
|
||||
tables: 'Tabelas',
|
||||
add_table: 'Adicionar Tabela',
|
||||
add_view: 'Adicionar Visualização',
|
||||
filter: 'Filtrar',
|
||||
collapse: 'Colapsar Todas',
|
||||
// TODO: Translate
|
||||
@@ -147,16 +146,23 @@ export const pt_BR: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'Atributos do Campo',
|
||||
unique: 'Único',
|
||||
auto_increment: 'Incremento Automático',
|
||||
comments: 'Comentários',
|
||||
no_comments: 'Sem comentários',
|
||||
delete_field: 'Excluir Campo',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'Precisão',
|
||||
scale: 'Escala',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Atributos do Índice',
|
||||
name: 'Nome',
|
||||
unique: 'Único',
|
||||
index_type: 'Tipo de Índice',
|
||||
delete_index: 'Excluir Índice',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -173,12 +179,15 @@ export const pt_BR: LanguageTranslation = {
|
||||
description: 'Crie uma tabela para começar',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'Relacionamentos',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Filtrar',
|
||||
add_relationship: 'Adicionar Relacionamento',
|
||||
collapse: 'Colapsar Todas',
|
||||
add_relationship: 'Adicionar Relacionamento',
|
||||
relationships: 'Relacionamentos',
|
||||
dependencies: 'Dependências',
|
||||
relationship: {
|
||||
relationship: 'Relacionamento',
|
||||
primary: 'Tabela Primária',
|
||||
foreign: 'Tabela Referenciada',
|
||||
cardinality: 'Cardinalidade',
|
||||
@@ -188,16 +197,8 @@ export const pt_BR: LanguageTranslation = {
|
||||
delete_relationship: 'Excluir',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Sem relacionamentos',
|
||||
description: 'Crie um relacionamento para conectar tabelas',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Dependências',
|
||||
filter: 'Filtrar',
|
||||
collapse: 'Colapsar Todas',
|
||||
dependency: {
|
||||
dependency: 'Dependência',
|
||||
table: 'Tabela',
|
||||
dependent_table: 'Visualização Dependente',
|
||||
delete_dependency: 'Excluir',
|
||||
@@ -207,8 +208,8 @@ export const pt_BR: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Sem dependências',
|
||||
description: 'Crie uma visualização para começar',
|
||||
title: 'Sem relacionamentos',
|
||||
description: 'Crie um relacionamento para começar',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -248,12 +249,16 @@ export const pt_BR: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'Nenhum valor de enum definido',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -267,8 +272,14 @@ export const pt_BR: LanguageTranslation = {
|
||||
show_all: 'Mostrar Tudo',
|
||||
undo: 'Desfazer',
|
||||
redo: 'Refazer',
|
||||
reorder_diagram: 'Reordenar Diagrama',
|
||||
reorder_diagram: 'Organizar Diagrama Automaticamente',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'Destacar Tabelas Sobrepostas',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -306,7 +317,7 @@ export const pt_BR: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Abrir Diagrama',
|
||||
title: 'Abrir Banco de Dados',
|
||||
description: 'Selecione um diagrama para abrir da lista abaixo.',
|
||||
table_columns: {
|
||||
name: 'Nome',
|
||||
@@ -316,6 +327,12 @@ export const pt_BR: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'Cancelar',
|
||||
open: 'Abrir',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Abrir',
|
||||
duplicate: 'Duplicar',
|
||||
delete: 'Excluir',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -402,6 +419,14 @@ export const pt_BR: LanguageTranslation = {
|
||||
confirm: 'Alterar',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'Criar Novo Esquema',
|
||||
description:
|
||||
'Ainda não existem esquemas. Crie seu primeiro esquema para organizar suas tabelas.',
|
||||
create: 'Criar',
|
||||
cancel: 'Cancelar',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'Ajude-nos a melhorar!',
|
||||
description:
|
||||
@@ -457,6 +482,7 @@ export const pt_BR: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'Nova Tabela',
|
||||
new_view: 'Nova Visualização',
|
||||
new_relationship: 'Novo Relacionamento',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -479,6 +505,9 @@ export const pt_BR: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'Idioma',
|
||||
},
|
||||
|
||||
on: 'Ligado',
|
||||
off: 'Desligado',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const ru: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Новая',
|
||||
browse: 'Обзор',
|
||||
tables: 'Таблицы',
|
||||
refs: 'Ссылки',
|
||||
areas: 'Области',
|
||||
dependencies: 'Зависимости',
|
||||
custom_types: 'Пользовательские типы',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'Файл',
|
||||
new: 'Создать',
|
||||
open: 'Открыть',
|
||||
actions: {
|
||||
actions: 'Действия',
|
||||
new: 'Новая...',
|
||||
browse: 'Обзор...',
|
||||
save: 'Сохранить',
|
||||
import: 'Импортировать базу данных',
|
||||
export_sql: 'Экспорт SQL',
|
||||
export_as: 'Экспортировать как',
|
||||
delete_diagram: 'Удалить диаграмму',
|
||||
exit: 'Выход',
|
||||
delete_diagram: 'Удалить',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Изменение',
|
||||
@@ -26,7 +34,10 @@ export const ru: LanguageTranslation = {
|
||||
hide_sidebar: 'Скрыть боковую панель',
|
||||
hide_cardinality: 'Скрыть виды связи',
|
||||
show_cardinality: 'Показать виды связи',
|
||||
show_field_attributes: 'Показать атрибуты поля',
|
||||
hide_field_attributes: 'Скрыть атрибуты поля',
|
||||
zoom_on_scroll: 'Увеличение при прокрутке',
|
||||
show_views: 'Представления базы данных',
|
||||
theme: 'Тема',
|
||||
show_dependencies: 'Показать зависимости',
|
||||
hide_dependencies: 'Скрыть зависимости',
|
||||
@@ -62,22 +73,13 @@ export const ru: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Переупорядочить диаграмму',
|
||||
title: 'Автоматическая расстановка диаграммы',
|
||||
description:
|
||||
'Это действие переставит все таблицы на диаграмме. Хотите продолжить?',
|
||||
reorder: 'Изменить порядок',
|
||||
reorder: 'Автоматическая расстановка',
|
||||
cancel: 'Отменить',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Множественные схемы',
|
||||
description:
|
||||
'{{schemasCount}} схем в этой диаграмме. В данный момент отображается: {{formattedSchemas}}.',
|
||||
dont_show_again: 'Больше не показывать',
|
||||
change_schema: 'Изменить',
|
||||
none: 'никто',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'Ошибка копирования',
|
||||
@@ -111,14 +113,11 @@ export const ru: LanguageTranslation = {
|
||||
show_less: 'Показать меньше',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Схема:',
|
||||
filter_by_schema: 'Фильтр по схеме',
|
||||
search_schema: 'Схема поиска...',
|
||||
no_schemas_found: 'Схемы не найдены.',
|
||||
view_all_options: 'Просмотреть все варианты...',
|
||||
tables_section: {
|
||||
tables: 'Таблицы',
|
||||
add_table: 'Добавить таблицу',
|
||||
add_view: 'Добавить представление',
|
||||
filter: 'Фильтр',
|
||||
collapse: 'Свернуть все',
|
||||
clear: 'Очистить фильтр',
|
||||
@@ -144,15 +143,22 @@ export const ru: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'Атрибуты поля',
|
||||
unique: 'Уникальный',
|
||||
auto_increment: 'Автоинкремент',
|
||||
comments: 'Комментарии',
|
||||
no_comments: 'Нет комментария',
|
||||
delete_field: 'Удалить поле',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
character_length: 'Макс. длина',
|
||||
precision: 'Точность',
|
||||
scale: 'Масштаб',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Атрибуты индекса',
|
||||
name: 'Имя',
|
||||
unique: 'Уникальный',
|
||||
index_type: 'Тип индекса',
|
||||
delete_index: 'Удалить индекс',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -169,12 +175,15 @@ export const ru: LanguageTranslation = {
|
||||
description: 'Создайте таблицу, чтобы начать',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'Отношения',
|
||||
refs_section: {
|
||||
refs: 'Ссылки',
|
||||
filter: 'Фильтр',
|
||||
add_relationship: 'Добавить отношение',
|
||||
collapse: 'Свернуть все',
|
||||
add_relationship: 'Добавить отношение',
|
||||
relationships: 'Отношения',
|
||||
dependencies: 'Зависимости',
|
||||
relationship: {
|
||||
relationship: 'Отношение',
|
||||
primary: 'Основная таблица',
|
||||
foreign: 'Справочная таблица',
|
||||
cardinality: 'Тип множественной связи',
|
||||
@@ -184,18 +193,10 @@ export const ru: LanguageTranslation = {
|
||||
delete_relationship: 'Удалить',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Нет отношений',
|
||||
description: 'Создайте связь для соединения таблиц',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Зависимости',
|
||||
filter: 'Фильтр',
|
||||
collapse: 'Свернуть все',
|
||||
dependency: {
|
||||
table: 'Стол',
|
||||
dependent_table: 'Зависимый вид',
|
||||
dependency: 'Зависимость',
|
||||
table: 'Таблица',
|
||||
dependent_table: 'Зависимое представление',
|
||||
delete_dependency: 'Удалить',
|
||||
dependency_actions: {
|
||||
title: 'Действия',
|
||||
@@ -203,8 +204,8 @@ export const ru: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Нет зависимостей',
|
||||
description: 'Создайте представление, чтобы начать',
|
||||
title: 'Нет отношений',
|
||||
description: 'Создайте отношение, чтобы начать',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -245,12 +246,16 @@ export const ru: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'Значения перечисления не определены',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -264,8 +269,14 @@ export const ru: LanguageTranslation = {
|
||||
show_all: 'Показать все',
|
||||
undo: 'Отменить',
|
||||
redo: 'Вернуть',
|
||||
reorder_diagram: 'Переупорядочить диаграмму',
|
||||
reorder_diagram: 'Автоматическая расстановка диаграммы',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'Выделение перекрывающихся таблиц',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -302,7 +313,7 @@ export const ru: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Открыть диаграмму',
|
||||
title: 'Открыть базу данных',
|
||||
description:
|
||||
'Выберите диаграмму, которую нужно открыть, из списка ниже.',
|
||||
table_columns: {
|
||||
@@ -313,6 +324,12 @@ export const ru: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'Отмена',
|
||||
open: 'Открыть',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Открыть',
|
||||
duplicate: 'Дублировать',
|
||||
delete: 'Удалить',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -399,6 +416,14 @@ export const ru: LanguageTranslation = {
|
||||
confirm: 'Изменить',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'Создать новую схему',
|
||||
description:
|
||||
'Схемы еще не существуют. Создайте вашу первую схему, чтобы организовать таблицы.',
|
||||
create: 'Создать',
|
||||
cancel: 'Отменить',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'Помогите нам стать лучше!',
|
||||
description:
|
||||
@@ -453,6 +478,7 @@ export const ru: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'Создать таблицу',
|
||||
new_view: 'Новое представление',
|
||||
new_relationship: 'Создать отношение',
|
||||
new_area: 'Новая область',
|
||||
},
|
||||
@@ -474,6 +500,9 @@ export const ru: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'Сменить язык',
|
||||
},
|
||||
|
||||
on: 'Вкл',
|
||||
off: 'Выкл',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const te: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'కొత్తది',
|
||||
browse: 'బ్రాఉజ్',
|
||||
tables: 'టేబల్లు',
|
||||
refs: 'సంబంధాలు',
|
||||
areas: 'ప్రదేశాలు',
|
||||
dependencies: 'ఆధారతలు',
|
||||
custom_types: 'కస్టమ్ టైప్స్',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'ఫైల్',
|
||||
new: 'కొత్తది',
|
||||
open: 'తెరవు',
|
||||
actions: {
|
||||
actions: 'చర్యలు',
|
||||
new: 'కొత్తది...',
|
||||
browse: 'బ్రాఉజ్ చేయండి...',
|
||||
save: 'సేవ్',
|
||||
import: 'డేటాబేస్ను దిగుమతి చేసుకోండి',
|
||||
export_sql: 'SQL ఎగుమతి',
|
||||
export_as: 'వగా ఎగుమతి చేయండి',
|
||||
delete_diagram: 'చిత్రాన్ని తొలగించండి',
|
||||
exit: 'నిష్క్రమించు',
|
||||
delete_diagram: 'తొలగించండి',
|
||||
},
|
||||
edit: {
|
||||
edit: 'సవరించు',
|
||||
@@ -26,7 +34,10 @@ export const te: LanguageTranslation = {
|
||||
hide_sidebar: 'సైడ్బార్ దాచండి',
|
||||
hide_cardinality: 'కార్డినాలిటీని దాచండి',
|
||||
show_cardinality: 'కార్డినాలిటీని చూపించండి',
|
||||
show_field_attributes: 'ఫీల్డ్ గుణాలను చూపించు',
|
||||
hide_field_attributes: 'ఫీల్డ్ గుణాలను దాచండి',
|
||||
zoom_on_scroll: 'స్క్రోల్పై జూమ్',
|
||||
show_views: 'డేటాబేస్ వ్యూలు',
|
||||
theme: 'థీమ్',
|
||||
show_dependencies: 'ఆధారాలు చూపించండి',
|
||||
hide_dependencies: 'ఆధారాలను దాచండి',
|
||||
@@ -64,22 +75,13 @@ export const te: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'చిత్రాన్ని పునఃసరిచేయండి',
|
||||
title: 'చిత్రాన్ని స్వయంచాలకంగా అమర్చండి',
|
||||
description:
|
||||
'ఈ చర్య చిత్రంలోని అన్ని పట్టికలను పునఃస్థాపిస్తుంది. మీరు కొనసాగించాలనుకుంటున్నారా?',
|
||||
reorder: 'పునఃసరిచేయండి',
|
||||
reorder: 'స్వయంచాలకంగా అమర్చండి',
|
||||
cancel: 'రద్దు',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'బహుళ స్కీమాలు',
|
||||
description:
|
||||
'{{schemasCount}} స్కీమాలు ఈ చిత్రంలో ఉన్నాయి. ప్రస్తుత స్కీమాలు: {{formattedSchemas}}.',
|
||||
dont_show_again: 'మరలా చూపించవద్దు',
|
||||
change_schema: 'మార్చు',
|
||||
none: 'ఎదరికాదు',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'కాపీ విఫలమైంది',
|
||||
@@ -114,14 +116,11 @@ export const te: LanguageTranslation = {
|
||||
copied: 'కాపీ చేయబడింది!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'స్కీమా:',
|
||||
filter_by_schema: 'స్కీమా ద్వారా ఫిల్టర్ చేయండి',
|
||||
search_schema: 'స్కీమా కోసం శోధించండి...',
|
||||
no_schemas_found: 'ఏ స్కీమాలు కూడా కనుగొనబడలేదు.',
|
||||
view_all_options: 'అన్ని ఎంపికలను చూడండి...',
|
||||
tables_section: {
|
||||
tables: 'పట్టికలు',
|
||||
add_table: 'పట్టికను జోడించు',
|
||||
add_view: 'వ్యూ జోడించండి',
|
||||
filter: 'ఫిల్టర్',
|
||||
collapse: 'అన్ని కూల్ చేయి',
|
||||
// TODO: Translate
|
||||
@@ -147,16 +146,23 @@ export const te: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'ఫీల్డ్ గుణాలు',
|
||||
unique: 'అద్వితీయ',
|
||||
auto_increment: 'ఆటో ఇంక్రిమెంట్',
|
||||
comments: 'వ్యాఖ్యలు',
|
||||
no_comments: 'వ్యాఖ్యలు లేవు',
|
||||
delete_field: 'ఫీల్డ్ తొలగించు',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'సూక్ష్మత',
|
||||
scale: 'స్కేల్',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'ఇండెక్స్ గుణాలు',
|
||||
name: 'పేరు',
|
||||
unique: 'అద్వితీయ',
|
||||
index_type: 'ఇండెక్స్ రకం',
|
||||
delete_index: 'ఇండెక్స్ తొలగించు',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -174,12 +180,15 @@ export const te: LanguageTranslation = {
|
||||
description: 'ప్రారంభించడానికి ఒక పట్టిక సృష్టించండి',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'సంబంధాలు',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'ఫిల్టర్',
|
||||
add_relationship: 'సంబంధం జోడించు',
|
||||
collapse: 'అన్ని కూల్ చేయి',
|
||||
add_relationship: 'సంబంధం జోడించు',
|
||||
relationships: 'సంబంధాలు',
|
||||
dependencies: 'ఆధారాలు',
|
||||
relationship: {
|
||||
relationship: 'సంబంధం',
|
||||
primary: 'ప్రాథమిక పట్టిక',
|
||||
foreign: 'సూచించబడిన పట్టిక',
|
||||
cardinality: 'కార్డినాలిటీ',
|
||||
@@ -189,16 +198,8 @@ export const te: LanguageTranslation = {
|
||||
delete_relationship: 'సంబంధం తొలగించు',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'సంబంధాలు లేవు',
|
||||
description: 'పట్టికలను అనుసంధించడానికి సంబంధం సృష్టించండి',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'ఆధారాలు',
|
||||
filter: 'ఫిల్టర్',
|
||||
collapse: 'అన్ని కూల్ చేయి',
|
||||
dependency: {
|
||||
dependency: 'ఆధారం',
|
||||
table: 'పట్టిక',
|
||||
dependent_table: 'ఆధారిత వీక్షణ',
|
||||
delete_dependency: 'ఆధారాన్ని తొలగించు',
|
||||
@@ -208,8 +209,8 @@ export const te: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'ఆధారాలు లేవు',
|
||||
description: 'ప్రారంభించడానికి ఒక వీక్షణ సృష్టించండి',
|
||||
title: 'సంబంధాలు లేవు',
|
||||
description: 'ప్రారంభించడానికి ఒక సంబంధం సృష్టించండి',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -249,12 +250,16 @@ export const te: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'ఏ enum విలువలు నిర్వచించబడలేదు',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -268,8 +273,14 @@ export const te: LanguageTranslation = {
|
||||
show_all: 'అన్ని చూపించు',
|
||||
undo: 'తిరిగి చేయు',
|
||||
redo: 'మరలా చేయు',
|
||||
reorder_diagram: 'చిత్రాన్ని పునఃసరిచేయండి',
|
||||
reorder_diagram: 'చిత్రాన్ని స్వయంచాలకంగా అమర్చండి',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'అవకాశించు పట్టికలను హైలైట్ చేయండి',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -307,7 +318,7 @@ export const te: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'చిత్రం తెరవండి',
|
||||
title: 'డేటాబేస్ తెరవండి',
|
||||
description: 'కింద ఉన్న జాబితా నుండి చిత్రాన్ని ఎంచుకోండి.',
|
||||
table_columns: {
|
||||
name: 'పేరు',
|
||||
@@ -317,6 +328,12 @@ export const te: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'రద్దు',
|
||||
open: 'తెరవు',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'తెరవు',
|
||||
duplicate: 'నకలు',
|
||||
delete: 'తొలగించు',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -403,6 +420,14 @@ export const te: LanguageTranslation = {
|
||||
confirm: 'మార్చు',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'కొత్త స్కీమా సృష్టించండి',
|
||||
description:
|
||||
'ఇంకా ఏ స్కీమాలు లేవు. మీ పట్టికలను వ్యవస్థీకరించడానికి మీ మొదటి స్కీమాను సృష్టించండి.',
|
||||
create: 'సృష్టించు',
|
||||
cancel: 'రద్దు',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'మా సహాయంతో మెరుగుపరచండి!',
|
||||
description:
|
||||
@@ -461,6 +486,7 @@ export const te: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'కొత్త పట్టిక',
|
||||
new_view: 'కొత్త వ్యూ',
|
||||
new_relationship: 'కొత్త సంబంధం',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -484,6 +510,9 @@ export const te: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'భాష మార్చు',
|
||||
},
|
||||
|
||||
on: 'ఆన్',
|
||||
off: 'ఆఫ్',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const tr: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Yeni',
|
||||
browse: 'Gözat',
|
||||
tables: 'Tablolar',
|
||||
refs: 'Refs',
|
||||
areas: 'Alanlar',
|
||||
dependencies: 'Bağımlılıklar',
|
||||
custom_types: 'Özel Tipler',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'Dosya',
|
||||
new: 'Yeni',
|
||||
open: 'Aç',
|
||||
actions: {
|
||||
actions: 'Eylemler',
|
||||
new: 'Yeni...',
|
||||
browse: 'Gözat...',
|
||||
save: 'Kaydet',
|
||||
import: 'Veritabanı İçe Aktar',
|
||||
export_sql: 'SQL Olarak Dışa Aktar',
|
||||
export_as: 'Olarak Dışa Aktar',
|
||||
delete_diagram: 'Diyagramı Sil',
|
||||
exit: 'Çıkış',
|
||||
delete_diagram: 'Sil',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Düzenle',
|
||||
@@ -26,7 +34,10 @@ export const tr: LanguageTranslation = {
|
||||
hide_sidebar: 'Kenar Çubuğunu Gizle',
|
||||
hide_cardinality: 'Kardinaliteyi Gizle',
|
||||
show_cardinality: 'Kardinaliteyi Göster',
|
||||
show_field_attributes: 'Alan Özelliklerini Göster',
|
||||
hide_field_attributes: 'Alan Özelliklerini Gizle',
|
||||
zoom_on_scroll: 'Kaydırarak Yakınlaştır',
|
||||
show_views: 'Veritabanı Görünümleri',
|
||||
theme: 'Tema',
|
||||
show_dependencies: 'Bağımlılıkları Göster',
|
||||
hide_dependencies: 'Bağımlılıkları Gizle',
|
||||
@@ -64,22 +75,13 @@ export const tr: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Diyagramı Yeniden Sırala',
|
||||
title: 'Diyagramı Otomatik Düzenle',
|
||||
description:
|
||||
'Bu işlem tüm tabloları yeniden düzenleyecektir. Devam etmek istiyor musunuz?',
|
||||
reorder: 'Yeniden Sırala',
|
||||
reorder: 'Otomatik Düzenle',
|
||||
cancel: 'İptal',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Birden Fazla Şema',
|
||||
description:
|
||||
'Bu diyagramda {{schemasCount}} şema var. Şu anda görüntülenen: {{formattedSchemas}}.',
|
||||
dont_show_again: 'Tekrar gösterme',
|
||||
change_schema: 'Değiştir',
|
||||
none: 'yok',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'Kopyalama başarısız',
|
||||
@@ -113,14 +115,11 @@ export const tr: LanguageTranslation = {
|
||||
copy_to_clipboard: 'Panoya Kopyala',
|
||||
copied: 'Kopyalandı!',
|
||||
side_panel: {
|
||||
schema: 'Şema:',
|
||||
filter_by_schema: 'Şemaya Göre Filtrele',
|
||||
search_schema: 'Şema ara...',
|
||||
no_schemas_found: 'Şema bulunamadı.',
|
||||
view_all_options: 'Tüm Seçenekleri Gör...',
|
||||
tables_section: {
|
||||
tables: 'Tablolar',
|
||||
add_table: 'Tablo Ekle',
|
||||
add_view: 'Görünüm Ekle',
|
||||
filter: 'Filtrele',
|
||||
collapse: 'Hepsini Daralt',
|
||||
// TODO: Translate
|
||||
@@ -146,16 +145,23 @@ export const tr: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'Alan Özellikleri',
|
||||
unique: 'Tekil',
|
||||
auto_increment: 'Otomatik Artış',
|
||||
comments: 'Yorumlar',
|
||||
no_comments: 'Yorum yok',
|
||||
delete_field: 'Alanı Sil',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'Hassasiyet',
|
||||
scale: 'Ölçek',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'İndeks Özellikleri',
|
||||
name: 'Ad',
|
||||
unique: 'Tekil',
|
||||
index_type: 'İndeks Türü',
|
||||
delete_index: 'İndeksi Sil',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -173,12 +179,15 @@ export const tr: LanguageTranslation = {
|
||||
description: 'Başlamak için bir tablo oluşturun',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'İlişkiler',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Filtrele',
|
||||
add_relationship: 'İlişki Ekle',
|
||||
collapse: 'Hepsini Daralt',
|
||||
add_relationship: 'İlişki Ekle',
|
||||
relationships: 'İlişkiler',
|
||||
dependencies: 'Bağımlılıklar',
|
||||
relationship: {
|
||||
relationship: 'İlişki',
|
||||
primary: 'Birincil Tablo',
|
||||
foreign: 'Referans Tablo',
|
||||
cardinality: 'Kardinalite',
|
||||
@@ -188,16 +197,8 @@ export const tr: LanguageTranslation = {
|
||||
delete_relationship: 'Sil',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'İlişki yok',
|
||||
description: 'Tabloları bağlamak için bir ilişki oluşturun',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Bağımlılıklar',
|
||||
filter: 'Filtrele',
|
||||
collapse: 'Hepsini Daralt',
|
||||
dependency: {
|
||||
dependency: 'Bağımlılık',
|
||||
table: 'Tablo',
|
||||
dependent_table: 'Bağımlı Görünüm',
|
||||
delete_dependency: 'Sil',
|
||||
@@ -207,8 +208,8 @@ export const tr: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Bağımlılık yok',
|
||||
description: 'Başlamak için bir görünüm oluşturun',
|
||||
title: 'İlişki yok',
|
||||
description: 'Başlamak için bir ilişki oluşturun',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -248,12 +249,16 @@ export const tr: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'Tanımlanmış enum değeri yok',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -266,8 +271,14 @@ export const tr: LanguageTranslation = {
|
||||
show_all: 'Hepsini Gör',
|
||||
undo: 'Geri Al',
|
||||
redo: 'Yinele',
|
||||
reorder_diagram: 'Diyagramı Yeniden Sırala',
|
||||
reorder_diagram: 'Diyagramı Otomatik Düzenle',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'Çakışan Tabloları Vurgula',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
new_diagram_dialog: {
|
||||
database_selection: {
|
||||
@@ -302,7 +313,7 @@ export const tr: LanguageTranslation = {
|
||||
import: 'İçe Aktar',
|
||||
},
|
||||
open_diagram_dialog: {
|
||||
title: 'Diyagramı Aç',
|
||||
title: 'Veritabanı Aç',
|
||||
description: 'Aşağıdaki listeden açmak için bir diyagram seçin.',
|
||||
table_columns: {
|
||||
name: 'Ad',
|
||||
@@ -312,6 +323,12 @@ export const tr: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'İptal',
|
||||
open: 'Aç',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Aç',
|
||||
duplicate: 'Kopyala',
|
||||
delete: 'Sil',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -392,6 +409,14 @@ export const tr: LanguageTranslation = {
|
||||
cancel: 'İptal',
|
||||
confirm: 'Değiştir',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'Yeni Şema Oluştur',
|
||||
description:
|
||||
'Henüz hiç şema mevcut değil. Tablolarınızı düzenlemek için ilk şemanızı oluşturun.',
|
||||
create: 'Oluştur',
|
||||
cancel: 'İptal',
|
||||
},
|
||||
star_us_dialog: {
|
||||
title: 'Bize yardım et!',
|
||||
description:
|
||||
@@ -446,6 +471,7 @@ export const tr: LanguageTranslation = {
|
||||
},
|
||||
canvas_context_menu: {
|
||||
new_table: 'Yeni Tablo',
|
||||
new_view: 'Yeni Görünüm',
|
||||
new_relationship: 'Yeni İlişki',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -468,6 +494,9 @@ export const tr: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'Dil',
|
||||
},
|
||||
|
||||
on: 'Açık',
|
||||
off: 'Kapalı',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const uk: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Нова',
|
||||
browse: 'Огляд',
|
||||
tables: 'Таблиці',
|
||||
refs: 'Зв’язки',
|
||||
areas: 'Області',
|
||||
dependencies: 'Залежності',
|
||||
custom_types: 'Користувацькі типи',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'Файл',
|
||||
new: 'Новий',
|
||||
open: 'Відкрити',
|
||||
actions: {
|
||||
actions: 'Дії',
|
||||
new: 'Нова...',
|
||||
browse: 'Огляд...',
|
||||
save: 'Зберегти',
|
||||
import: 'Імпорт бази даних',
|
||||
export_sql: 'Експорт SQL',
|
||||
export_as: 'Експортувати як',
|
||||
delete_diagram: 'Видалити діаграму',
|
||||
exit: 'Вийти',
|
||||
delete_diagram: 'Видалити',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Редагувати',
|
||||
@@ -26,7 +34,10 @@ export const uk: LanguageTranslation = {
|
||||
hide_sidebar: 'Приховати бічну панель',
|
||||
hide_cardinality: 'Приховати потужність',
|
||||
show_cardinality: 'Показати кардинальність',
|
||||
show_field_attributes: 'Показати атрибути полів',
|
||||
hide_field_attributes: 'Приховати атрибути полів',
|
||||
zoom_on_scroll: 'Масштабувати прокручуванням',
|
||||
show_views: 'Представлення бази даних',
|
||||
theme: 'Тема',
|
||||
show_dependencies: 'Показати залежності',
|
||||
hide_dependencies: 'Приховати залежності',
|
||||
@@ -62,22 +73,13 @@ export const uk: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Перевпорядкувати діаграму',
|
||||
title: 'Автоматичне розміщення діаграми',
|
||||
description:
|
||||
'Ця дія перевпорядкує всі таблиці на діаграмі. Хочете продовжити?',
|
||||
reorder: 'Перевпорядкувати',
|
||||
reorder: 'Автоматичне розміщення',
|
||||
cancel: 'Скасувати',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Кілька схем',
|
||||
description:
|
||||
'{{schemasCount}} схеми на цій діаграмі. Зараз відображається: {{formattedSchemas}}.',
|
||||
dont_show_again: 'Більше не показувати',
|
||||
change_schema: 'Зміна',
|
||||
none: 'немає',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'Помилка копіювання',
|
||||
@@ -112,14 +114,11 @@ export const uk: LanguageTranslation = {
|
||||
copied: 'Скопійовано!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Схема:',
|
||||
filter_by_schema: 'Фільтрувати за схемою',
|
||||
search_schema: 'Пошук схеми…',
|
||||
no_schemas_found: 'Схеми не знайдено.',
|
||||
view_all_options: 'Переглянути всі параметри…',
|
||||
tables_section: {
|
||||
tables: 'Таблиці',
|
||||
add_table: 'Додати таблицю',
|
||||
add_view: 'Додати представлення',
|
||||
filter: 'Фільтр',
|
||||
collapse: 'Згорнути все',
|
||||
// TODO: Translate
|
||||
@@ -145,16 +144,23 @@ export const uk: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'Атрибути полів',
|
||||
unique: 'Унікальне',
|
||||
auto_increment: 'Автоінкремент',
|
||||
comments: 'Коментарі',
|
||||
no_comments: 'Немає коментарів',
|
||||
delete_field: 'Видалити поле',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'Точність',
|
||||
scale: 'Масштаб',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Атрибути індексу',
|
||||
name: 'Назва індекса',
|
||||
unique: 'Унікальний',
|
||||
index_type: 'Тип індексу',
|
||||
delete_index: 'Видалити індекс',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -171,12 +177,15 @@ export const uk: LanguageTranslation = {
|
||||
description: 'Щоб почати, створіть таблицю',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'Звʼязки',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Фільтр',
|
||||
add_relationship: 'Додати звʼязок',
|
||||
collapse: 'Згорнути все',
|
||||
add_relationship: 'Додати звʼязок',
|
||||
relationships: 'Звʼязки',
|
||||
dependencies: 'Залежності',
|
||||
relationship: {
|
||||
relationship: 'Звʼязок',
|
||||
primary: 'Первинна таблиця',
|
||||
foreign: 'Посилання на таблицю',
|
||||
cardinality: 'Звʼязок',
|
||||
@@ -186,16 +195,8 @@ export const uk: LanguageTranslation = {
|
||||
delete_relationship: 'Видалити',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Звʼязків немає',
|
||||
description: 'Створіть звʼязок для зʼєднання таблиць',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Залежності',
|
||||
filter: 'Фільтр',
|
||||
collapse: 'Згорнути все',
|
||||
dependency: {
|
||||
dependency: 'Залежність',
|
||||
table: 'Таблиця',
|
||||
dependent_table: 'Залежне подання',
|
||||
delete_dependency: 'Видалити',
|
||||
@@ -205,8 +206,8 @@ export const uk: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Жодних залежностей',
|
||||
description: 'Створіть подання, щоб почати',
|
||||
title: 'Жодних зв’язків',
|
||||
description: 'Створіть зв’язок, щоб почати',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -246,12 +247,16 @@ export const uk: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'Значення переліку не визначені',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -265,8 +270,14 @@ export const uk: LanguageTranslation = {
|
||||
show_all: 'Показати все',
|
||||
undo: 'Скасувати',
|
||||
redo: 'Повторити',
|
||||
reorder_diagram: 'Перевпорядкувати діаграму',
|
||||
reorder_diagram: 'Автоматичне розміщення діаграми',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'Показати таблиці, що перекриваються',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -303,7 +314,7 @@ export const uk: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Відкрити діаграму',
|
||||
title: 'Відкрити базу даних',
|
||||
description:
|
||||
'Виберіть діаграму, яку потрібно відкрити, зі списку нижче.',
|
||||
table_columns: {
|
||||
@@ -314,6 +325,12 @@ export const uk: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'Скасувати',
|
||||
open: 'Відкрити',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Відкрити',
|
||||
duplicate: 'Дублювати',
|
||||
delete: 'Видалити',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -400,6 +417,14 @@ export const uk: LanguageTranslation = {
|
||||
confirm: 'Змінити',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'Створити нову схему',
|
||||
description:
|
||||
'Поки що не існує жодної схеми. Створіть свою першу схему, щоб організувати ваші таблиці.',
|
||||
create: 'Створити',
|
||||
cancel: 'Скасувати',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'Допоможіть нам покращитися!',
|
||||
description: 'Поставне на зірку на GitHub? Це лише один клік!',
|
||||
@@ -452,6 +477,7 @@ export const uk: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'Нова таблиця',
|
||||
new_view: 'Нове представлення',
|
||||
new_relationship: 'Новий звʼязок',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -473,6 +499,9 @@ export const uk: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'Мова',
|
||||
},
|
||||
|
||||
on: 'Увімк',
|
||||
off: 'Вимк',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const vi: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: 'Mới',
|
||||
browse: 'Duyệt',
|
||||
tables: 'Bảng',
|
||||
refs: 'Refs',
|
||||
areas: 'Khu vực',
|
||||
dependencies: 'Phụ thuộc',
|
||||
custom_types: 'Kiểu tùy chỉnh',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: 'Tệp',
|
||||
new: 'Tạo mới',
|
||||
open: 'Mở',
|
||||
actions: {
|
||||
actions: 'Hành động',
|
||||
new: 'Mới...',
|
||||
browse: 'Duyệt...',
|
||||
save: 'Lưu',
|
||||
import: 'Nhập cơ sở dữ liệu',
|
||||
export_sql: 'Xuất SQL',
|
||||
export_as: 'Xuất thành',
|
||||
delete_diagram: 'Xóa sơ đồ',
|
||||
exit: 'Thoát',
|
||||
delete_diagram: 'Xóa',
|
||||
},
|
||||
edit: {
|
||||
edit: 'Sửa',
|
||||
@@ -26,7 +34,10 @@ export const vi: LanguageTranslation = {
|
||||
hide_sidebar: 'Ẩn thanh bên',
|
||||
hide_cardinality: 'Ẩn số lượng',
|
||||
show_cardinality: 'Hiển thị số lượng',
|
||||
show_field_attributes: 'Hiển thị thuộc tính trường',
|
||||
hide_field_attributes: 'Ẩn thuộc tính trường',
|
||||
zoom_on_scroll: 'Thu phóng khi cuộn',
|
||||
show_views: 'Chế độ xem Cơ sở dữ liệu',
|
||||
theme: 'Chủ đề',
|
||||
show_dependencies: 'Hiển thị các phụ thuộc',
|
||||
hide_dependencies: 'Ẩn các phụ thuộc',
|
||||
@@ -63,22 +74,13 @@ export const vi: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: 'Sắp xếp lại sơ đồ',
|
||||
title: 'Tự động sắp xếp sơ đồ',
|
||||
description:
|
||||
'Hành động này sẽ sắp xếp lại tất cả các bảng trong sơ đồ. Bạn có muốn tiếp tục không?',
|
||||
reorder: 'Sắp xếp',
|
||||
reorder: 'Tự động sắp xếp',
|
||||
cancel: 'Hủy',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: 'Có nhiều lược đồ',
|
||||
description:
|
||||
'Có {{schemasCount}} lược đồ trong sơ đồ này. Hiện đang hiển thị: {{formattedSchemas}}.',
|
||||
dont_show_again: 'Không hiển thị lại',
|
||||
change_schema: 'Thay đổi',
|
||||
none: 'không có',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: 'Sao chép thất bại',
|
||||
@@ -113,14 +115,11 @@ export const vi: LanguageTranslation = {
|
||||
copied: 'Đã sao chép!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Lược đồ:',
|
||||
filter_by_schema: 'Lọc bởi lược đồ',
|
||||
search_schema: 'Tìm kiếm lược đồ...',
|
||||
no_schemas_found: 'Không tìm thấy lược đồ.',
|
||||
view_all_options: 'Xem tất cả tùy chọn...',
|
||||
tables_section: {
|
||||
tables: 'Bảng',
|
||||
add_table: 'Thêm bảng',
|
||||
add_view: 'Thêm Chế độ xem',
|
||||
filter: 'Lọc',
|
||||
collapse: 'Thu gọn tất cả',
|
||||
// TODO: Translate
|
||||
@@ -146,16 +145,23 @@ export const vi: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: 'Thuộc tính trường',
|
||||
unique: 'Giá trị duy nhất',
|
||||
auto_increment: 'Tự động tăng',
|
||||
comments: 'Bình luận',
|
||||
no_comments: 'Không có bình luận',
|
||||
delete_field: 'Xóa trường',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: 'Độ chính xác',
|
||||
scale: 'Tỷ lệ',
|
||||
},
|
||||
index_actions: {
|
||||
title: 'Thuộc tính chỉ mục',
|
||||
name: 'Tên',
|
||||
unique: 'Giá trị duy nhất',
|
||||
index_type: 'Loại chỉ mục',
|
||||
delete_index: 'Xóa chỉ mục',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -172,12 +178,15 @@ export const vi: LanguageTranslation = {
|
||||
description: 'Tạo một bảng để bắt đầu',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: 'Quan hệ',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: 'Lọc',
|
||||
add_relationship: 'Thêm quan hệ',
|
||||
collapse: 'Thu gọn tất cả',
|
||||
add_relationship: 'Thêm quan hệ',
|
||||
relationships: 'Quan hệ',
|
||||
dependencies: 'Phụ thuộc',
|
||||
relationship: {
|
||||
relationship: 'Quan hệ',
|
||||
primary: 'Bảng khóa chính',
|
||||
foreign: 'Bảng khóa ngoại',
|
||||
cardinality: 'Quan hệ',
|
||||
@@ -187,16 +196,8 @@ export const vi: LanguageTranslation = {
|
||||
delete_relationship: 'Xóa',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Không có quan hệ',
|
||||
description: 'Tạo quan hệ để kết nối các bảng',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: 'Phụ thuộc',
|
||||
filter: 'Lọc',
|
||||
collapse: 'Thu gọn tất cả',
|
||||
dependency: {
|
||||
dependency: 'Phụ thuộc',
|
||||
table: 'Bảng',
|
||||
dependent_table: 'Bảng xem phụ thuộc',
|
||||
delete_dependency: 'Xóa',
|
||||
@@ -206,8 +207,8 @@ export const vi: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: 'Không có phụ thuộc',
|
||||
description: 'Tạo bảng xem phụ thuộc để bắt đầu',
|
||||
title: 'Không có quan hệ',
|
||||
description: 'Tạo một quan hệ để bắt đầu',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -247,12 +248,16 @@ export const vi: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: 'Không có giá trị enum được định nghĩa',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -266,8 +271,14 @@ export const vi: LanguageTranslation = {
|
||||
show_all: 'Hiển thị tất cả',
|
||||
undo: 'Hoàn tác',
|
||||
redo: 'Làm lại',
|
||||
reorder_diagram: 'Sắp xếp lại sơ đồ',
|
||||
reorder_diagram: 'Tự động sắp xếp sơ đồ',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: 'Làm nổi bật các bảng chồng chéo',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -304,7 +315,7 @@ export const vi: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: 'Mở sơ đồ',
|
||||
title: 'Mở cơ sở dữ liệu',
|
||||
description: 'Chọn sơ đồ để mở từ danh sách bên dưới.',
|
||||
table_columns: {
|
||||
name: 'Tên',
|
||||
@@ -314,6 +325,12 @@ export const vi: LanguageTranslation = {
|
||||
},
|
||||
cancel: 'Hủy',
|
||||
open: 'Mở',
|
||||
|
||||
diagram_actions: {
|
||||
open: 'Mở',
|
||||
duplicate: 'Nhân bản',
|
||||
delete: 'Xóa',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -399,6 +416,14 @@ export const vi: LanguageTranslation = {
|
||||
confirm: 'Xác nhận',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: 'Tạo lược đồ mới',
|
||||
description:
|
||||
'Chưa có lược đồ nào. Tạo lược đồ đầu tiên của bạn để tổ chức các bảng.',
|
||||
create: 'Tạo',
|
||||
cancel: 'Hủy',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: 'Hãy giúp chúng tôi cải thiện!',
|
||||
description:
|
||||
@@ -453,6 +478,7 @@ export const vi: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: 'Tạo bảng mới',
|
||||
new_view: 'Chế độ xem Mới',
|
||||
new_relationship: 'Tạo quan hệ mới',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -474,6 +500,9 @@ export const vi: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: 'Ngôn ngữ',
|
||||
},
|
||||
|
||||
on: 'Bật',
|
||||
off: 'Tắt',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const zh_CN: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: '新建',
|
||||
browse: '浏览',
|
||||
tables: '表',
|
||||
refs: '引用',
|
||||
areas: '区域',
|
||||
dependencies: '依赖关系',
|
||||
custom_types: '自定义类型',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: '文件',
|
||||
new: '新建',
|
||||
open: '打开',
|
||||
actions: {
|
||||
actions: '操作',
|
||||
new: '新建...',
|
||||
browse: '浏览...',
|
||||
save: '保存',
|
||||
import: '导入数据库',
|
||||
export_sql: '导出 SQL 语句',
|
||||
export_as: '导出为',
|
||||
delete_diagram: '删除关系图',
|
||||
exit: '退出',
|
||||
delete_diagram: '删除',
|
||||
},
|
||||
edit: {
|
||||
edit: '编辑',
|
||||
@@ -26,7 +34,10 @@ export const zh_CN: LanguageTranslation = {
|
||||
hide_sidebar: '隐藏侧边栏',
|
||||
hide_cardinality: '隐藏基数',
|
||||
show_cardinality: '展示基数',
|
||||
show_field_attributes: '展示字段属性',
|
||||
hide_field_attributes: '隐藏字段属性',
|
||||
zoom_on_scroll: '滚动缩放',
|
||||
show_views: '数据库视图',
|
||||
theme: '主题',
|
||||
show_dependencies: '展示依赖',
|
||||
hide_dependencies: '隐藏依赖',
|
||||
@@ -61,21 +72,12 @@ export const zh_CN: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: '重新排列关系图',
|
||||
title: '自动排列关系图',
|
||||
description: '此操作将重新排列关系图中的所有表。是否要继续?',
|
||||
reorder: '重新排列',
|
||||
reorder: '自动排列',
|
||||
cancel: '取消',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: '多个模式',
|
||||
description:
|
||||
'此关系图中有 {{schemasCount}} 个模式,当前显示:{{formattedSchemas}}。',
|
||||
dont_show_again: '不再展示',
|
||||
change_schema: '更改',
|
||||
none: '无',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: '复制失败',
|
||||
@@ -110,14 +112,11 @@ export const zh_CN: LanguageTranslation = {
|
||||
copied: '复制了!',
|
||||
|
||||
side_panel: {
|
||||
schema: '模式:',
|
||||
filter_by_schema: '按模式筛选',
|
||||
search_schema: '搜索模式...',
|
||||
no_schemas_found: '未找到模式。',
|
||||
view_all_options: '查看所有选项...',
|
||||
tables_section: {
|
||||
tables: '表',
|
||||
add_table: '添加表',
|
||||
add_view: '添加视图',
|
||||
filter: '筛选',
|
||||
collapse: '全部折叠',
|
||||
// TODO: Translate
|
||||
@@ -143,16 +142,23 @@ export const zh_CN: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: '字段属性',
|
||||
unique: '唯一',
|
||||
auto_increment: '自动递增',
|
||||
comments: '注释',
|
||||
no_comments: '空',
|
||||
delete_field: '删除字段',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: '精度',
|
||||
scale: '小数位',
|
||||
},
|
||||
index_actions: {
|
||||
title: '索引属性',
|
||||
name: '名称',
|
||||
unique: '唯一',
|
||||
index_type: '索引类型',
|
||||
delete_index: '删除索引',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -169,12 +175,15 @@ export const zh_CN: LanguageTranslation = {
|
||||
description: '新建表以开始',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: '关系',
|
||||
refs_section: {
|
||||
refs: '引用',
|
||||
filter: '筛选',
|
||||
add_relationship: '添加关系',
|
||||
collapse: '全部折叠',
|
||||
add_relationship: '添加关系',
|
||||
relationships: '关系',
|
||||
dependencies: '依赖关系',
|
||||
relationship: {
|
||||
relationship: '关系',
|
||||
primary: '主表',
|
||||
foreign: '被引用表',
|
||||
cardinality: '基数',
|
||||
@@ -184,16 +193,8 @@ export const zh_CN: LanguageTranslation = {
|
||||
delete_relationship: '删除',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: '无关系',
|
||||
description: '创建关系以连接表',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: '依赖关系',
|
||||
filter: '筛选',
|
||||
collapse: '全部折叠',
|
||||
dependency: {
|
||||
dependency: '依赖',
|
||||
table: '表',
|
||||
dependent_table: '依赖视图',
|
||||
delete_dependency: '删除',
|
||||
@@ -203,8 +204,8 @@ export const zh_CN: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: '无依赖',
|
||||
description: '创建视图以开始',
|
||||
title: '无关系',
|
||||
description: '创建关系以开始',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -244,12 +245,16 @@ export const zh_CN: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: '没有定义枚举值',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -263,8 +268,14 @@ export const zh_CN: LanguageTranslation = {
|
||||
show_all: '展示全部',
|
||||
undo: '撤销',
|
||||
redo: '重做',
|
||||
reorder_diagram: '重新排列关系图',
|
||||
reorder_diagram: '自动排列关系图',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: '突出显示重叠的表',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -301,7 +312,7 @@ export const zh_CN: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: '打开关系图',
|
||||
title: '打开数据库',
|
||||
description: '从下面的列表中选择一个图表打开。',
|
||||
table_columns: {
|
||||
name: '名称',
|
||||
@@ -311,6 +322,12 @@ export const zh_CN: LanguageTranslation = {
|
||||
},
|
||||
cancel: '取消',
|
||||
open: '打开',
|
||||
|
||||
diagram_actions: {
|
||||
open: '打开',
|
||||
duplicate: '复制',
|
||||
delete: '删除',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -395,6 +412,13 @@ export const zh_CN: LanguageTranslation = {
|
||||
confirm: '更改',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: '创建新模式',
|
||||
description: '尚未存在任何模式。创建您的第一个模式来组织您的表。',
|
||||
create: '创建',
|
||||
cancel: '取消',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: '帮助我们改进!',
|
||||
description: '您想在 GitHub 上为我们加注星标吗?只需点击一下即可!',
|
||||
@@ -449,6 +473,7 @@ export const zh_CN: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: '新建表',
|
||||
new_view: '新建视图',
|
||||
new_relationship: '新建关系',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -470,6 +495,9 @@ export const zh_CN: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: '语言',
|
||||
},
|
||||
|
||||
on: '开启',
|
||||
off: '关闭',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
||||
|
||||
export const zh_TW: LanguageTranslation = {
|
||||
translation: {
|
||||
editor_sidebar: {
|
||||
new_diagram: '新建',
|
||||
browse: '瀏覽',
|
||||
tables: '表格',
|
||||
refs: 'Refs',
|
||||
areas: '區域',
|
||||
dependencies: '相依性',
|
||||
custom_types: '自定義類型',
|
||||
},
|
||||
menu: {
|
||||
file: {
|
||||
file: '檔案',
|
||||
new: '新增',
|
||||
open: '開啟',
|
||||
actions: {
|
||||
actions: '操作',
|
||||
new: '新增...',
|
||||
browse: '瀏覽...',
|
||||
save: '儲存',
|
||||
import: '匯入資料庫',
|
||||
export_sql: '匯出 SQL',
|
||||
export_as: '匯出為特定格式',
|
||||
delete_diagram: '刪除圖表',
|
||||
exit: '退出',
|
||||
delete_diagram: '刪除',
|
||||
},
|
||||
edit: {
|
||||
edit: '編輯',
|
||||
@@ -26,7 +34,10 @@ export const zh_TW: LanguageTranslation = {
|
||||
hide_sidebar: '隱藏側邊欄',
|
||||
hide_cardinality: '隱藏基數',
|
||||
show_cardinality: '顯示基數',
|
||||
hide_field_attributes: '隱藏欄位屬性',
|
||||
show_field_attributes: '顯示欄位屬性',
|
||||
zoom_on_scroll: '滾動縮放',
|
||||
show_views: '資料庫檢視',
|
||||
theme: '主題',
|
||||
show_dependencies: '顯示相依性',
|
||||
hide_dependencies: '隱藏相依性',
|
||||
@@ -61,21 +72,12 @@ export const zh_TW: LanguageTranslation = {
|
||||
},
|
||||
|
||||
reorder_diagram_alert: {
|
||||
title: '重新排列圖表',
|
||||
title: '自動排列圖表',
|
||||
description: '此操作將重新排列圖表中的所有表格。是否繼續?',
|
||||
reorder: '重新排列',
|
||||
reorder: '自動排列',
|
||||
cancel: '取消',
|
||||
},
|
||||
|
||||
multiple_schemas_alert: {
|
||||
title: '多重 Schema',
|
||||
description:
|
||||
'此圖表中包含 {{schemasCount}} 個 Schema,目前顯示:{{formattedSchemas}}。',
|
||||
dont_show_again: '不再顯示',
|
||||
change_schema: '變更',
|
||||
none: '無',
|
||||
},
|
||||
|
||||
copy_to_clipboard_toast: {
|
||||
unsupported: {
|
||||
title: '複製失敗',
|
||||
@@ -110,14 +112,11 @@ export const zh_TW: LanguageTranslation = {
|
||||
copied: '已複製!',
|
||||
|
||||
side_panel: {
|
||||
schema: 'Schema:',
|
||||
filter_by_schema: '依 Schema 篩選',
|
||||
search_schema: '搜尋 Schema...',
|
||||
no_schemas_found: '未找到 Schema。',
|
||||
view_all_options: '顯示所有選項...',
|
||||
tables_section: {
|
||||
tables: '表格',
|
||||
add_table: '新增表格',
|
||||
add_view: '新增檢視',
|
||||
filter: '篩選',
|
||||
collapse: '全部摺疊',
|
||||
// TODO: Translate
|
||||
@@ -143,16 +142,23 @@ export const zh_TW: LanguageTranslation = {
|
||||
field_actions: {
|
||||
title: '欄位屬性',
|
||||
unique: '唯一',
|
||||
auto_increment: '自動遞增',
|
||||
comments: '註解',
|
||||
no_comments: '無註解',
|
||||
delete_field: '刪除欄位',
|
||||
// TODO: Translate
|
||||
default_value: 'Default Value',
|
||||
no_default: 'No default',
|
||||
// TODO: Translate
|
||||
character_length: 'Max Length',
|
||||
precision: '精度',
|
||||
scale: '小數位',
|
||||
},
|
||||
index_actions: {
|
||||
title: '索引屬性',
|
||||
name: '名稱',
|
||||
unique: '唯一',
|
||||
index_type: '索引類型',
|
||||
delete_index: '刪除索引',
|
||||
},
|
||||
table_actions: {
|
||||
@@ -169,12 +175,15 @@ export const zh_TW: LanguageTranslation = {
|
||||
description: '請新增表格以開始',
|
||||
},
|
||||
},
|
||||
relationships_section: {
|
||||
relationships: '關聯',
|
||||
refs_section: {
|
||||
refs: 'Refs',
|
||||
filter: '篩選',
|
||||
add_relationship: '新增關聯',
|
||||
collapse: '全部摺疊',
|
||||
add_relationship: '新增關聯',
|
||||
relationships: '關聯',
|
||||
dependencies: '相依性',
|
||||
relationship: {
|
||||
relationship: '關聯',
|
||||
primary: '主表格',
|
||||
foreign: '參照表格',
|
||||
cardinality: '基數',
|
||||
@@ -184,16 +193,8 @@ export const zh_TW: LanguageTranslation = {
|
||||
delete_relationship: '刪除',
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: '尚無關聯',
|
||||
description: '請新增關聯以連接表格',
|
||||
},
|
||||
},
|
||||
dependencies_section: {
|
||||
dependencies: '相依性',
|
||||
filter: '篩選',
|
||||
collapse: '全部摺疊',
|
||||
dependency: {
|
||||
dependency: '相依性',
|
||||
table: '表格',
|
||||
dependent_table: '相依檢視',
|
||||
delete_dependency: '刪除',
|
||||
@@ -203,8 +204,8 @@ export const zh_TW: LanguageTranslation = {
|
||||
},
|
||||
},
|
||||
empty_state: {
|
||||
title: '尚無相依性',
|
||||
description: '請建立檢視以開始',
|
||||
title: '尚無關聯',
|
||||
description: '請建立關聯以開始',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -244,12 +245,16 @@ export const zh_TW: LanguageTranslation = {
|
||||
enum_values: 'Enum Values',
|
||||
composite_fields: 'Fields',
|
||||
no_fields: 'No fields defined',
|
||||
no_values: '沒有定義列舉值',
|
||||
field_name_placeholder: 'Field name',
|
||||
field_type_placeholder: 'Select type',
|
||||
add_field: 'Add Field',
|
||||
no_fields_tooltip: 'No fields defined for this custom type',
|
||||
custom_type_actions: {
|
||||
title: 'Actions',
|
||||
highlight_fields: 'Highlight Fields',
|
||||
delete_custom_type: 'Delete',
|
||||
clear_field_highlight: 'Clear Highlight',
|
||||
},
|
||||
delete_custom_type: 'Delete Type',
|
||||
},
|
||||
@@ -263,8 +268,14 @@ export const zh_TW: LanguageTranslation = {
|
||||
show_all: '顯示全部',
|
||||
undo: '復原',
|
||||
redo: '重做',
|
||||
reorder_diagram: '重新排列圖表',
|
||||
reorder_diagram: '自動排列圖表',
|
||||
// TODO: Translate
|
||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||
custom_type_highlight_tooltip:
|
||||
'Highlighting "{{typeName}}" - Click to clear',
|
||||
highlight_overlapping_tables: '突出顯示重疊表格',
|
||||
// TODO: Translate
|
||||
filter: 'Filter Tables',
|
||||
},
|
||||
|
||||
new_diagram_dialog: {
|
||||
@@ -300,7 +311,7 @@ export const zh_TW: LanguageTranslation = {
|
||||
},
|
||||
|
||||
open_diagram_dialog: {
|
||||
title: '開啟圖表',
|
||||
title: '開啟資料庫',
|
||||
description: '請從以下列表中選擇一個圖表。',
|
||||
table_columns: {
|
||||
name: '名稱',
|
||||
@@ -310,6 +321,12 @@ export const zh_TW: LanguageTranslation = {
|
||||
},
|
||||
cancel: '取消',
|
||||
open: '開啟',
|
||||
|
||||
diagram_actions: {
|
||||
open: '開啟',
|
||||
duplicate: '複製',
|
||||
delete: '刪除',
|
||||
},
|
||||
},
|
||||
|
||||
export_sql_dialog: {
|
||||
@@ -394,6 +411,14 @@ export const zh_TW: LanguageTranslation = {
|
||||
confirm: '變更',
|
||||
},
|
||||
|
||||
create_table_schema_dialog: {
|
||||
title: '建立新 Schema',
|
||||
description:
|
||||
'尚未存在任何 Schema。建立您的第一個 Schema 來組織您的表格。',
|
||||
create: '建立',
|
||||
cancel: '取消',
|
||||
},
|
||||
|
||||
star_us_dialog: {
|
||||
title: '協助我們改善!',
|
||||
description: '請在 GitHub 上給我們一顆星,只需點擊一下!',
|
||||
@@ -448,6 +473,7 @@ export const zh_TW: LanguageTranslation = {
|
||||
|
||||
canvas_context_menu: {
|
||||
new_table: '新建表格',
|
||||
new_view: '新檢視',
|
||||
new_relationship: '新建關聯',
|
||||
// TODO: Translate
|
||||
new_area: 'New Area',
|
||||
@@ -469,6 +495,9 @@ export const zh_TW: LanguageTranslation = {
|
||||
language_select: {
|
||||
change_language: '變更語言',
|
||||
},
|
||||
|
||||
on: '開啟',
|
||||
off: '關閉',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { DBCustomType } from './domain';
|
||||
import type { Area } from './domain/area';
|
||||
import type { DBDependency } from './domain/db-dependency';
|
||||
import type { DBField } from './domain/db-field';
|
||||
@@ -48,6 +49,10 @@ const generateIdsMapFromDiagram = (
|
||||
idsMap.set(area.id, generateId());
|
||||
});
|
||||
|
||||
diagram.customTypes?.forEach((customType) => {
|
||||
idsMap.set(customType.id, generateId());
|
||||
});
|
||||
|
||||
return idsMap;
|
||||
};
|
||||
|
||||
@@ -124,7 +129,7 @@ export const cloneDiagram = (
|
||||
} = {
|
||||
generateId: defaultGenerateId,
|
||||
}
|
||||
): Diagram => {
|
||||
): { diagram: Diagram; idsMap: Map<string, string> } => {
|
||||
const { generateId } = options;
|
||||
const diagramId = generateId();
|
||||
|
||||
@@ -213,14 +218,38 @@ export const cloneDiagram = (
|
||||
})
|
||||
.filter((area): area is Area => area !== null) ?? [];
|
||||
|
||||
const customTypes: DBCustomType[] =
|
||||
diagram.customTypes
|
||||
?.map((customType) => {
|
||||
const id = getNewId(customType.id);
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...customType,
|
||||
id,
|
||||
} satisfies DBCustomType;
|
||||
})
|
||||
.filter(
|
||||
(customType): customType is DBCustomType => customType !== null
|
||||
) ?? [];
|
||||
|
||||
return {
|
||||
...diagram,
|
||||
id: diagramId,
|
||||
dependencies,
|
||||
relationships,
|
||||
tables,
|
||||
areas,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
diagram: {
|
||||
...diagram,
|
||||
id: diagramId,
|
||||
dependencies,
|
||||
relationships,
|
||||
tables,
|
||||
areas,
|
||||
customTypes,
|
||||
createdAt: diagram.createdAt
|
||||
? new Date(diagram.createdAt)
|
||||
: new Date(),
|
||||
updatedAt: diagram.updatedAt
|
||||
? new Date(diagram.updatedAt)
|
||||
: new Date(),
|
||||
},
|
||||
idsMap,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -19,3 +19,5 @@ export const randomColor = () => {
|
||||
|
||||
export const viewColor = '#b0b0b0';
|
||||
export const materializedViewColor = '#7d7d7d';
|
||||
export const defaultTableColor = '#8eb7ff';
|
||||
export const defaultAreaColor = '#b067e9';
|
||||
|
||||
@@ -48,18 +48,30 @@ export const clickhouseDataTypes: readonly DataTypeData[] = [
|
||||
{ name: 'mediumblob', id: 'mediumblob' },
|
||||
{ name: 'tinyblob', id: 'tinyblob' },
|
||||
{ name: 'blob', id: 'blob' },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
|
||||
{ name: 'char large object', id: 'char_large_object' },
|
||||
{ name: 'char varying', id: 'char_varying', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'char varying',
|
||||
id: 'char_varying',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'character large object', id: 'character_large_object' },
|
||||
{
|
||||
name: 'character varying',
|
||||
id: 'character_varying',
|
||||
hasCharMaxLength: true,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'nchar large object', id: 'nchar_large_object' },
|
||||
{ name: 'nchar varying', id: 'nchar_varying', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'nchar varying',
|
||||
id: 'nchar_varying',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'national character large object',
|
||||
id: 'national_character_large_object',
|
||||
@@ -67,22 +79,34 @@ export const clickhouseDataTypes: readonly DataTypeData[] = [
|
||||
{
|
||||
name: 'national character varying',
|
||||
id: 'national_character_varying',
|
||||
hasCharMaxLength: true,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'national char varying',
|
||||
id: 'national_char_varying',
|
||||
hasCharMaxLength: true,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'national character',
|
||||
id: 'national_character',
|
||||
hasCharMaxLength: true,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'national char',
|
||||
id: 'national_char',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'national char', id: 'national_char', hasCharMaxLength: true },
|
||||
{ name: 'binary large object', id: 'binary_large_object' },
|
||||
{ name: 'binary varying', id: 'binary_varying', hasCharMaxLength: true },
|
||||
{ name: 'fixedstring', id: 'fixedstring', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'binary varying',
|
||||
id: 'binary_varying',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'fixedstring',
|
||||
id: 'fixedstring',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'string', id: 'string' },
|
||||
|
||||
// Date Types
|
||||
|
||||
@@ -14,9 +14,23 @@ export interface DataType {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface DataTypeData extends DataType {
|
||||
export interface FieldAttributeRange {
|
||||
max: number;
|
||||
min: number;
|
||||
default: number;
|
||||
}
|
||||
|
||||
interface FieldAttributes {
|
||||
hasCharMaxLength?: boolean;
|
||||
hasCharMaxLengthOption?: boolean;
|
||||
precision?: FieldAttributeRange;
|
||||
scale?: FieldAttributeRange;
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
export interface DataTypeData extends DataType {
|
||||
usageLevel?: 1 | 2; // Level 1 is most common, Level 2 is second most common
|
||||
fieldAttributes?: FieldAttributes;
|
||||
}
|
||||
|
||||
export const dataTypeSchema: z.ZodType<DataType> = z.object({
|
||||
@@ -132,3 +146,22 @@ export const findDataTypeDataById = (
|
||||
|
||||
return dataTypesOptions.find((dataType) => dataType.id === id);
|
||||
};
|
||||
|
||||
export const supportsAutoIncrementDataType = (
|
||||
dataTypeName: string
|
||||
): boolean => {
|
||||
return [
|
||||
'integer',
|
||||
'int',
|
||||
'bigint',
|
||||
'smallint',
|
||||
'tinyint',
|
||||
'mediumint',
|
||||
'serial',
|
||||
'bigserial',
|
||||
'smallserial',
|
||||
'number',
|
||||
'numeric',
|
||||
'decimal',
|
||||
].includes(dataTypeName.toLocaleLowerCase());
|
||||
};
|
||||
|
||||
@@ -2,7 +2,12 @@ import type { DataTypeData } from './data-types';
|
||||
|
||||
export const genericDataTypes: readonly DataTypeData[] = [
|
||||
// Level 1 - Most commonly used types
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
|
||||
{
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
usageLevel: 1,
|
||||
},
|
||||
{ name: 'int', id: 'int', usageLevel: 1 },
|
||||
{ name: 'text', id: 'text', usageLevel: 1 },
|
||||
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
|
||||
@@ -10,23 +15,62 @@ export const genericDataTypes: readonly DataTypeData[] = [
|
||||
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
|
||||
|
||||
// Level 2 - Second most common types
|
||||
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
|
||||
{
|
||||
name: 'decimal',
|
||||
id: 'decimal',
|
||||
usageLevel: 2,
|
||||
fieldAttributes: {
|
||||
precision: {
|
||||
max: 999,
|
||||
min: 1,
|
||||
default: 10,
|
||||
},
|
||||
scale: {
|
||||
max: 999,
|
||||
min: 0,
|
||||
default: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: 'datetime', id: 'datetime', usageLevel: 2 },
|
||||
{ name: 'json', id: 'json', usageLevel: 2 },
|
||||
{ name: 'uuid', id: 'uuid', usageLevel: 2 },
|
||||
|
||||
// Less common types
|
||||
{ name: 'bigint', id: 'bigint' },
|
||||
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'binary',
|
||||
id: 'binary',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'blob', id: 'blob' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
|
||||
{ name: 'double', id: 'double' },
|
||||
{ name: 'enum', id: 'enum' },
|
||||
{ name: 'float', id: 'float' },
|
||||
{ name: 'numeric', id: 'numeric' },
|
||||
{
|
||||
name: 'numeric',
|
||||
id: 'numeric',
|
||||
fieldAttributes: {
|
||||
precision: {
|
||||
max: 999,
|
||||
min: 1,
|
||||
default: 10,
|
||||
},
|
||||
scale: {
|
||||
max: 999,
|
||||
min: 0,
|
||||
default: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: 'real', id: 'real' },
|
||||
{ name: 'set', id: 'set' },
|
||||
{ name: 'smallint', id: 'smallint' },
|
||||
{ name: 'time', id: 'time' },
|
||||
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'varbinary',
|
||||
id: 'varbinary',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -4,12 +4,32 @@ export const mariadbDataTypes: readonly DataTypeData[] = [
|
||||
// Level 1 - Most commonly used types
|
||||
{ name: 'int', id: 'int', usageLevel: 1 },
|
||||
{ name: 'bigint', id: 'bigint', usageLevel: 1 },
|
||||
{ name: 'decimal', id: 'decimal', usageLevel: 1 },
|
||||
{
|
||||
name: 'decimal',
|
||||
id: 'decimal',
|
||||
usageLevel: 1,
|
||||
fieldAttributes: {
|
||||
precision: {
|
||||
max: 65,
|
||||
min: 1,
|
||||
default: 10,
|
||||
},
|
||||
scale: {
|
||||
max: 30,
|
||||
min: 0,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
|
||||
{ name: 'datetime', id: 'datetime', usageLevel: 1 },
|
||||
{ name: 'date', id: 'date', usageLevel: 1 },
|
||||
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
|
||||
{
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'text', id: 'text', usageLevel: 1 },
|
||||
|
||||
// Level 2 - Second most common types
|
||||
@@ -20,16 +40,39 @@ export const mariadbDataTypes: readonly DataTypeData[] = [
|
||||
{ name: 'tinyint', id: 'tinyint' },
|
||||
{ name: 'smallint', id: 'smallint' },
|
||||
{ name: 'mediumint', id: 'mediumint' },
|
||||
{ name: 'numeric', id: 'numeric' },
|
||||
{
|
||||
name: 'numeric',
|
||||
id: 'numeric',
|
||||
fieldAttributes: {
|
||||
precision: {
|
||||
max: 65,
|
||||
min: 1,
|
||||
default: 10,
|
||||
},
|
||||
scale: {
|
||||
max: 30,
|
||||
min: 0,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: 'float', id: 'float' },
|
||||
{ name: 'double', id: 'double' },
|
||||
{ name: 'bit', id: 'bit' },
|
||||
{ name: 'bool', id: 'bool' },
|
||||
{ name: 'time', id: 'time' },
|
||||
{ name: 'year', id: 'year' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
|
||||
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
|
||||
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
|
||||
{
|
||||
name: 'binary',
|
||||
id: 'binary',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'varbinary',
|
||||
id: 'varbinary',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'tinyblob', id: 'tinyblob' },
|
||||
{ name: 'blob', id: 'blob' },
|
||||
{ name: 'mediumblob', id: 'mediumblob' },
|
||||
|
||||
@@ -3,7 +3,12 @@ import type { DataTypeData } from './data-types';
|
||||
export const mysqlDataTypes: readonly DataTypeData[] = [
|
||||
// Level 1 - Most commonly used types
|
||||
{ name: 'int', id: 'int', usageLevel: 1 },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
|
||||
{
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
usageLevel: 1,
|
||||
},
|
||||
{ name: 'text', id: 'text', usageLevel: 1 },
|
||||
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
|
||||
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
|
||||
@@ -11,7 +16,23 @@ export const mysqlDataTypes: readonly DataTypeData[] = [
|
||||
|
||||
// Level 2 - Second most common types
|
||||
{ name: 'bigint', id: 'bigint', usageLevel: 2 },
|
||||
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
|
||||
{
|
||||
name: 'decimal',
|
||||
id: 'decimal',
|
||||
usageLevel: 2,
|
||||
fieldAttributes: {
|
||||
precision: {
|
||||
max: 65,
|
||||
min: 1,
|
||||
default: 10,
|
||||
},
|
||||
scale: {
|
||||
max: 30,
|
||||
min: 0,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: 'datetime', id: 'datetime', usageLevel: 2 },
|
||||
{ name: 'json', id: 'json', usageLevel: 2 },
|
||||
|
||||
@@ -22,7 +43,7 @@ export const mysqlDataTypes: readonly DataTypeData[] = [
|
||||
{ name: 'float', id: 'float' },
|
||||
{ name: 'double', id: 'double' },
|
||||
{ name: 'bit', id: 'bit' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
|
||||
{ name: 'tinytext', id: 'tinytext' },
|
||||
{ name: 'mediumtext', id: 'mediumtext' },
|
||||
{ name: 'longtext', id: 'longtext' },
|
||||
|
||||
@@ -2,15 +2,30 @@ import type { DataTypeData } from './data-types';
|
||||
|
||||
export const oracleDataTypes: readonly DataTypeData[] = [
|
||||
// Character types
|
||||
{ name: 'VARCHAR2', id: 'varchar2', usageLevel: 1, hasCharMaxLength: true },
|
||||
{
|
||||
name: 'VARCHAR2',
|
||||
id: 'varchar2',
|
||||
usageLevel: 1,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'NVARCHAR2',
|
||||
id: 'nvarchar2',
|
||||
usageLevel: 1,
|
||||
hasCharMaxLength: true,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'CHAR',
|
||||
id: 'char',
|
||||
usageLevel: 2,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'NCHAR',
|
||||
id: 'nchar',
|
||||
usageLevel: 2,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'CHAR', id: 'char', usageLevel: 2, hasCharMaxLength: true },
|
||||
{ name: 'NCHAR', id: 'nchar', usageLevel: 2, hasCharMaxLength: true },
|
||||
{ name: 'CLOB', id: 'clob', usageLevel: 2 },
|
||||
{ name: 'NCLOB', id: 'nclob', usageLevel: 2 },
|
||||
|
||||
@@ -49,7 +64,12 @@ export const oracleDataTypes: readonly DataTypeData[] = [
|
||||
{ name: 'BFILE', id: 'bfile', usageLevel: 2 },
|
||||
|
||||
// Other types
|
||||
{ name: 'RAW', id: 'raw', usageLevel: 2, hasCharMaxLength: true },
|
||||
{
|
||||
name: 'RAW',
|
||||
id: 'raw',
|
||||
usageLevel: 2,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'LONG RAW', id: 'long_raw', usageLevel: 2 },
|
||||
{ name: 'ROWID', id: 'rowid', usageLevel: 2 },
|
||||
{ name: 'UROWID', id: 'urowid', usageLevel: 2 },
|
||||
|
||||
@@ -3,7 +3,12 @@ import type { DataTypeData } from './data-types';
|
||||
export const postgresDataTypes: readonly DataTypeData[] = [
|
||||
// Level 1 - Most commonly used types
|
||||
{ name: 'integer', id: 'integer', usageLevel: 1 },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
|
||||
{
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
usageLevel: 1,
|
||||
},
|
||||
{ name: 'text', id: 'text', usageLevel: 1 },
|
||||
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
|
||||
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
|
||||
@@ -11,7 +16,23 @@ export const postgresDataTypes: readonly DataTypeData[] = [
|
||||
|
||||
// Level 2 - Second most common types
|
||||
{ name: 'bigint', id: 'bigint', usageLevel: 2 },
|
||||
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
|
||||
{
|
||||
name: 'decimal',
|
||||
id: 'decimal',
|
||||
usageLevel: 2,
|
||||
fieldAttributes: {
|
||||
precision: {
|
||||
max: 131072,
|
||||
min: 0,
|
||||
default: 10,
|
||||
},
|
||||
scale: {
|
||||
max: 16383,
|
||||
min: 0,
|
||||
default: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: 'serial', id: 'serial', usageLevel: 2 },
|
||||
{ name: 'json', id: 'json', usageLevel: 2 },
|
||||
{ name: 'jsonb', id: 'jsonb', usageLevel: 2 },
|
||||
@@ -23,18 +44,33 @@ export const postgresDataTypes: readonly DataTypeData[] = [
|
||||
},
|
||||
|
||||
// Less common types
|
||||
{ name: 'numeric', id: 'numeric' },
|
||||
{
|
||||
name: 'numeric',
|
||||
id: 'numeric',
|
||||
fieldAttributes: {
|
||||
precision: {
|
||||
max: 131072,
|
||||
min: 0,
|
||||
default: 10,
|
||||
},
|
||||
scale: {
|
||||
max: 16383,
|
||||
min: 0,
|
||||
default: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: 'real', id: 'real' },
|
||||
{ name: 'double precision', id: 'double_precision' },
|
||||
{ name: 'smallserial', id: 'smallserial' },
|
||||
{ name: 'bigserial', id: 'bigserial' },
|
||||
{ name: 'money', id: 'money' },
|
||||
{ name: 'smallint', id: 'smallint' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
|
||||
{
|
||||
name: 'character varying',
|
||||
id: 'character_varying',
|
||||
hasCharMaxLength: true,
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{ name: 'time', id: 'time' },
|
||||
{ name: 'timestamp without time zone', id: 'timestamp_without_time_zone' },
|
||||
|
||||
@@ -4,32 +4,93 @@ export const sqlServerDataTypes: readonly DataTypeData[] = [
|
||||
// Level 1 - Most commonly used types
|
||||
{ name: 'int', id: 'int', usageLevel: 1 },
|
||||
{ name: 'bit', id: 'bit', usageLevel: 1 },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
|
||||
{ name: 'nvarchar', id: 'nvarchar', hasCharMaxLength: true, usageLevel: 1 },
|
||||
{
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
fieldAttributes: {
|
||||
hasCharMaxLength: true,
|
||||
hasCharMaxLengthOption: true,
|
||||
maxLength: 8000,
|
||||
},
|
||||
usageLevel: 1,
|
||||
},
|
||||
{
|
||||
name: 'nvarchar',
|
||||
id: 'nvarchar',
|
||||
fieldAttributes: {
|
||||
hasCharMaxLength: true,
|
||||
hasCharMaxLengthOption: true,
|
||||
maxLength: 4000,
|
||||
},
|
||||
usageLevel: 1,
|
||||
},
|
||||
{ name: 'text', id: 'text', usageLevel: 1 },
|
||||
{ name: 'datetime', id: 'datetime', usageLevel: 1 },
|
||||
{ name: 'date', id: 'date', usageLevel: 1 },
|
||||
|
||||
// Level 2 - Second most common types
|
||||
{ name: 'bigint', id: 'bigint', usageLevel: 2 },
|
||||
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
|
||||
{
|
||||
name: 'decimal',
|
||||
id: 'decimal',
|
||||
usageLevel: 2,
|
||||
fieldAttributes: {
|
||||
precision: {
|
||||
max: 38,
|
||||
min: 1,
|
||||
default: 18,
|
||||
},
|
||||
scale: {
|
||||
max: 38,
|
||||
min: 0,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: 'datetime2', id: 'datetime2', usageLevel: 2 },
|
||||
{ name: 'uniqueidentifier', id: 'uniqueidentifier', usageLevel: 2 },
|
||||
{ name: 'json', id: 'json', usageLevel: 2 },
|
||||
|
||||
// Less common types
|
||||
{ name: 'numeric', id: 'numeric' },
|
||||
{
|
||||
name: 'numeric',
|
||||
id: 'numeric',
|
||||
fieldAttributes: {
|
||||
precision: {
|
||||
max: 38,
|
||||
min: 1,
|
||||
default: 18,
|
||||
},
|
||||
scale: {
|
||||
max: 38,
|
||||
min: 0,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: 'smallint', id: 'smallint' },
|
||||
{ name: 'smallmoney', id: 'smallmoney' },
|
||||
{ name: 'tinyint', id: 'tinyint' },
|
||||
{ name: 'money', id: 'money' },
|
||||
{ name: 'float', id: 'float' },
|
||||
{ name: 'real', id: 'real' },
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{ name: 'nchar', id: 'nchar', hasCharMaxLength: true },
|
||||
{ name: 'char', id: 'char', fieldAttributes: { hasCharMaxLength: true } },
|
||||
{ name: 'nchar', id: 'nchar', fieldAttributes: { hasCharMaxLength: true } },
|
||||
{ name: 'ntext', id: 'ntext' },
|
||||
{ name: 'binary', id: 'binary', hasCharMaxLength: true },
|
||||
{ name: 'varbinary', id: 'varbinary', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'binary',
|
||||
id: 'binary',
|
||||
fieldAttributes: { hasCharMaxLength: true },
|
||||
},
|
||||
{
|
||||
name: 'varbinary',
|
||||
id: 'varbinary',
|
||||
fieldAttributes: {
|
||||
hasCharMaxLength: true,
|
||||
hasCharMaxLengthOption: true,
|
||||
maxLength: 8000,
|
||||
},
|
||||
},
|
||||
{ name: 'image', id: 'image' },
|
||||
{ name: 'datetimeoffset', id: 'datetimeoffset' },
|
||||
{ name: 'smalldatetime', id: 'smalldatetime' },
|
||||
|
||||
@@ -10,25 +10,48 @@ export const sqliteDataTypes: readonly DataTypeData[] = [
|
||||
|
||||
// SQLite type aliases and common types
|
||||
{ name: 'int', id: 'int', usageLevel: 1 },
|
||||
{ name: 'varchar', id: 'varchar', hasCharMaxLength: true, usageLevel: 1 },
|
||||
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
|
||||
{ name: 'date', id: 'date', usageLevel: 1 },
|
||||
{ name: 'datetime', id: 'datetime', usageLevel: 1 },
|
||||
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
|
||||
{
|
||||
name: 'varchar',
|
||||
id: 'varchar',
|
||||
fieldAttributes: {
|
||||
hasCharMaxLength: true,
|
||||
},
|
||||
usageLevel: 1,
|
||||
},
|
||||
{
|
||||
name: 'timestamp',
|
||||
id: 'timestamp',
|
||||
usageLevel: 1,
|
||||
},
|
||||
|
||||
// Level 2 - Second most common types
|
||||
{ name: 'numeric', id: 'numeric', usageLevel: 2 },
|
||||
{ name: 'decimal', id: 'decimal', usageLevel: 2 },
|
||||
{ name: 'float', id: 'float', usageLevel: 2 },
|
||||
{
|
||||
name: 'decimal',
|
||||
id: 'decimal',
|
||||
usageLevel: 2,
|
||||
},
|
||||
{ name: 'double', id: 'double', usageLevel: 2 },
|
||||
{ name: 'json', id: 'json', usageLevel: 2 },
|
||||
|
||||
// Less common types (all map to SQLite storage classes)
|
||||
{ name: 'char', id: 'char', hasCharMaxLength: true },
|
||||
{
|
||||
name: 'char',
|
||||
id: 'char',
|
||||
fieldAttributes: {
|
||||
hasCharMaxLength: true,
|
||||
},
|
||||
usageLevel: 2,
|
||||
},
|
||||
{ name: 'binary', id: 'binary' },
|
||||
{ name: 'varbinary', id: 'varbinary' },
|
||||
{ name: 'smallint', id: 'smallint' },
|
||||
{ name: 'bigint', id: 'bigint' },
|
||||
{ name: 'bool', id: 'bool' },
|
||||
{ name: 'boolean', id: 'boolean' }, // Added for smartquery compatibility
|
||||
{ name: 'time', id: 'time' },
|
||||
{ name: 'date', id: 'date' }, // Added for smartquery compatibility
|
||||
{ name: 'datetime', id: 'datetime' }, // Added for smartquery compatibility
|
||||
] as const;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user