mirror of
https://github.com/chartdb/chartdb.git
synced 2025-11-04 22:13:15 +00:00
Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9baecea4ab | ||
|
|
c8b827764c | ||
|
|
0afa71efcc | ||
|
|
69d4e8dca6 | ||
|
|
a4674a2bf8 | ||
|
|
07dc4eace0 | ||
|
|
4fd940afbb | ||
|
|
3d85bcc6ab | ||
|
|
973b7663b1 | ||
|
|
6d38ebe3ec | ||
|
|
68412f90a7 | ||
|
|
084a1d505c | ||
|
|
91e713c30a | ||
|
|
acf6d4b365 | ||
|
|
9e8979d062 | ||
|
|
9ed27cf30c | ||
|
|
2c4b344efb | ||
|
|
ccb29e0a57 | ||
|
|
7d811de097 | ||
|
|
62dec48572 | ||
|
|
49328d8fbd | ||
|
|
459698b5d0 | ||
|
|
7ad0e7712d | ||
|
|
34475add32 | ||
|
|
38fedcec0c | ||
|
|
498655e7b7 | ||
|
|
bcd8aa9378 | ||
|
|
b15bc945ac | ||
|
|
c3c646bf7c | ||
|
|
57b3b8777f | ||
|
|
bb033091b1 | ||
|
|
c9ac8929c5 | ||
|
|
c567c0a5f3 | ||
|
|
2dc1a6fc75 | ||
|
|
98f6edd5c8 | ||
|
|
47a7a73a13 | ||
|
|
d71b46e8b5 | ||
|
|
e4c4a3b354 | ||
|
|
1b8d51b73c | ||
|
|
93d72a896b | ||
|
|
9991077978 | ||
|
|
bc82f9d6a8 | ||
|
|
26dc299cd2 | ||
|
|
d6ba4a4074 | ||
|
|
d09379e8be | ||
|
|
bdc41c0b74 | ||
|
|
d3dbf41894 | ||
|
|
e6783a89cc | ||
|
|
af3638da7a | ||
|
|
8954d893bb | ||
|
|
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 |
2
.github/workflows/cla.yaml
vendored
2
.github/workflows/cla.yaml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
actions: write
|
actions: write
|
||||||
contents: write # this can be 'read' if the signatures are in remote repository
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
statuses: write
|
statuses: write
|
||||||
|
|
||||||
|
|||||||
117
CHANGELOG.md
117
CHANGELOG.md
@@ -1,5 +1,122 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.17.0](https://github.com/chartdb/chartdb/compare/v1.16.0...v1.17.0) (2025-10-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* create relationships on canvas modal ([#946](https://github.com/chartdb/chartdb/issues/946)) ([34475ad](https://github.com/chartdb/chartdb/commit/34475add32f11323589ef092ccf2a8e9152ff272))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add auto-increment field detection in smart-query import ([#935](https://github.com/chartdb/chartdb/issues/935)) ([57b3b87](https://github.com/chartdb/chartdb/commit/57b3b8777fd0a445abf0ba6603faab612d469d5c))
|
||||||
|
* add open table in editor from canvas edit ([#952](https://github.com/chartdb/chartdb/issues/952)) ([7d811de](https://github.com/chartdb/chartdb/commit/7d811de097eb11e51012772fa6bf586fd0b16c62))
|
||||||
|
* add rels export dbml ([#937](https://github.com/chartdb/chartdb/issues/937)) ([c3c646b](https://github.com/chartdb/chartdb/commit/c3c646bf7cbb1328f4b2eb85c9a7e929f0fcd3b9))
|
||||||
|
* add support for arrays ([#949](https://github.com/chartdb/chartdb/issues/949)) ([49328d8](https://github.com/chartdb/chartdb/commit/49328d8fbd7786f6c0c04cd5605d43a24cbf10ea))
|
||||||
|
* add support for parsing default values in DBML ([#948](https://github.com/chartdb/chartdb/issues/948)) ([459698b](https://github.com/chartdb/chartdb/commit/459698b5d0a1ff23a3719c2e55e4ab2e2384c4fe))
|
||||||
|
* add timestampz and int as datatypes to postgres ([#940](https://github.com/chartdb/chartdb/issues/940)) ([b15bc94](https://github.com/chartdb/chartdb/commit/b15bc945acb96d7cb3832b3b1b607dfcaef9e5ca))
|
||||||
|
* auto-enter edit mode when creating new tables from canvas ([#943](https://github.com/chartdb/chartdb/issues/943)) ([bcd8aa9](https://github.com/chartdb/chartdb/commit/bcd8aa9378aa563f40a2b6802cc503be4c882356))
|
||||||
|
* dbml diff fields types preview ([#934](https://github.com/chartdb/chartdb/issues/934)) ([bb03309](https://github.com/chartdb/chartdb/commit/bb033091b1f64b888822be1423a80f16f5314f6b))
|
||||||
|
* exit table edit on area click ([#945](https://github.com/chartdb/chartdb/issues/945)) ([38fedce](https://github.com/chartdb/chartdb/commit/38fedcec0c10ea2b3f0b7fc92ca1f5ac9e540389))
|
||||||
|
* import array fields ([#961](https://github.com/chartdb/chartdb/issues/961)) ([91e713c](https://github.com/chartdb/chartdb/commit/91e713c30a44f1ba7a767ca7816079610136fcb8))
|
||||||
|
* manipulate schema directly from the canvas ([#947](https://github.com/chartdb/chartdb/issues/947)) ([7ad0e77](https://github.com/chartdb/chartdb/commit/7ad0e7712de975a23b2a337dc0a4a7fb4b122bd1))
|
||||||
|
* preserve multi-word types in DBML export/import ([#956](https://github.com/chartdb/chartdb/issues/956)) ([9ed27cf](https://github.com/chartdb/chartdb/commit/9ed27cf30cca1312713e80e525138f0c27154936))
|
||||||
|
* prevent text input glitch when editing table field names ([#944](https://github.com/chartdb/chartdb/issues/944)) ([498655e](https://github.com/chartdb/chartdb/commit/498655e7b77e57eaf641ba86263ce1ef60b93e16))
|
||||||
|
* resolve canvas filter tree state issues ([#953](https://github.com/chartdb/chartdb/issues/953)) ([ccb29e0](https://github.com/chartdb/chartdb/commit/ccb29e0a574dfa4cfdf0ebf242a4c4aaa48cc37b))
|
||||||
|
* resolve dbml increment & nullable attributes issue ([#954](https://github.com/chartdb/chartdb/issues/954)) ([2c4b344](https://github.com/chartdb/chartdb/commit/2c4b344efb24041e7f607fc6124e109b69aaa457))
|
||||||
|
* show SQL Script option conditionally for databases without DDL support ([#960](https://github.com/chartdb/chartdb/issues/960)) ([acf6d4b](https://github.com/chartdb/chartdb/commit/acf6d4b3654d8868b8a8ebf717c608d9749b71da))
|
||||||
|
* use flag for custom types ([#951](https://github.com/chartdb/chartdb/issues/951)) ([62dec48](https://github.com/chartdb/chartdb/commit/62dec4857211b705a8039691da1772263ea986fe))
|
||||||
|
|
||||||
|
## [1.16.0](https://github.com/chartdb/chartdb/compare/v1.15.1...v1.16.0) (2025-09-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add area context menu and UI improvements ([#918](https://github.com/chartdb/chartdb/issues/918)) ([d09379e](https://github.com/chartdb/chartdb/commit/d09379e8be0fa3c83ca77ff62ae815fe4db9869b))
|
||||||
|
* add quick table mode on canvas ([#915](https://github.com/chartdb/chartdb/issues/915)) ([8954d89](https://github.com/chartdb/chartdb/commit/8954d893bbfee45bb311380115fb14ebbf3a3133))
|
||||||
|
* add zoom navigation buttons to canvas filter for tables and areas ([#903](https://github.com/chartdb/chartdb/issues/903)) ([a0fb1ed](https://github.com/chartdb/chartdb/commit/a0fb1ed08ba18b66354fa3498d610097a83d4afc))
|
||||||
|
* **import-db:** add DBML syntax to import database dialog ([#768](https://github.com/chartdb/chartdb/issues/768)) ([af3638d](https://github.com/chartdb/chartdb/commit/af3638da7a9b70f281ceaddbc2f712a713d90cda))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add areas width and height + table width to diff check ([#931](https://github.com/chartdb/chartdb/issues/931)) ([98f6edd](https://github.com/chartdb/chartdb/commit/98f6edd5c8a8e9130e892b2d841744e0cf63a7bf))
|
||||||
|
* add diff x,y ([#928](https://github.com/chartdb/chartdb/issues/928)) ([e4c4a3b](https://github.com/chartdb/chartdb/commit/e4c4a3b35484d9ece955a5aec577603dde73d634))
|
||||||
|
* add support for ALTER TABLE ADD COLUMN in PostgreSQL importer ([#892](https://github.com/chartdb/chartdb/issues/892)) ([ec6e46f](https://github.com/chartdb/chartdb/commit/ec6e46fe81ea1806c179c50a4c5779d8596008aa))
|
||||||
|
* add tests for diff ([#930](https://github.com/chartdb/chartdb/issues/930)) ([47a7a73](https://github.com/chartdb/chartdb/commit/47a7a73a137b87dfa6e67aff5f939cf64ccf4601))
|
||||||
|
* dbml edit mode glitch ([#925](https://github.com/chartdb/chartdb/issues/925)) ([93d72a8](https://github.com/chartdb/chartdb/commit/93d72a896bab9aa79d8ea2f876126887e432214c))
|
||||||
|
* dbml export default time bug ([#922](https://github.com/chartdb/chartdb/issues/922)) ([bc82f9d](https://github.com/chartdb/chartdb/commit/bc82f9d6a8fe4de2f7e0fc465e0a20c5dbf8f41d))
|
||||||
|
* dbml export renaming fields bug ([#921](https://github.com/chartdb/chartdb/issues/921)) ([26dc299](https://github.com/chartdb/chartdb/commit/26dc299cd28e9890d191c13f84a15ac38ae48b11))
|
||||||
|
* **dbml:** export array fields without quotes ([#911](https://github.com/chartdb/chartdb/issues/911)) ([5e81c18](https://github.com/chartdb/chartdb/commit/5e81c1848aaa911990e1e881d62525f5254d6d34))
|
||||||
|
* diff logic ([#927](https://github.com/chartdb/chartdb/issues/927)) ([1b8d51b](https://github.com/chartdb/chartdb/commit/1b8d51b73c4ed4b7c5929adcb17a44927c7defca))
|
||||||
|
* export dbml issues after upgrade version ([#883](https://github.com/chartdb/chartdb/issues/883)) ([07937a2](https://github.com/chartdb/chartdb/commit/07937a2f51708b1c10b45c2bd1f9a9acf5c3f708))
|
||||||
|
* export sql + import metadata lib ([#902](https://github.com/chartdb/chartdb/issues/902)) ([ffddcdc](https://github.com/chartdb/chartdb/commit/ffddcdcc987bacb0e0d7e8dea27d08d3a8c5a8c8))
|
||||||
|
* handle bidirectional relationships in DBML export ([#924](https://github.com/chartdb/chartdb/issues/924)) ([9991077](https://github.com/chartdb/chartdb/commit/99910779789a9c6ef113d06bc3de31e35b9b04d1))
|
||||||
|
* import dbml set pk field unique ([#920](https://github.com/chartdb/chartdb/issues/920)) ([d6ba4a4](https://github.com/chartdb/chartdb/commit/d6ba4a40749d85d2703f120600df4345dab3c561))
|
||||||
|
* improve SQL default value parsing for PostgreSQL, MySQL, and SQL Server with proper type handling and casting support ([#900](https://github.com/chartdb/chartdb/issues/900)) ([fe9ef27](https://github.com/chartdb/chartdb/commit/fe9ef275b8619dcfd7e57541a62a6237a16d29a8))
|
||||||
|
* move area utils ([#932](https://github.com/chartdb/chartdb/issues/932)) ([2dc1a6f](https://github.com/chartdb/chartdb/commit/2dc1a6fc7519e0a455b0e1306601195deb156c96))
|
||||||
|
* move auto arrange to toolbar ([#904](https://github.com/chartdb/chartdb/issues/904)) ([b016a70](https://github.com/chartdb/chartdb/commit/b016a70691bc22af5720b4de683e8c9353994fcc))
|
||||||
|
* remove general db creation ([#901](https://github.com/chartdb/chartdb/issues/901)) ([df89f0b](https://github.com/chartdb/chartdb/commit/df89f0b6b9ba3fcc8b05bae4f60c0dc4ad1d2215))
|
||||||
|
* remove many to many rel option ([#933](https://github.com/chartdb/chartdb/issues/933)) ([c567c0a](https://github.com/chartdb/chartdb/commit/c567c0a5f39157b2c430e92192b6750304d7a834))
|
||||||
|
* reset increment and default when change field ([#896](https://github.com/chartdb/chartdb/issues/896)) ([e5e1d59](https://github.com/chartdb/chartdb/commit/e5e1d5932762422ea63acfd6cf9fe4f03aa822f7))
|
||||||
|
* **sql-import:** handle SQL Server DDL with multiple tables, inline foreign keys, and case-insensitive field matching ([#897](https://github.com/chartdb/chartdb/issues/897)) ([2a64dee](https://github.com/chartdb/chartdb/commit/2a64deebb87a11ee3892024c3273d682bb86f7ef))
|
||||||
|
* **sql-import:** support ALTER TABLE ALTER COLUMN TYPE in PostgreSQL importer ([#895](https://github.com/chartdb/chartdb/issues/895)) ([aa29061](https://github.com/chartdb/chartdb/commit/aa290615caf806d7d0374c848d50b4636fde7e96))
|
||||||
|
* **sqlite:** improve parser to handle tables without column types and fix column detection ([#914](https://github.com/chartdb/chartdb/issues/914)) ([d3dbf41](https://github.com/chartdb/chartdb/commit/d3dbf41894d74f0ffce9afe3bd810f065aa53017))
|
||||||
|
* trigger edit table on canvas from context menu ([#919](https://github.com/chartdb/chartdb/issues/919)) ([bdc41c0](https://github.com/chartdb/chartdb/commit/bdc41c0b74d9d9918e7b6cd2152fa07c0c58ce60))
|
||||||
|
* update deps vulns ([#909](https://github.com/chartdb/chartdb/issues/909)) ([2bd9ca2](https://github.com/chartdb/chartdb/commit/2bd9ca25b2c7b1f053ff4fdc8c5cfc1b0e65901d))
|
||||||
|
* upgrade dbml lib ([#880](https://github.com/chartdb/chartdb/issues/880)) ([d8e0bc7](https://github.com/chartdb/chartdb/commit/d8e0bc7db8881971ddaea7177bcebee13cc865f6))
|
||||||
|
|
||||||
|
## [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)
|
## [1.14.0](https://github.com/chartdb/chartdb/compare/v1.13.2...v1.14.0) (2025-08-04)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
14
index.html
14
index.html
@@ -4,8 +4,9 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<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>
|
<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.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link
|
<link
|
||||||
@@ -15,14 +16,19 @@
|
|||||||
<script src="/config.js"></script>
|
<script src="/config.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Load analytics only if not disabled
|
// Load analytics only if not disabled
|
||||||
(function() {
|
(function () {
|
||||||
const disableAnalytics = (window.env && window.env.DISABLE_ANALYTICS === 'true') ||
|
const disableAnalytics =
|
||||||
(typeof process !== 'undefined' && process.env && process.env.VITE_DISABLE_ANALYTICS === 'true');
|
(window.env && window.env.DISABLE_ANALYTICS === 'true') ||
|
||||||
|
(typeof process !== 'undefined' &&
|
||||||
|
process.env &&
|
||||||
|
process.env.VITE_DISABLE_ANALYTICS === 'true');
|
||||||
|
|
||||||
if (!disableAnalytics) {
|
if (!disableAnalytics) {
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = 'https://cdn.usefathom.com/script.js';
|
script.src = 'https://cdn.usefathom.com/script.js';
|
||||||
script.setAttribute('data-site', 'PRHIVBNN');
|
script.setAttribute('data-site', 'PRHIVBNN');
|
||||||
|
script.setAttribute('data-canonical', 'false');
|
||||||
|
script.setAttribute('data-spa', 'auto');
|
||||||
script.defer = true;
|
script.defer = true;
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
}
|
}
|
||||||
|
|||||||
2511
package-lock.json
generated
2511
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "chartdb",
|
"name": "chartdb",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.14.0",
|
"version": "1.17.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/openai": "^0.0.51",
|
"@ai-sdk/openai": "^0.0.51",
|
||||||
"@dbml/core": "^3.9.5",
|
"@dbml/core": "^3.13.9",
|
||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
"@monaco-editor/react": "^4.6.0",
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@radix-ui/react-accordion": "^1.2.0",
|
"@radix-ui/react-accordion": "^1.2.0",
|
||||||
@@ -26,22 +26,22 @@
|
|||||||
"@radix-ui/react-checkbox": "^1.1.1",
|
"@radix-ui/react-checkbox": "^1.1.1",
|
||||||
"@radix-ui/react-collapsible": "^1.1.0",
|
"@radix-ui/react-collapsible": "^1.1.0",
|
||||||
"@radix-ui/react-context-menu": "^2.2.1",
|
"@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-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-hover-card": "^1.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-label": "^2.1.0",
|
||||||
"@radix-ui/react-menubar": "^1.1.1",
|
"@radix-ui/react-menubar": "^1.1.1",
|
||||||
"@radix-ui/react-popover": "^1.1.1",
|
"@radix-ui/react-popover": "^1.1.1",
|
||||||
"@radix-ui/react-scroll-area": "1.2.0",
|
"@radix-ui/react-scroll-area": "1.2.0",
|
||||||
"@radix-ui/react-select": "^2.1.1",
|
"@radix-ui/react-select": "^2.1.1",
|
||||||
"@radix-ui/react-separator": "^1.1.2",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-tabs": "^1.1.0",
|
"@radix-ui/react-tabs": "^1.1.0",
|
||||||
"@radix-ui/react-toast": "^1.2.1",
|
"@radix-ui/react-toast": "^1.2.1",
|
||||||
"@radix-ui/react-toggle": "^1.1.0",
|
"@radix-ui/react-toggle": "^1.1.0",
|
||||||
"@radix-ui/react-toggle-group": "^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",
|
"@uidotdev/usehooks": "^2.4.1",
|
||||||
"@xyflow/react": "^12.8.2",
|
"@xyflow/react": "^12.8.2",
|
||||||
"ahooks": "^3.8.1",
|
"ahooks": "^3.8.1",
|
||||||
@@ -64,10 +64,13 @@
|
|||||||
"react-helmet-async": "^2.0.5",
|
"react-helmet-async": "^2.0.5",
|
||||||
"react-hotkeys-hook": "^4.5.0",
|
"react-hotkeys-hook": "^4.5.0",
|
||||||
"react-i18next": "^15.0.1",
|
"react-i18next": "^15.0.1",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
"react-resizable-panels": "^2.0.22",
|
"react-resizable-panels": "^2.0.22",
|
||||||
"react-responsive": "^10.0.0",
|
"react-responsive": "^10.0.0",
|
||||||
"react-router-dom": "^7.1.1",
|
"react-router-dom": "^7.1.1",
|
||||||
"react-use": "^17.5.1",
|
"react-use": "^17.5.1",
|
||||||
|
"rehype-raw": "^7.0.0",
|
||||||
|
"remark-gfm": "^4.0.1",
|
||||||
"tailwind-merge": "^2.4.0",
|
"tailwind-merge": "^2.4.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"timeago-react": "^3.0.6",
|
"timeago-react": "^3.0.6",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Allow: /
|
Disallow: /
|
||||||
|
|
||||||
Sitemap: https://app.chartdb.io/sitemap.xml
|
Sitemap: https://app.chartdb.io/sitemap.xml
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { cva } from 'class-variance-authority';
|
import { cva } from 'class-variance-authority';
|
||||||
|
|
||||||
export const buttonVariants = cva(
|
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: {
|
variants: {
|
||||||
variant: {
|
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 };
|
||||||
@@ -38,7 +38,7 @@ export interface CodeSnippetProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
code: string;
|
code: string;
|
||||||
codeToCopy?: string;
|
codeToCopy?: string;
|
||||||
language?: 'sql' | 'shell';
|
language?: 'sql' | 'shell' | 'dbml';
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
autoScroll?: boolean;
|
autoScroll?: boolean;
|
||||||
isComplete?: boolean;
|
isComplete?: boolean;
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ export const setupDBMLLanguage = (monaco: Monaco) => {
|
|||||||
base: 'vs-dark',
|
base: 'vs-dark',
|
||||||
inherit: true,
|
inherit: true,
|
||||||
rules: [
|
rules: [
|
||||||
|
{ token: 'comment', foreground: '6A9955' }, // Comments
|
||||||
{ token: 'keyword', foreground: '569CD6' }, // Table, Ref keywords
|
{ token: 'keyword', foreground: '569CD6' }, // Table, Ref keywords
|
||||||
{ token: 'string', foreground: 'CE9178' }, // Strings
|
{ token: 'string', foreground: 'CE9178' }, // Strings
|
||||||
{ token: 'annotation', foreground: '9CDCFE' }, // [annotations]
|
{ token: 'annotation', foreground: '9CDCFE' }, // [annotations]
|
||||||
{ token: 'delimiter', foreground: 'D4D4D4' }, // Braces {}
|
{ token: 'delimiter', foreground: 'D4D4D4' }, // Braces {}
|
||||||
{ token: 'operator', foreground: 'D4D4D4' }, // Operators
|
{ token: 'operator', foreground: 'D4D4D4' }, // Operators
|
||||||
{ token: 'datatype', foreground: '4EC9B0' }, // Data types
|
{ token: 'type', foreground: '4EC9B0' }, // Data types
|
||||||
|
{ token: 'identifier', foreground: '9CDCFE' }, // Field names
|
||||||
],
|
],
|
||||||
colors: {},
|
colors: {},
|
||||||
});
|
});
|
||||||
@@ -23,12 +25,14 @@ export const setupDBMLLanguage = (monaco: Monaco) => {
|
|||||||
base: 'vs',
|
base: 'vs',
|
||||||
inherit: true,
|
inherit: true,
|
||||||
rules: [
|
rules: [
|
||||||
|
{ token: 'comment', foreground: '008000' }, // Comments
|
||||||
{ token: 'keyword', foreground: '0000FF' }, // Table, Ref keywords
|
{ token: 'keyword', foreground: '0000FF' }, // Table, Ref keywords
|
||||||
{ token: 'string', foreground: 'A31515' }, // Strings
|
{ token: 'string', foreground: 'A31515' }, // Strings
|
||||||
{ token: 'annotation', foreground: '001080' }, // [annotations]
|
{ token: 'annotation', foreground: '001080' }, // [annotations]
|
||||||
{ token: 'delimiter', foreground: '000000' }, // Braces {}
|
{ token: 'delimiter', foreground: '000000' }, // Braces {}
|
||||||
{ token: 'operator', foreground: '000000' }, // Operators
|
{ token: 'operator', foreground: '000000' }, // Operators
|
||||||
{ token: 'type', foreground: '267F99' }, // Data types
|
{ token: 'type', foreground: '267F99' }, // Data types
|
||||||
|
{ token: 'identifier', foreground: '001080' }, // Field names
|
||||||
],
|
],
|
||||||
colors: {},
|
colors: {},
|
||||||
});
|
});
|
||||||
@@ -37,23 +41,59 @@ export const setupDBMLLanguage = (monaco: Monaco) => {
|
|||||||
const datatypePattern = dataTypesNames.join('|');
|
const datatypePattern = dataTypesNames.join('|');
|
||||||
|
|
||||||
monaco.languages.setMonarchTokensProvider('dbml', {
|
monaco.languages.setMonarchTokensProvider('dbml', {
|
||||||
keywords: ['Table', 'Ref', 'Indexes', 'Note', 'Enum'],
|
keywords: ['Table', 'Ref', 'Indexes', 'Note', 'Enum', 'enum'],
|
||||||
datatypes: dataTypesNames,
|
datatypes: dataTypesNames,
|
||||||
|
operators: ['>', '<', '-'],
|
||||||
|
|
||||||
tokenizer: {
|
tokenizer: {
|
||||||
root: [
|
root: [
|
||||||
|
// Comments
|
||||||
|
[/\/\/.*$/, 'comment'],
|
||||||
|
|
||||||
|
// Keywords - case insensitive
|
||||||
[
|
[
|
||||||
/\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/,
|
/\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',
|
'keyword',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Annotations in brackets
|
||||||
[/\[.*?\]/, 'annotation'],
|
[/\[.*?\]/, 'annotation'],
|
||||||
|
|
||||||
|
// Strings
|
||||||
[/'''/, 'string', '@tripleQuoteString'],
|
[/'''/, 'string', '@tripleQuoteString'],
|
||||||
[/".*?"/, 'string'],
|
[/"([^"\\]|\\.)*$/, 'string.invalid'], // non-terminated string
|
||||||
[/'.*?'/, 'string'],
|
[/'([^'\\]|\\.)*$/, 'string.invalid'], // non-terminated string
|
||||||
|
[/"/, 'string', '@string_double'],
|
||||||
|
[/'/, 'string', '@string_single'],
|
||||||
[/`.*?`/, 'string'],
|
[/`.*?`/, 'string'],
|
||||||
[/[{}]/, 'delimiter'],
|
|
||||||
[/[<>]/, 'operator'],
|
// Delimiters and operators
|
||||||
[new RegExp(`\\b(${datatypePattern})\\b`, 'i'), 'type'], // Added 'i' flag for case-insensitive matching
|
[/[{}()]/, 'delimiter'],
|
||||||
|
[/[<>-]/, 'operator'],
|
||||||
|
[/:/, 'delimiter'],
|
||||||
|
|
||||||
|
// Data types
|
||||||
|
[new RegExp(`\\b(${datatypePattern})\\b`, 'i'), 'type'],
|
||||||
|
|
||||||
|
// Numbers
|
||||||
|
[/\d+/, 'number'],
|
||||||
|
|
||||||
|
// Identifiers
|
||||||
|
[/[a-zA-Z_]\w*/, 'identifier'],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
string_double: [
|
||||||
|
[/[^\\"]+/, 'string'],
|
||||||
|
[/\\./, 'string.escape'],
|
||||||
|
[/"/, 'string', '@pop'],
|
||||||
|
],
|
||||||
|
|
||||||
|
string_single: [
|
||||||
|
[/[^\\']+/, 'string'],
|
||||||
|
[/\\./, 'string.escape'],
|
||||||
|
[/'/, 'string', '@pop'],
|
||||||
|
],
|
||||||
|
|
||||||
tripleQuoteString: [
|
tripleQuoteString: [
|
||||||
[/[^']+/, 'string'],
|
[/[^']+/, 'string'],
|
||||||
[/'''/, 'string', '@pop'],
|
[/'''/, 'string', '@pop'],
|
||||||
|
|||||||
@@ -5,27 +5,45 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@/components/popover/popover';
|
} from '@/components/popover/popover';
|
||||||
import { colorOptions } from '@/lib/colors';
|
import { colorOptions } from '@/lib/colors';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
export interface ColorPickerProps {
|
export interface ColorPickerProps {
|
||||||
color: string;
|
color: string;
|
||||||
onChange: (color: string) => void;
|
onChange: (color: string) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
popoverOnMouseDown?: (e: React.MouseEvent) => void;
|
||||||
|
popoverOnClick?: (e: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ColorPicker = React.forwardRef<
|
export const ColorPicker = React.forwardRef<
|
||||||
React.ElementRef<typeof PopoverTrigger>,
|
React.ElementRef<typeof PopoverTrigger>,
|
||||||
ColorPickerProps
|
ColorPickerProps
|
||||||
>(({ color, onChange }, ref) => {
|
>(({ color, onChange, disabled, popoverOnMouseDown, popoverOnClick }, ref) => {
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild ref={ref}>
|
<PopoverTrigger
|
||||||
|
asChild
|
||||||
|
ref={ref}
|
||||||
|
disabled={disabled}
|
||||||
|
{...(disabled ? { onClick: (e) => e.preventDefault() } : {})}
|
||||||
|
>
|
||||||
<div
|
<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={{
|
style={{
|
||||||
backgroundColor: color,
|
backgroundColor: color,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-fit">
|
<PopoverContent
|
||||||
|
className="w-fit"
|
||||||
|
onMouseDown={popoverOnMouseDown}
|
||||||
|
onClick={popoverOnClick}
|
||||||
|
>
|
||||||
<div className="grid grid-cols-4 gap-2">
|
<div className="grid grid-cols-4 gap-2">
|
||||||
{colorOptions.map((option) => (
|
{colorOptions.map((option) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Cross2Icon } from '@radix-ui/react-icons';
|
|||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { ScrollArea } from '../scroll-area/scroll-area';
|
import { ScrollArea } from '../scroll-area/scroll-area';
|
||||||
|
import { ChevronLeft } from 'lucide-react';
|
||||||
|
|
||||||
const Dialog = DialogPrimitive.Root;
|
const Dialog = DialogPrimitive.Root;
|
||||||
|
|
||||||
@@ -32,28 +33,75 @@ const DialogContent = React.forwardRef<
|
|||||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
||||||
showClose?: boolean;
|
showClose?: boolean;
|
||||||
|
showBack?: boolean;
|
||||||
|
backButtonClassName?: string;
|
||||||
|
blurBackground?: boolean;
|
||||||
|
forceOverlay?: boolean;
|
||||||
|
onBackClick?: () => void;
|
||||||
}
|
}
|
||||||
>(({ className, children, showClose, ...props }, ref) => (
|
>(
|
||||||
<DialogPortal>
|
(
|
||||||
<DialogOverlay />
|
{
|
||||||
<DialogPrimitive.Content
|
className,
|
||||||
ref={ref}
|
children,
|
||||||
className={cn(
|
showClose,
|
||||||
'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',
|
showBack,
|
||||||
className
|
onBackClick,
|
||||||
)}
|
backButtonClassName,
|
||||||
{...props}
|
blurBackground,
|
||||||
>
|
forceOverlay,
|
||||||
{children}
|
...props
|
||||||
{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">
|
ref
|
||||||
<Cross2Icon className="size-4" />
|
) => (
|
||||||
<span className="sr-only">Close</span>
|
<DialogPortal>
|
||||||
</DialogPrimitive.Close>
|
{forceOverlay ? (
|
||||||
)}
|
<div
|
||||||
</DialogPrimitive.Content>
|
className={cn(
|
||||||
</DialogPortal>
|
'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;
|
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||||
|
|
||||||
const DialogHeader = ({
|
const DialogHeader = ({
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
import React, { forwardRef } from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
import EmptyStateImage from '@/assets/empty_state.png';
|
import EmptyStateImage from '@/assets/empty_state.png';
|
||||||
import EmptyStateImageDark from '@/assets/empty_state_dark.png';
|
import EmptyStateImageDark from '@/assets/empty_state_dark.png';
|
||||||
import { Label } from '@/components/label/label';
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useTheme } from '@/hooks/use-theme';
|
import { useTheme } from '@/hooks/use-theme';
|
||||||
|
import {
|
||||||
|
Empty,
|
||||||
|
EmptyContent,
|
||||||
|
EmptyDescription,
|
||||||
|
EmptyHeader,
|
||||||
|
EmptyMedia,
|
||||||
|
EmptyTitle,
|
||||||
|
} from '../empty/empty';
|
||||||
|
|
||||||
export interface EmptyStateProps {
|
export interface EmptyStateProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -38,26 +45,29 @@ export const EmptyState = forwardRef<
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<img
|
<Empty>
|
||||||
src={
|
<EmptyHeader>
|
||||||
effectiveTheme === 'dark'
|
<EmptyMedia variant="icon">
|
||||||
? EmptyStateImageDark
|
{/* <Group /> */}
|
||||||
: EmptyStateImage
|
<img
|
||||||
}
|
src={
|
||||||
alt="Empty state"
|
effectiveTheme === 'dark'
|
||||||
className={cn('mb-2 w-20', imageClassName)}
|
? EmptyStateImageDark
|
||||||
/>
|
: EmptyStateImage
|
||||||
<Label className={cn('text-base', titleClassName)}>
|
}
|
||||||
{title}
|
alt="Empty state"
|
||||||
</Label>
|
className={cn('p-2', imageClassName)}
|
||||||
<Label
|
/>
|
||||||
className={cn(
|
</EmptyMedia>
|
||||||
'text-sm text-center font-normal text-muted-foreground',
|
<EmptyTitle className={titleClassName}>
|
||||||
descriptionClassName
|
{title}
|
||||||
)}
|
</EmptyTitle>
|
||||||
>
|
<EmptyDescription className={descriptionClassName}>
|
||||||
{description}
|
{description}
|
||||||
</Label>
|
</EmptyDescription>
|
||||||
|
</EmptyHeader>
|
||||||
|
<EmptyContent />
|
||||||
|
</Empty>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
105
src/components/empty/empty.tsx
Normal file
105
src/components/empty/empty.tsx
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils/index';
|
||||||
|
|
||||||
|
function Empty({ className, ...props }: React.ComponentProps<'div'>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="empty"
|
||||||
|
className={cn(
|
||||||
|
'flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance rounded-lg border-dashed p-6 text-center md:p-12',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EmptyHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="empty-header"
|
||||||
|
className={cn(
|
||||||
|
'flex max-w-sm flex-col items-center gap-2 text-center',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyMediaVariants = cva(
|
||||||
|
'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: 'bg-transparent',
|
||||||
|
icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'default',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function EmptyMedia({
|
||||||
|
className,
|
||||||
|
variant = 'default',
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<'div'> & VariantProps<typeof emptyMediaVariants>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="empty-icon"
|
||||||
|
data-variant={variant}
|
||||||
|
className={cn(emptyMediaVariants({ variant, className }))}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EmptyTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="empty-title"
|
||||||
|
className={cn('text-lg font-medium tracking-tight', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="empty-description"
|
||||||
|
className={cn(
|
||||||
|
'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EmptyContent({ className, ...props }: React.ComponentProps<'div'>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="empty-content"
|
||||||
|
className={cn(
|
||||||
|
'flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Empty,
|
||||||
|
EmptyHeader,
|
||||||
|
EmptyTitle,
|
||||||
|
EmptyDescription,
|
||||||
|
EmptyContent,
|
||||||
|
EmptyMedia,
|
||||||
|
};
|
||||||
@@ -2,16 +2,13 @@ import React from 'react';
|
|||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
export interface InputProps
|
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
||||||
|
|
||||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
||||||
({ className, type, ...props }, ref) => {
|
({ className, type, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export interface SelectBoxOption {
|
|||||||
regex?: string;
|
regex?: string;
|
||||||
extractRegex?: RegExp;
|
extractRegex?: RegExp;
|
||||||
group?: string;
|
group?: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectBoxProps {
|
export interface SelectBoxProps {
|
||||||
@@ -53,6 +54,11 @@ export interface SelectBoxProps {
|
|||||||
open?: boolean;
|
open?: boolean;
|
||||||
onOpenChange?: (open: boolean) => void;
|
onOpenChange?: (open: boolean) => void;
|
||||||
popoverClassName?: string;
|
popoverClassName?: string;
|
||||||
|
readonly?: boolean;
|
||||||
|
footerButtons?: React.ReactNode;
|
||||||
|
commandOnMouseDown?: (e: React.MouseEvent) => void;
|
||||||
|
commandOnClick?: (e: React.MouseEvent) => void;
|
||||||
|
onSearchChange?: (search: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
||||||
@@ -78,6 +84,11 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
open,
|
open,
|
||||||
onOpenChange: setOpen,
|
onOpenChange: setOpen,
|
||||||
popoverClassName,
|
popoverClassName,
|
||||||
|
readonly,
|
||||||
|
footerButtons,
|
||||||
|
commandOnMouseDown,
|
||||||
|
commandOnClick,
|
||||||
|
onSearchChange,
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
@@ -94,6 +105,10 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
setOpen?.(isOpen);
|
setOpen?.(isOpen);
|
||||||
setIsOpen(isOpen);
|
setIsOpen(isOpen);
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
setSearchTerm('');
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => (document.body.style.pointerEvents = ''), 500);
|
setTimeout(() => (document.body.style.pointerEvents = ''), 500);
|
||||||
},
|
},
|
||||||
[setOpen]
|
[setOpen]
|
||||||
@@ -148,18 +163,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' : ''}`}
|
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>{option.label}</span>
|
||||||
<span
|
{!readonly ? (
|
||||||
onClick={(e) => {
|
<span
|
||||||
e.preventDefault();
|
onClick={(e) => {
|
||||||
handleSelect(option.value);
|
e.preventDefault();
|
||||||
}}
|
handleSelect(option.value);
|
||||||
className="flex items-center rounded-sm px-px text-muted-foreground/60 hover:bg-accent hover:text-muted-foreground"
|
}}
|
||||||
>
|
className="flex items-center rounded-sm px-px text-muted-foreground/60 hover:bg-accent hover:text-muted-foreground"
|
||||||
<Cross2Icon />
|
>
|
||||||
</span>
|
<Cross2Icon />
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
)),
|
)),
|
||||||
[options, value, handleSelect, oneLine, keepOrder]
|
[options, value, handleSelect, oneLine, keepOrder, readonly]
|
||||||
);
|
);
|
||||||
|
|
||||||
const isAllSelected = React.useMemo(
|
const isAllSelected = React.useMemo(
|
||||||
@@ -225,6 +242,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
<CommandItem
|
<CommandItem
|
||||||
className="flex items-center"
|
className="flex items-center"
|
||||||
key={option.value}
|
key={option.value}
|
||||||
|
value={option.label}
|
||||||
keywords={option.regex ? [option.regex] : undefined}
|
keywords={option.regex ? [option.regex] : undefined}
|
||||||
onSelect={() =>
|
onSelect={() =>
|
||||||
handleSelect(
|
handleSelect(
|
||||||
@@ -232,6 +250,8 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
matches?.map((match) => match?.toString())
|
matches?.map((match) => match?.toString())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
onMouseDown={commandOnMouseDown}
|
||||||
|
onClick={commandOnClick}
|
||||||
>
|
>
|
||||||
{multiple && (
|
{multiple && (
|
||||||
<div
|
<div
|
||||||
@@ -246,6 +266,11 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-1 items-center truncate">
|
<div className="flex flex-1 items-center truncate">
|
||||||
|
{option.icon ? (
|
||||||
|
<span className="mr-2 shrink-0">
|
||||||
|
{option.icon}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
<span>
|
<span>
|
||||||
{isRegexMatch ? searchTerm : option.label}
|
{isRegexMatch ? searchTerm : option.label}
|
||||||
{!isRegexMatch && optionSuffix
|
{!isRegexMatch && optionSuffix
|
||||||
@@ -272,7 +297,15 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
</CommandItem>
|
</CommandItem>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[value, multiple, searchTerm, handleSelect, optionSuffix]
|
[
|
||||||
|
value,
|
||||||
|
multiple,
|
||||||
|
searchTerm,
|
||||||
|
handleSelect,
|
||||||
|
optionSuffix,
|
||||||
|
commandOnClick,
|
||||||
|
commandOnMouseDown,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -280,7 +313,7 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
<PopoverTrigger asChild tabIndex={0} onKeyDown={handleKeyDown}>
|
<PopoverTrigger asChild tabIndex={0} onKeyDown={handleKeyDown}>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -350,6 +383,8 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
popoverClassName
|
popoverClassName
|
||||||
)}
|
)}
|
||||||
align="center"
|
align="center"
|
||||||
|
onMouseDown={(e) => e.stopPropagation()}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<Command
|
<Command
|
||||||
filter={(value, search, keywords) => {
|
filter={(value, search, keywords) => {
|
||||||
@@ -372,7 +407,10 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<CommandInput
|
<CommandInput
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onValueChange={(e) => setSearchTerm(e)}
|
onValueChange={(e) => {
|
||||||
|
setSearchTerm(e);
|
||||||
|
onSearchChange?.(e);
|
||||||
|
}}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
placeholder={inputPlaceholder ?? 'Search...'}
|
placeholder={inputPlaceholder ?? 'Search...'}
|
||||||
className="h-9"
|
className="h-9"
|
||||||
@@ -439,6 +477,9 @@ export const SelectBox = React.forwardRef<HTMLInputElement, SelectBoxProps>(
|
|||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</Command>
|
</Command>
|
||||||
|
{footerButtons ? (
|
||||||
|
<div className="border-t">{footerButtons}</div>
|
||||||
|
) : null}
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|||||||
const SIDEBAR_WIDTH = '16rem';
|
const SIDEBAR_WIDTH = '16rem';
|
||||||
const SIDEBAR_WIDTH_MOBILE = '18rem';
|
const SIDEBAR_WIDTH_MOBILE = '18rem';
|
||||||
const SIDEBAR_WIDTH_ICON = '3rem';
|
const SIDEBAR_WIDTH_ICON = '3rem';
|
||||||
|
const SIDEBAR_WIDTH_ICON_EXTENDED = '4rem';
|
||||||
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
||||||
|
|
||||||
type SidebarContext = {
|
type SidebarContext = {
|
||||||
@@ -142,6 +143,8 @@ const SidebarProvider = React.forwardRef<
|
|||||||
{
|
{
|
||||||
'--sidebar-width': SIDEBAR_WIDTH,
|
'--sidebar-width': SIDEBAR_WIDTH,
|
||||||
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
|
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
|
||||||
|
'--sidebar-width-icon-extended':
|
||||||
|
SIDEBAR_WIDTH_ICON_EXTENDED,
|
||||||
...style,
|
...style,
|
||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
@@ -166,7 +169,7 @@ const Sidebar = React.forwardRef<
|
|||||||
React.ComponentProps<'div'> & {
|
React.ComponentProps<'div'> & {
|
||||||
side?: 'left' | 'right';
|
side?: 'left' | 'right';
|
||||||
variant?: 'sidebar' | 'floating' | 'inset';
|
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-[collapsible=offcanvas]:w-0',
|
||||||
'group-data-[side=right]:rotate-180',
|
'group-data-[side=right]:rotate-180',
|
||||||
variant === 'floating' || variant === 'inset'
|
variant === 'floating' || variant === 'inset'
|
||||||
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]'
|
? '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]:w-[--sidebar-width-icon] group-data-[collapsible=icon-extended]:w-[--sidebar-width-icon-extended]'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
@@ -257,8 +260,8 @@ const Sidebar = React.forwardRef<
|
|||||||
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
|
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
|
||||||
// Adjust the padding for floating and inset variants.
|
// Adjust the padding for floating and inset variants.
|
||||||
variant === 'floating' || variant === 'inset'
|
variant === 'floating' || variant === 'inset'
|
||||||
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]'
|
? '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-[side=left]:border-r group-data-[side=right]:border-l',
|
: '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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -421,7 +424,7 @@ const SidebarContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
data-sidebar="content"
|
data-sidebar="content"
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -461,6 +464,7 @@ const SidebarGroupLabel = React.forwardRef<
|
|||||||
className={cn(
|
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',
|
'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]:-mt-8 group-data-[collapsible=icon]:opacity-0',
|
||||||
|
'group-data-[collapsible=icon-extended]:-mt-8 group-data-[collapsible=icon-extended]:opacity-0',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...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',
|
'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.
|
// Increases the hit area of the button on mobile.
|
||||||
'after:absolute after:-inset-2 after:md:hidden',
|
'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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -532,7 +536,7 @@ const SidebarMenuItem = React.forwardRef<
|
|||||||
SidebarMenuItem.displayName = 'SidebarMenuItem';
|
SidebarMenuItem.displayName = 'SidebarMenuItem';
|
||||||
|
|
||||||
const sidebarMenuButtonVariants = cva(
|
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: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
@@ -636,7 +640,7 @@ const SidebarMenuAction = React.forwardRef<
|
|||||||
'peer-data-[size=sm]/menu-button:top-1',
|
'peer-data-[size=sm]/menu-button:top-1',
|
||||||
'peer-data-[size=default]/menu-button:top-1.5',
|
'peer-data-[size=default]/menu-button:top-1.5',
|
||||||
'peer-data-[size=lg]/menu-button:top-2.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 &&
|
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',
|
'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
|
className
|
||||||
@@ -753,7 +757,7 @@ const SidebarMenuSubButton = React.forwardRef<
|
|||||||
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
|
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
|
||||||
size === 'sm' && 'text-xs',
|
size === 'sm' && 'text-xs',
|
||||||
size === 'md' && 'text-sm',
|
size === 'md' && 'text-sm',
|
||||||
'group-data-[collapsible=icon]:hidden',
|
'group-data-[collapsible=icon]:hidden group-data-[collapsible=icon-extended]:hidden',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -13,15 +13,17 @@ const TooltipContent = React.forwardRef<
|
|||||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||||
|
// <TooltipPrimitive.Portal>
|
||||||
<TooltipPrimitive.Content
|
<TooltipPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
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
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
// </TooltipPrimitive.Portal>
|
||||||
));
|
));
|
||||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ interface TreeViewProps<
|
|||||||
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
loadingNodeIds?: string[];
|
loadingNodeIds?: string[];
|
||||||
|
disableCache?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TreeView<
|
export function TreeView<
|
||||||
@@ -62,12 +63,14 @@ export function TreeView<
|
|||||||
renderHoverComponent,
|
renderHoverComponent,
|
||||||
renderActionsComponent,
|
renderActionsComponent,
|
||||||
loadingNodeIds,
|
loadingNodeIds,
|
||||||
|
disableCache = false,
|
||||||
}: TreeViewProps<Type, Context>) {
|
}: TreeViewProps<Type, Context>) {
|
||||||
const { expanded, loading, loadedChildren, hasMoreChildren, toggleNode } =
|
const { expanded, loading, loadedChildren, hasMoreChildren, toggleNode } =
|
||||||
useTree({
|
useTree({
|
||||||
fetchChildren,
|
fetchChildren,
|
||||||
expanded: expandedProp,
|
expanded: expandedProp,
|
||||||
setExpanded: setExpandedProp,
|
setExpanded: setExpandedProp,
|
||||||
|
disableCache,
|
||||||
});
|
});
|
||||||
const [selectedIdInternal, setSelectedIdInternal] = React.useState<
|
const [selectedIdInternal, setSelectedIdInternal] = React.useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
@@ -145,6 +148,7 @@ export function TreeView<
|
|||||||
renderHoverComponent={renderHoverComponent}
|
renderHoverComponent={renderHoverComponent}
|
||||||
renderActionsComponent={renderActionsComponent}
|
renderActionsComponent={renderActionsComponent}
|
||||||
loadingNodeIds={loadingNodeIds}
|
loadingNodeIds={loadingNodeIds}
|
||||||
|
disableCache={disableCache}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -179,6 +183,7 @@ interface TreeNodeProps<
|
|||||||
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
loadingNodeIds?: string[];
|
loadingNodeIds?: string[];
|
||||||
|
disableCache?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
||||||
@@ -201,11 +206,16 @@ function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
|||||||
renderHoverComponent,
|
renderHoverComponent,
|
||||||
renderActionsComponent,
|
renderActionsComponent,
|
||||||
loadingNodeIds,
|
loadingNodeIds,
|
||||||
|
disableCache = false,
|
||||||
}: TreeNodeProps<Type, Context>) {
|
}: TreeNodeProps<Type, Context>) {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const isExpanded = expanded[node.id];
|
const isExpanded = expanded[node.id];
|
||||||
const isLoading = loading[node.id];
|
const isLoading = loading[node.id];
|
||||||
const children = loadedChildren[node.id] || node.children;
|
// If cache is disabled, always use fresh node.children
|
||||||
|
// Otherwise, use cached loadedChildren if available (for async fetched data)
|
||||||
|
const children = disableCache
|
||||||
|
? node.children
|
||||||
|
: node.children || loadedChildren[node.id];
|
||||||
const isSelected = selectedId === node.id;
|
const isSelected = selectedId === node.id;
|
||||||
|
|
||||||
const IconComponent =
|
const IconComponent =
|
||||||
@@ -423,6 +433,7 @@ function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
|||||||
renderHoverComponent={renderHoverComponent}
|
renderHoverComponent={renderHoverComponent}
|
||||||
renderActionsComponent={renderActionsComponent}
|
renderActionsComponent={renderActionsComponent}
|
||||||
loadingNodeIds={loadingNodeIds}
|
loadingNodeIds={loadingNodeIds}
|
||||||
|
disableCache={disableCache}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
|||||||
@@ -28,10 +28,12 @@ export function useTree<
|
|||||||
fetchChildren,
|
fetchChildren,
|
||||||
expanded: expandedProp,
|
expanded: expandedProp,
|
||||||
setExpanded: setExpandedProp,
|
setExpanded: setExpandedProp,
|
||||||
|
disableCache = false,
|
||||||
}: {
|
}: {
|
||||||
fetchChildren?: FetchChildrenFunction<Type, Context>;
|
fetchChildren?: FetchChildrenFunction<Type, Context>;
|
||||||
expanded?: ExpandedState;
|
expanded?: ExpandedState;
|
||||||
setExpanded?: Dispatch<SetStateAction<ExpandedState>>;
|
setExpanded?: Dispatch<SetStateAction<ExpandedState>>;
|
||||||
|
disableCache?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [expandedInternal, setExpandedInternal] = useState<ExpandedState>({});
|
const [expandedInternal, setExpandedInternal] = useState<ExpandedState>({});
|
||||||
|
|
||||||
@@ -89,8 +91,8 @@ export function useTree<
|
|||||||
// Get any previously fetched children
|
// Get any previously fetched children
|
||||||
const previouslyFetchedChildren = loadedChildren[nodeId] || [];
|
const previouslyFetchedChildren = loadedChildren[nodeId] || [];
|
||||||
|
|
||||||
// If we have static children, merge them with any previously fetched children
|
// Only cache if caching is enabled
|
||||||
if (staticChildren?.length) {
|
if (!disableCache && staticChildren?.length) {
|
||||||
const mergedChildren = mergeChildren(
|
const mergedChildren = mergeChildren(
|
||||||
staticChildren,
|
staticChildren,
|
||||||
previouslyFetchedChildren
|
previouslyFetchedChildren
|
||||||
@@ -110,8 +112,8 @@ export function useTree<
|
|||||||
// Set expanded state immediately to show static/previously fetched children
|
// Set expanded state immediately to show static/previously fetched children
|
||||||
setExpanded((prev) => ({ ...prev, [nodeId]: true }));
|
setExpanded((prev) => ({ ...prev, [nodeId]: true }));
|
||||||
|
|
||||||
// If we haven't loaded dynamic children yet
|
// If we haven't loaded dynamic children yet and cache is enabled
|
||||||
if (!previouslyFetchedChildren.length) {
|
if (!disableCache && !previouslyFetchedChildren.length) {
|
||||||
setLoading((prev) => ({ ...prev, [nodeId]: true }));
|
setLoading((prev) => ({ ...prev, [nodeId]: true }));
|
||||||
try {
|
try {
|
||||||
const fetchedChildren = await fetchChildren?.(
|
const fetchedChildren = await fetchChildren?.(
|
||||||
@@ -140,7 +142,14 @@ export function useTree<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[expanded, loadedChildren, fetchChildren, mergeChildren, setExpanded]
|
[
|
||||||
|
expanded,
|
||||||
|
loadedChildren,
|
||||||
|
fetchChildren,
|
||||||
|
mergeChildren,
|
||||||
|
setExpanded,
|
||||||
|
disableCache,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,6 +2,24 @@ import { createContext } from 'react';
|
|||||||
import { emptyFn } from '@/lib/utils';
|
import { emptyFn } from '@/lib/utils';
|
||||||
import type { Graph } from '@/lib/graph';
|
import type { Graph } from '@/lib/graph';
|
||||||
import { createGraph } from '@/lib/graph';
|
import { createGraph } from '@/lib/graph';
|
||||||
|
import { EventEmitter } from 'ahooks/lib/useEventEmitter';
|
||||||
|
|
||||||
|
export type CanvasEventType = 'pan_click';
|
||||||
|
|
||||||
|
export type CanvasEventBase<T extends CanvasEventType, D> = {
|
||||||
|
action: T;
|
||||||
|
data: D;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PanClickEvent = CanvasEventBase<
|
||||||
|
'pan_click',
|
||||||
|
{
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type CanvasEvent = PanClickEvent;
|
||||||
|
|
||||||
export interface CanvasContext {
|
export interface CanvasContext {
|
||||||
reorderTables: (options?: { updateHistory?: boolean }) => void;
|
reorderTables: (options?: { updateHistory?: boolean }) => void;
|
||||||
@@ -14,6 +32,42 @@ export interface CanvasContext {
|
|||||||
overlapGraph: Graph<string>;
|
overlapGraph: Graph<string>;
|
||||||
setShowFilter: React.Dispatch<React.SetStateAction<boolean>>;
|
setShowFilter: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
showFilter: boolean;
|
showFilter: boolean;
|
||||||
|
editTableModeTable: {
|
||||||
|
tableId: string;
|
||||||
|
fieldId?: string;
|
||||||
|
} | null;
|
||||||
|
setEditTableModeTable: React.Dispatch<
|
||||||
|
React.SetStateAction<{
|
||||||
|
tableId: string;
|
||||||
|
fieldId?: string;
|
||||||
|
} | null>
|
||||||
|
>;
|
||||||
|
tempFloatingEdge: {
|
||||||
|
sourceNodeId: string;
|
||||||
|
targetNodeId?: string;
|
||||||
|
} | null;
|
||||||
|
setTempFloatingEdge: React.Dispatch<
|
||||||
|
React.SetStateAction<{
|
||||||
|
sourceNodeId: string;
|
||||||
|
targetNodeId?: string;
|
||||||
|
} | null>
|
||||||
|
>;
|
||||||
|
startFloatingEdgeCreation: ({
|
||||||
|
sourceNodeId,
|
||||||
|
}: {
|
||||||
|
sourceNodeId: string;
|
||||||
|
}) => void;
|
||||||
|
endFloatingEdgeCreation: () => void;
|
||||||
|
hoveringTableId: string | null;
|
||||||
|
setHoveringTableId: React.Dispatch<React.SetStateAction<string | null>>;
|
||||||
|
showCreateRelationshipNode: (params: {
|
||||||
|
sourceTableId: string;
|
||||||
|
targetTableId: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}) => void;
|
||||||
|
hideCreateRelationshipNode: () => void;
|
||||||
|
events: EventEmitter<CanvasEvent>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const canvasContext = createContext<CanvasContext>({
|
export const canvasContext = createContext<CanvasContext>({
|
||||||
@@ -23,4 +77,15 @@ export const canvasContext = createContext<CanvasContext>({
|
|||||||
overlapGraph: createGraph(),
|
overlapGraph: createGraph(),
|
||||||
setShowFilter: emptyFn,
|
setShowFilter: emptyFn,
|
||||||
showFilter: false,
|
showFilter: false,
|
||||||
|
editTableModeTable: null,
|
||||||
|
setEditTableModeTable: emptyFn,
|
||||||
|
tempFloatingEdge: null,
|
||||||
|
setTempFloatingEdge: emptyFn,
|
||||||
|
startFloatingEdgeCreation: emptyFn,
|
||||||
|
endFloatingEdgeCreation: emptyFn,
|
||||||
|
hoveringTableId: null,
|
||||||
|
setHoveringTableId: emptyFn,
|
||||||
|
showCreateRelationshipNode: emptyFn,
|
||||||
|
hideCreateRelationshipNode: emptyFn,
|
||||||
|
events: new EventEmitter(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,28 +1,74 @@
|
|||||||
import React, { type ReactNode, useCallback, useState } from 'react';
|
import React, {
|
||||||
|
type ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
import type { CanvasContext, CanvasEvent } from './canvas-context';
|
||||||
import { canvasContext } from './canvas-context';
|
import { canvasContext } from './canvas-context';
|
||||||
import { useChartDB } from '@/hooks/use-chartdb';
|
import { useChartDB } from '@/hooks/use-chartdb';
|
||||||
import {
|
import { adjustTablePositions } from '@/lib/domain/db-table';
|
||||||
adjustTablePositions,
|
|
||||||
shouldShowTablesBySchemaFilter,
|
|
||||||
} from '@/lib/domain/db-table';
|
|
||||||
import { useReactFlow } from '@xyflow/react';
|
import { useReactFlow } from '@xyflow/react';
|
||||||
import { findOverlappingTables } from '@/pages/editor-page/canvas/canvas-utils';
|
import { findOverlappingTables } from '@/pages/editor-page/canvas/canvas-utils';
|
||||||
import type { Graph } from '@/lib/graph';
|
import type { Graph } from '@/lib/graph';
|
||||||
import { createGraph } 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';
|
||||||
|
import {
|
||||||
|
CREATE_RELATIONSHIP_NODE_ID,
|
||||||
|
type CreateRelationshipNodeType,
|
||||||
|
} from '@/pages/editor-page/canvas/create-relationship-node/create-relationship-node';
|
||||||
|
import { useEventEmitter } from 'ahooks';
|
||||||
|
|
||||||
interface CanvasProviderProps {
|
interface CanvasProviderProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
||||||
const { tables, relationships, updateTablesState, filteredSchemas } =
|
const {
|
||||||
useChartDB();
|
tables,
|
||||||
const { fitView } = useReactFlow();
|
relationships,
|
||||||
|
updateTablesState,
|
||||||
|
databaseType,
|
||||||
|
areas,
|
||||||
|
diagramId,
|
||||||
|
} = useChartDB();
|
||||||
|
const { filter, loading: filterLoading } = useDiagramFilter();
|
||||||
|
const { fitView, screenToFlowPosition, setNodes } = useReactFlow();
|
||||||
const [overlapGraph, setOverlapGraph] =
|
const [overlapGraph, setOverlapGraph] =
|
||||||
useState<Graph<string>>(createGraph());
|
useState<Graph<string>>(createGraph());
|
||||||
|
const [editTableModeTable, setEditTableModeTable] = useState<{
|
||||||
|
tableId: string;
|
||||||
|
fieldId?: string;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
|
const events = useEventEmitter<CanvasEvent>();
|
||||||
|
|
||||||
const [showFilter, setShowFilter] = useState(false);
|
const [showFilter, setShowFilter] = useState(false);
|
||||||
|
|
||||||
|
const [tempFloatingEdge, setTempFloatingEdge] =
|
||||||
|
useState<CanvasContext['tempFloatingEdge']>(null);
|
||||||
|
|
||||||
|
const [hoveringTableId, setHoveringTableId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const diagramIdActiveFilterRef = useRef<string>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (filterLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diagramIdActiveFilterRef.current === diagramId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
diagramIdActiveFilterRef.current = diagramId;
|
||||||
|
|
||||||
|
setShowFilter(true);
|
||||||
|
}, [filterLoading, diagramId]);
|
||||||
|
|
||||||
const reorderTables = useCallback(
|
const reorderTables = useCallback(
|
||||||
(
|
(
|
||||||
options: { updateHistory?: boolean } = {
|
options: { updateHistory?: boolean } = {
|
||||||
@@ -32,9 +78,19 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
|||||||
const newTables = adjustTablePositions({
|
const newTables = adjustTablePositions({
|
||||||
relationships,
|
relationships,
|
||||||
tables: tables.filter((table) =>
|
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({
|
const updatedOverlapGraph = findOverlappingTables({
|
||||||
@@ -69,9 +125,77 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
|||||||
});
|
});
|
||||||
}, 500);
|
}, 500);
|
||||||
},
|
},
|
||||||
[filteredSchemas, relationships, tables, updateTablesState, fitView]
|
[
|
||||||
|
filter,
|
||||||
|
relationships,
|
||||||
|
tables,
|
||||||
|
updateTablesState,
|
||||||
|
fitView,
|
||||||
|
databaseType,
|
||||||
|
areas,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const startFloatingEdgeCreation: CanvasContext['startFloatingEdgeCreation'] =
|
||||||
|
useCallback(({ sourceNodeId }) => {
|
||||||
|
setShowFilter(false);
|
||||||
|
setTempFloatingEdge({
|
||||||
|
sourceNodeId,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const endFloatingEdgeCreation: CanvasContext['endFloatingEdgeCreation'] =
|
||||||
|
useCallback(() => {
|
||||||
|
setTempFloatingEdge(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const hideCreateRelationshipNode: CanvasContext['hideCreateRelationshipNode'] =
|
||||||
|
useCallback(() => {
|
||||||
|
setNodes((nds) =>
|
||||||
|
nds.filter((n) => n.id !== CREATE_RELATIONSHIP_NODE_ID)
|
||||||
|
);
|
||||||
|
endFloatingEdgeCreation();
|
||||||
|
}, [setNodes, endFloatingEdgeCreation]);
|
||||||
|
|
||||||
|
const showCreateRelationshipNode: CanvasContext['showCreateRelationshipNode'] =
|
||||||
|
useCallback(
|
||||||
|
({ sourceTableId, targetTableId, x, y }) => {
|
||||||
|
setTempFloatingEdge((edge) =>
|
||||||
|
edge
|
||||||
|
? {
|
||||||
|
...edge,
|
||||||
|
targetNodeId: targetTableId,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
const cursorPos = screenToFlowPosition({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newNode: CreateRelationshipNodeType = {
|
||||||
|
id: CREATE_RELATIONSHIP_NODE_ID,
|
||||||
|
type: 'create-relationship',
|
||||||
|
position: cursorPos,
|
||||||
|
data: {
|
||||||
|
sourceTableId,
|
||||||
|
targetTableId,
|
||||||
|
},
|
||||||
|
draggable: true,
|
||||||
|
selectable: false,
|
||||||
|
zIndex: 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
setNodes((nds) => {
|
||||||
|
const nodesWithoutOldCreateRelationshipNode = nds.filter(
|
||||||
|
(n) => n.id !== CREATE_RELATIONSHIP_NODE_ID
|
||||||
|
);
|
||||||
|
return [...nodesWithoutOldCreateRelationshipNode, newNode];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[screenToFlowPosition, setNodes]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<canvasContext.Provider
|
<canvasContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -81,6 +205,17 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
|
|||||||
overlapGraph,
|
overlapGraph,
|
||||||
setShowFilter,
|
setShowFilter,
|
||||||
showFilter,
|
showFilter,
|
||||||
|
editTableModeTable,
|
||||||
|
setEditTableModeTable,
|
||||||
|
tempFloatingEdge: tempFloatingEdge,
|
||||||
|
setTempFloatingEdge: setTempFloatingEdge,
|
||||||
|
startFloatingEdgeCreation: startFloatingEdgeCreation,
|
||||||
|
endFloatingEdgeCreation: endFloatingEdgeCreation,
|
||||||
|
hoveringTableId,
|
||||||
|
setHoveringTableId,
|
||||||
|
showCreateRelationshipNode,
|
||||||
|
hideCreateRelationshipNode,
|
||||||
|
events,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type { DBDependency } from '@/lib/domain/db-dependency';
|
|||||||
import { EventEmitter } from 'ahooks/lib/useEventEmitter';
|
import { EventEmitter } from 'ahooks/lib/useEventEmitter';
|
||||||
import type { Area } from '@/lib/domain/area';
|
import type { Area } from '@/lib/domain/area';
|
||||||
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
||||||
|
import type { Note } from '@/lib/domain/note';
|
||||||
|
|
||||||
export type ChartDBEventType =
|
export type ChartDBEventType =
|
||||||
| 'add_tables'
|
| 'add_tables'
|
||||||
@@ -74,6 +75,7 @@ export interface ChartDBContext {
|
|||||||
dependencies: DBDependency[];
|
dependencies: DBDependency[];
|
||||||
areas: Area[];
|
areas: Area[];
|
||||||
customTypes: DBCustomType[];
|
customTypes: DBCustomType[];
|
||||||
|
notes: Note[];
|
||||||
currentDiagram: Diagram;
|
currentDiagram: Diagram;
|
||||||
events: EventEmitter<ChartDBEvent>;
|
events: EventEmitter<ChartDBEvent>;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
@@ -81,9 +83,6 @@ export interface ChartDBContext {
|
|||||||
highlightedCustomType?: DBCustomType;
|
highlightedCustomType?: DBCustomType;
|
||||||
highlightCustomTypeId: (id?: string) => void;
|
highlightCustomTypeId: (id?: string) => void;
|
||||||
|
|
||||||
filteredSchemas?: string[];
|
|
||||||
filterSchemas: (schemaIds: string[]) => void;
|
|
||||||
|
|
||||||
// General operations
|
// General operations
|
||||||
updateDiagramId: (id: string) => Promise<void>;
|
updateDiagramId: (id: string) => Promise<void>;
|
||||||
updateDiagramName: (
|
updateDiagramName: (
|
||||||
@@ -258,6 +257,31 @@ export interface ChartDBContext {
|
|||||||
options?: { updateHistory: boolean }
|
options?: { updateHistory: boolean }
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
|
// Note operations
|
||||||
|
createNote: (attributes?: Partial<Omit<Note, 'id'>>) => Promise<Note>;
|
||||||
|
addNote: (
|
||||||
|
note: Note,
|
||||||
|
options?: { updateHistory: boolean }
|
||||||
|
) => Promise<void>;
|
||||||
|
addNotes: (
|
||||||
|
notes: Note[],
|
||||||
|
options?: { updateHistory: boolean }
|
||||||
|
) => Promise<void>;
|
||||||
|
getNote: (id: string) => Note | null;
|
||||||
|
removeNote: (
|
||||||
|
id: string,
|
||||||
|
options?: { updateHistory: boolean }
|
||||||
|
) => Promise<void>;
|
||||||
|
removeNotes: (
|
||||||
|
ids: string[],
|
||||||
|
options?: { updateHistory: boolean }
|
||||||
|
) => Promise<void>;
|
||||||
|
updateNote: (
|
||||||
|
id: string,
|
||||||
|
note: Partial<Note>,
|
||||||
|
options?: { updateHistory: boolean }
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
// Custom type operations
|
// Custom type operations
|
||||||
createCustomType: (
|
createCustomType: (
|
||||||
attributes?: Partial<Omit<DBCustomType, 'id'>>
|
attributes?: Partial<Omit<DBCustomType, 'id'>>
|
||||||
@@ -284,11 +308,6 @@ export interface ChartDBContext {
|
|||||||
customType: Partial<DBCustomType>,
|
customType: Partial<DBCustomType>,
|
||||||
options?: { updateHistory: boolean }
|
options?: { updateHistory: boolean }
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
// Filters
|
|
||||||
hiddenTableIds?: string[];
|
|
||||||
addHiddenTableId: (tableId: string) => Promise<void>;
|
|
||||||
removeHiddenTableId: (tableId: string) => Promise<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const chartDBContext = createContext<ChartDBContext>({
|
export const chartDBContext = createContext<ChartDBContext>({
|
||||||
@@ -300,10 +319,9 @@ export const chartDBContext = createContext<ChartDBContext>({
|
|||||||
dependencies: [],
|
dependencies: [],
|
||||||
areas: [],
|
areas: [],
|
||||||
customTypes: [],
|
customTypes: [],
|
||||||
|
notes: [],
|
||||||
schemas: [],
|
schemas: [],
|
||||||
highlightCustomTypeId: emptyFn,
|
highlightCustomTypeId: emptyFn,
|
||||||
filteredSchemas: [],
|
|
||||||
filterSchemas: emptyFn,
|
|
||||||
currentDiagram: {
|
currentDiagram: {
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
@@ -378,6 +396,15 @@ export const chartDBContext = createContext<ChartDBContext>({
|
|||||||
removeAreas: emptyFn,
|
removeAreas: emptyFn,
|
||||||
updateArea: emptyFn,
|
updateArea: emptyFn,
|
||||||
|
|
||||||
|
// Note operations
|
||||||
|
createNote: emptyFn,
|
||||||
|
addNote: emptyFn,
|
||||||
|
addNotes: emptyFn,
|
||||||
|
getNote: emptyFn,
|
||||||
|
removeNote: emptyFn,
|
||||||
|
removeNotes: emptyFn,
|
||||||
|
updateNote: emptyFn,
|
||||||
|
|
||||||
// Custom type operations
|
// Custom type operations
|
||||||
createCustomType: emptyFn,
|
createCustomType: emptyFn,
|
||||||
addCustomType: emptyFn,
|
addCustomType: emptyFn,
|
||||||
@@ -386,9 +413,4 @@ export const chartDBContext = createContext<ChartDBContext>({
|
|||||||
removeCustomType: emptyFn,
|
removeCustomType: emptyFn,
|
||||||
removeCustomTypes: emptyFn,
|
removeCustomTypes: emptyFn,
|
||||||
updateCustomType: emptyFn,
|
updateCustomType: emptyFn,
|
||||||
|
|
||||||
// Filters
|
|
||||||
hiddenTableIds: [],
|
|
||||||
addHiddenTableId: emptyFn,
|
|
||||||
removeHiddenTableId: emptyFn,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import type { DBTable } from '@/lib/domain/db-table';
|
import type { DBTable } from '@/lib/domain/db-table';
|
||||||
import { deepCopy, generateId } from '@/lib/utils';
|
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 type { ChartDBContext, ChartDBEvent } from './chartdb-context';
|
||||||
import { chartDBContext } from './chartdb-context';
|
import { chartDBContext } from './chartdb-context';
|
||||||
import { DatabaseType } from '@/lib/domain/database-type';
|
import { DatabaseType } from '@/lib/domain/database-type';
|
||||||
import type { DBField } from '@/lib/domain/db-field';
|
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 type { DBRelationship } from '@/lib/domain/db-relationship';
|
||||||
import { useStorage } from '@/hooks/use-storage';
|
import { useStorage } from '@/hooks/use-storage';
|
||||||
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
||||||
@@ -17,11 +20,11 @@ import {
|
|||||||
databasesWithSchemas,
|
databasesWithSchemas,
|
||||||
schemaNameToSchemaId,
|
schemaNameToSchemaId,
|
||||||
} from '@/lib/domain/db-schema';
|
} from '@/lib/domain/db-schema';
|
||||||
import { useLocalConfig } from '@/hooks/use-local-config';
|
|
||||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||||
import { useEventEmitter } from 'ahooks';
|
import { useEventEmitter } from 'ahooks';
|
||||||
import type { DBDependency } from '@/lib/domain/db-dependency';
|
import type { DBDependency } from '@/lib/domain/db-dependency';
|
||||||
import type { Area } from '@/lib/domain/area';
|
import type { Area } from '@/lib/domain/area';
|
||||||
|
import type { Note } from '@/lib/domain/note';
|
||||||
import { storageInitialValue } from '../storage-context/storage-context';
|
import { storageInitialValue } from '../storage-context/storage-context';
|
||||||
import { useDiff } from '../diff-context/use-diff';
|
import { useDiff } from '../diff-context/use-diff';
|
||||||
import type { DiffCalculatedEvent } from '../diff-context/diff-context';
|
import type { DiffCalculatedEvent } from '../diff-context/diff-context';
|
||||||
@@ -29,7 +32,6 @@ import {
|
|||||||
DBCustomTypeKind,
|
DBCustomTypeKind,
|
||||||
type DBCustomType,
|
type DBCustomType,
|
||||||
} from '@/lib/domain/db-custom-type';
|
} from '@/lib/domain/db-custom-type';
|
||||||
import { useConfig } from '@/hooks/use-config';
|
|
||||||
|
|
||||||
export interface ChartDBProviderProps {
|
export interface ChartDBProviderProps {
|
||||||
diagram?: Diagram;
|
diagram?: Diagram;
|
||||||
@@ -40,17 +42,11 @@ export const ChartDBProvider: React.FC<
|
|||||||
React.PropsWithChildren<ChartDBProviderProps>
|
React.PropsWithChildren<ChartDBProviderProps>
|
||||||
> = ({ children, diagram, readonly: readonlyProp }) => {
|
> = ({ children, diagram, readonly: readonlyProp }) => {
|
||||||
const { hasDiff } = useDiff();
|
const { hasDiff } = useDiff();
|
||||||
const dbStorage = useStorage();
|
const storageDB = useStorage();
|
||||||
let db = dbStorage;
|
|
||||||
const events = useEventEmitter<ChartDBEvent>();
|
const events = useEventEmitter<ChartDBEvent>();
|
||||||
const { setSchemasFilter, schemasFilter } = useLocalConfig();
|
|
||||||
const { addUndoAction, resetRedoStack, resetUndoStack } =
|
const { addUndoAction, resetRedoStack, resetUndoStack } =
|
||||||
useRedoUndoStack();
|
useRedoUndoStack();
|
||||||
const {
|
|
||||||
getHiddenTablesForDiagram,
|
|
||||||
hideTableForDiagram,
|
|
||||||
unhideTableForDiagram,
|
|
||||||
} = useConfig();
|
|
||||||
const [diagramId, setDiagramId] = useState('');
|
const [diagramId, setDiagramId] = useState('');
|
||||||
const [diagramName, setDiagramName] = useState('');
|
const [diagramName, setDiagramName] = useState('');
|
||||||
const [diagramCreatedAt, setDiagramCreatedAt] = useState<Date>(new Date());
|
const [diagramCreatedAt, setDiagramCreatedAt] = useState<Date>(new Date());
|
||||||
@@ -72,17 +68,18 @@ export const ChartDBProvider: React.FC<
|
|||||||
const [customTypes, setCustomTypes] = useState<DBCustomType[]>(
|
const [customTypes, setCustomTypes] = useState<DBCustomType[]>(
|
||||||
diagram?.customTypes ?? []
|
diagram?.customTypes ?? []
|
||||||
);
|
);
|
||||||
const [hiddenTableIds, setHiddenTableIds] = useState<string[]>([]);
|
const [notes, setNotes] = useState<Note[]>(diagram?.notes ?? []);
|
||||||
|
|
||||||
const { events: diffEvents } = useDiff();
|
const { events: diffEvents } = useDiff();
|
||||||
|
|
||||||
const [highlightedCustomTypeId, setHighlightedCustomTypeId] =
|
const [highlightedCustomTypeId, setHighlightedCustomTypeId] =
|
||||||
useState<string>();
|
useState<string>();
|
||||||
|
|
||||||
const diffCalculatedHandler = useCallback((event: DiffCalculatedEvent) => {
|
const diffCalculatedHandler = useCallback((event: DiffCalculatedEvent) => {
|
||||||
const { tablesAdded, fieldsAdded, relationshipsAdded } = event.data;
|
const { tablesToAdd, fieldsToAdd, relationshipsToAdd } = event.data;
|
||||||
setTables((tables) =>
|
setTables((tables) =>
|
||||||
[...tables, ...(tablesAdded ?? [])].map((table) => {
|
[...tables, ...(tablesToAdd ?? [])].map((table) => {
|
||||||
const fields = fieldsAdded.get(table.id);
|
const fields = fieldsToAdd.get(table.id);
|
||||||
return fields
|
return fields
|
||||||
? { ...table, fields: [...table.fields, ...fields] }
|
? { ...table, fields: [...table.fields, ...fields] }
|
||||||
: table;
|
: table;
|
||||||
@@ -90,31 +87,22 @@ export const ChartDBProvider: React.FC<
|
|||||||
);
|
);
|
||||||
setRelationships((relationships) => [
|
setRelationships((relationships) => [
|
||||||
...relationships,
|
...relationships,
|
||||||
...(relationshipsAdded ?? []),
|
...(relationshipsToAdd ?? []),
|
||||||
]);
|
]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
diffEvents.useSubscription(diffCalculatedHandler);
|
diffEvents.useSubscription(diffCalculatedHandler);
|
||||||
|
|
||||||
// Sync hiddenTableIds with config
|
const defaultSchemaName = useMemo(
|
||||||
useEffect(() => {
|
() => defaultSchemas[databaseType],
|
||||||
if (diagramId) {
|
[databaseType]
|
||||||
const hiddenTables = getHiddenTablesForDiagram(diagramId);
|
);
|
||||||
setHiddenTableIds(hiddenTables);
|
|
||||||
}
|
|
||||||
}, [diagramId, getHiddenTablesForDiagram]);
|
|
||||||
|
|
||||||
const defaultSchemaName = defaultSchemas[databaseType];
|
|
||||||
|
|
||||||
const readonly = useMemo(
|
const readonly = useMemo(
|
||||||
() => readonlyProp ?? hasDiff ?? false,
|
() => readonlyProp ?? hasDiff ?? false,
|
||||||
[readonlyProp, hasDiff]
|
[readonlyProp, hasDiff]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (readonly) {
|
|
||||||
db = storageInitialValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const schemas = useMemo(
|
const schemas = useMemo(
|
||||||
() =>
|
() =>
|
||||||
databasesWithSchemas.includes(databaseType)
|
databasesWithSchemas.includes(databaseType)
|
||||||
@@ -125,9 +113,11 @@ export const ChartDBProvider: React.FC<
|
|||||||
.filter((schema) => !!schema) as string[]
|
.filter((schema) => !!schema) as string[]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
.sort((a, b) =>
|
.sort((a, b) => {
|
||||||
a === defaultSchemaName ? -1 : a.localeCompare(b)
|
if (a === defaultSchemaName) return -1;
|
||||||
)
|
if (b === defaultSchemaName) return 1;
|
||||||
|
return a.localeCompare(b);
|
||||||
|
})
|
||||||
.map(
|
.map(
|
||||||
(schema): DBSchema => ({
|
(schema): DBSchema => ({
|
||||||
id: schemaNameToSchemaId(schema),
|
id: schemaNameToSchemaId(schema),
|
||||||
@@ -141,34 +131,11 @@ export const ChartDBProvider: React.FC<
|
|||||||
[tables, defaultSchemaName, databaseType]
|
[tables, defaultSchemaName, databaseType]
|
||||||
);
|
);
|
||||||
|
|
||||||
const filterSchemas: ChartDBContext['filterSchemas'] = useCallback(
|
const db = useMemo(
|
||||||
(schemaIds) => {
|
() => (readonly ? storageInitialValue : storageDB),
|
||||||
setSchemasFilter((prev) => ({
|
[storageDB, readonly]
|
||||||
...prev,
|
|
||||||
[diagramId]: schemaIds,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
[diagramId, setSchemasFilter]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
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(
|
const currentDiagram: Diagram = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
id: diagramId,
|
id: diagramId,
|
||||||
@@ -182,6 +149,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
dependencies,
|
dependencies,
|
||||||
areas,
|
areas,
|
||||||
customTypes,
|
customTypes,
|
||||||
|
notes,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
diagramId,
|
diagramId,
|
||||||
@@ -193,6 +161,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
dependencies,
|
dependencies,
|
||||||
areas,
|
areas,
|
||||||
customTypes,
|
customTypes,
|
||||||
|
notes,
|
||||||
diagramCreatedAt,
|
diagramCreatedAt,
|
||||||
diagramUpdatedAt,
|
diagramUpdatedAt,
|
||||||
]
|
]
|
||||||
@@ -206,6 +175,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
setDependencies([]);
|
setDependencies([]);
|
||||||
setAreas([]);
|
setAreas([]);
|
||||||
setCustomTypes([]);
|
setCustomTypes([]);
|
||||||
|
setNotes([]);
|
||||||
setDiagramUpdatedAt(updatedAt);
|
setDiagramUpdatedAt(updatedAt);
|
||||||
|
|
||||||
resetRedoStack();
|
resetRedoStack();
|
||||||
@@ -218,6 +188,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
db.deleteDiagramDependencies(diagramId),
|
db.deleteDiagramDependencies(diagramId),
|
||||||
db.deleteDiagramAreas(diagramId),
|
db.deleteDiagramAreas(diagramId),
|
||||||
db.deleteDiagramCustomTypes(diagramId),
|
db.deleteDiagramCustomTypes(diagramId),
|
||||||
|
db.deleteDiagramNotes(diagramId),
|
||||||
]);
|
]);
|
||||||
}, [db, diagramId, resetRedoStack, resetUndoStack]);
|
}, [db, diagramId, resetRedoStack, resetUndoStack]);
|
||||||
|
|
||||||
@@ -232,6 +203,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
setDependencies([]);
|
setDependencies([]);
|
||||||
setAreas([]);
|
setAreas([]);
|
||||||
setCustomTypes([]);
|
setCustomTypes([]);
|
||||||
|
setNotes([]);
|
||||||
resetRedoStack();
|
resetRedoStack();
|
||||||
resetUndoStack();
|
resetUndoStack();
|
||||||
|
|
||||||
@@ -242,6 +214,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
db.deleteDiagramDependencies(diagramId),
|
db.deleteDiagramDependencies(diagramId),
|
||||||
db.deleteDiagramAreas(diagramId),
|
db.deleteDiagramAreas(diagramId),
|
||||||
db.deleteDiagramCustomTypes(diagramId),
|
db.deleteDiagramCustomTypes(diagramId),
|
||||||
|
db.deleteDiagramNotes(diagramId),
|
||||||
]);
|
]);
|
||||||
}, [db, diagramId, resetRedoStack, resetUndoStack]);
|
}, [db, diagramId, resetRedoStack, resetUndoStack]);
|
||||||
|
|
||||||
@@ -380,12 +353,18 @@ export const ChartDBProvider: React.FC<
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
indexes: [],
|
indexes: [],
|
||||||
color: randomColor(),
|
color: attributes?.isView ? viewColor : defaultTableColor,
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
isView: false,
|
isView: false,
|
||||||
order: tables.length,
|
order: tables.length,
|
||||||
...attributes,
|
...attributes,
|
||||||
|
schema: attributes?.schema ?? defaultSchemas[databaseType],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
table.indexes = getTableIndexesWithPrimaryKey({
|
||||||
|
table,
|
||||||
|
});
|
||||||
|
|
||||||
await addTable(table);
|
await addTable(table);
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
@@ -677,17 +656,30 @@ export const ChartDBProvider: React.FC<
|
|||||||
options = { updateHistory: true }
|
options = { updateHistory: true }
|
||||||
) => {
|
) => {
|
||||||
const prevField = getField(tableId, fieldId);
|
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) =>
|
setTables((tables) =>
|
||||||
tables.map((table) =>
|
tables.map((table) => {
|
||||||
table.id === tableId
|
if (table.id === tableId) {
|
||||||
? {
|
return updateTableFn(table);
|
||||||
...table,
|
}
|
||||||
fields: table.fields.map((f) =>
|
|
||||||
f.id === fieldId ? { ...f, ...field } : f
|
return table;
|
||||||
),
|
})
|
||||||
}
|
|
||||||
: table
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const table = await db.getTable({ diagramId, id: tableId });
|
const table = await db.getTable({ diagramId, id: tableId });
|
||||||
@@ -702,10 +694,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
db.updateTable({
|
db.updateTable({
|
||||||
id: tableId,
|
id: tableId,
|
||||||
attributes: {
|
attributes: {
|
||||||
...table,
|
...updateTableFn(table),
|
||||||
fields: table.fields.map((f) =>
|
|
||||||
f.id === fieldId ? { ...f, ...field } : f
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
@@ -732,19 +721,29 @@ export const ChartDBProvider: React.FC<
|
|||||||
fieldId: string,
|
fieldId: string,
|
||||||
options = { updateHistory: true }
|
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 fields = getTable(tableId)?.fields ?? [];
|
||||||
const prevField = getField(tableId, fieldId);
|
const prevField = getField(tableId, fieldId);
|
||||||
setTables((tables) =>
|
setTables((tables) =>
|
||||||
tables.map((table) =>
|
tables.map((table) => {
|
||||||
table.id === tableId
|
if (table.id === tableId) {
|
||||||
? {
|
return updateTableFn(table);
|
||||||
...table,
|
}
|
||||||
fields: table.fields.filter(
|
|
||||||
(f) => f.id !== fieldId
|
return table;
|
||||||
),
|
})
|
||||||
}
|
|
||||||
: table
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
events.emit({
|
events.emit({
|
||||||
@@ -768,8 +767,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
db.updateTable({
|
db.updateTable({
|
||||||
id: tableId,
|
id: tableId,
|
||||||
attributes: {
|
attributes: {
|
||||||
...table,
|
...updateTableFn(table),
|
||||||
fields: table.fields.filter((f) => f.id !== fieldId),
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
@@ -1125,12 +1123,15 @@ export const ChartDBProvider: React.FC<
|
|||||||
|
|
||||||
const sourceFieldName = sourceField?.name ?? '';
|
const sourceFieldName = sourceField?.name ?? '';
|
||||||
|
|
||||||
|
const targetTable = getTable(targetTableId);
|
||||||
|
const targetTableSchema = targetTable?.schema;
|
||||||
|
|
||||||
const relationship: DBRelationship = {
|
const relationship: DBRelationship = {
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
name: `${sourceTableName}_${sourceFieldName}_fk`,
|
name: `${sourceTableName}_${sourceFieldName}_fk`,
|
||||||
sourceSchema: sourceTable?.schema,
|
sourceSchema: sourceTable?.schema,
|
||||||
sourceTableId,
|
sourceTableId,
|
||||||
targetSchema: sourceTable?.schema,
|
targetSchema: targetTableSchema,
|
||||||
targetTableId,
|
targetTableId,
|
||||||
sourceFieldId,
|
sourceFieldId,
|
||||||
targetFieldId,
|
targetFieldId,
|
||||||
@@ -1452,7 +1453,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
y: 0,
|
y: 0,
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 200,
|
height: 200,
|
||||||
color: randomColor(),
|
color: defaultAreaColor,
|
||||||
...attributes,
|
...attributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1535,6 +1536,130 @@ export const ChartDBProvider: React.FC<
|
|||||||
[db, diagramId, setAreas, getArea, addUndoAction, resetRedoStack]
|
[db, diagramId, setAreas, getArea, addUndoAction, resetRedoStack]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Note operations
|
||||||
|
const addNotes: ChartDBContext['addNotes'] = useCallback(
|
||||||
|
async (notes: Note[], options = { updateHistory: true }) => {
|
||||||
|
setNotes((currentNotes) => [...currentNotes, ...notes]);
|
||||||
|
|
||||||
|
const updatedAt = new Date();
|
||||||
|
setDiagramUpdatedAt(updatedAt);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
...notes.map((note) => db.addNote({ diagramId, note })),
|
||||||
|
db.updateDiagram({ id: diagramId, attributes: { updatedAt } }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (options.updateHistory) {
|
||||||
|
addUndoAction({
|
||||||
|
action: 'addNotes',
|
||||||
|
redoData: { notes },
|
||||||
|
undoData: { noteIds: notes.map((n) => n.id) },
|
||||||
|
});
|
||||||
|
resetRedoStack();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[db, diagramId, setNotes, addUndoAction, resetRedoStack]
|
||||||
|
);
|
||||||
|
|
||||||
|
const addNote: ChartDBContext['addNote'] = useCallback(
|
||||||
|
async (note: Note, options = { updateHistory: true }) => {
|
||||||
|
return addNotes([note], options);
|
||||||
|
},
|
||||||
|
[addNotes]
|
||||||
|
);
|
||||||
|
|
||||||
|
const createNote: ChartDBContext['createNote'] = useCallback(
|
||||||
|
async (attributes) => {
|
||||||
|
const note: Note = {
|
||||||
|
id: generateId(),
|
||||||
|
content: '',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 200,
|
||||||
|
height: 150,
|
||||||
|
color: '#ffe374', // Default warm yellow
|
||||||
|
...attributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
await addNote(note);
|
||||||
|
|
||||||
|
return note;
|
||||||
|
},
|
||||||
|
[addNote]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getNote: ChartDBContext['getNote'] = useCallback(
|
||||||
|
(id: string) => notes.find((note) => note.id === id) ?? null,
|
||||||
|
[notes]
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeNotes: ChartDBContext['removeNotes'] = useCallback(
|
||||||
|
async (ids: string[], options = { updateHistory: true }) => {
|
||||||
|
const prevNotes = [
|
||||||
|
...notes.filter((note) => ids.includes(note.id)),
|
||||||
|
];
|
||||||
|
|
||||||
|
setNotes((notes) => notes.filter((note) => !ids.includes(note.id)));
|
||||||
|
|
||||||
|
const updatedAt = new Date();
|
||||||
|
setDiagramUpdatedAt(updatedAt);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
...ids.map((id) => db.deleteNote({ diagramId, id })),
|
||||||
|
db.updateDiagram({ id: diagramId, attributes: { updatedAt } }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (prevNotes.length > 0 && options.updateHistory) {
|
||||||
|
addUndoAction({
|
||||||
|
action: 'removeNotes',
|
||||||
|
redoData: { noteIds: ids },
|
||||||
|
undoData: { notes: prevNotes },
|
||||||
|
});
|
||||||
|
resetRedoStack();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[db, diagramId, setNotes, notes, addUndoAction, resetRedoStack]
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeNote: ChartDBContext['removeNote'] = useCallback(
|
||||||
|
async (id: string, options = { updateHistory: true }) => {
|
||||||
|
return removeNotes([id], options);
|
||||||
|
},
|
||||||
|
[removeNotes]
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateNote: ChartDBContext['updateNote'] = useCallback(
|
||||||
|
async (
|
||||||
|
id: string,
|
||||||
|
note: Partial<Note>,
|
||||||
|
options = { updateHistory: true }
|
||||||
|
) => {
|
||||||
|
const prevNote = getNote(id);
|
||||||
|
|
||||||
|
setNotes((notes) =>
|
||||||
|
notes.map((n) => (n.id === id ? { ...n, ...note } : n))
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedAt = new Date();
|
||||||
|
setDiagramUpdatedAt(updatedAt);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
db.updateDiagram({ id: diagramId, attributes: { updatedAt } }),
|
||||||
|
db.updateNote({ id, attributes: note }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!!prevNote && options.updateHistory) {
|
||||||
|
addUndoAction({
|
||||||
|
action: 'updateNote',
|
||||||
|
redoData: { noteId: id, note },
|
||||||
|
undoData: { noteId: id, note: prevNote },
|
||||||
|
});
|
||||||
|
resetRedoStack();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[db, diagramId, setNotes, getNote, addUndoAction, resetRedoStack]
|
||||||
|
);
|
||||||
|
|
||||||
const highlightCustomTypeId = useCallback(
|
const highlightCustomTypeId = useCallback(
|
||||||
(id?: string) => setHighlightedCustomTypeId(id),
|
(id?: string) => setHighlightedCustomTypeId(id),
|
||||||
[setHighlightedCustomTypeId]
|
[setHighlightedCustomTypeId]
|
||||||
@@ -1561,6 +1686,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
setDiagramCreatedAt(diagram.createdAt);
|
setDiagramCreatedAt(diagram.createdAt);
|
||||||
setDiagramUpdatedAt(diagram.updatedAt);
|
setDiagramUpdatedAt(diagram.updatedAt);
|
||||||
setHighlightedCustomTypeId(undefined);
|
setHighlightedCustomTypeId(undefined);
|
||||||
|
setNotes(diagram.notes ?? []);
|
||||||
|
|
||||||
events.emit({ action: 'load_diagram', data: { diagram } });
|
events.emit({ action: 'load_diagram', data: { diagram } });
|
||||||
|
|
||||||
@@ -1581,6 +1707,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
setDiagramUpdatedAt,
|
setDiagramUpdatedAt,
|
||||||
setHighlightedCustomTypeId,
|
setHighlightedCustomTypeId,
|
||||||
events,
|
events,
|
||||||
|
setNotes,
|
||||||
resetRedoStack,
|
resetRedoStack,
|
||||||
resetUndoStack,
|
resetUndoStack,
|
||||||
]
|
]
|
||||||
@@ -1588,22 +1715,23 @@ export const ChartDBProvider: React.FC<
|
|||||||
|
|
||||||
const updateDiagramData: ChartDBContext['updateDiagramData'] = useCallback(
|
const updateDiagramData: ChartDBContext['updateDiagramData'] = useCallback(
|
||||||
async (diagram, options) => {
|
async (diagram, options) => {
|
||||||
const st = options?.forceUpdateStorage ? dbStorage : db;
|
const st = options?.forceUpdateStorage ? storageDB : db;
|
||||||
await st.deleteDiagram(diagram.id);
|
await st.deleteDiagram(diagram.id);
|
||||||
await st.addDiagram({ diagram });
|
await st.addDiagram({ diagram });
|
||||||
loadDiagramFromData(diagram);
|
loadDiagramFromData(diagram);
|
||||||
},
|
},
|
||||||
[db, dbStorage, loadDiagramFromData]
|
[db, storageDB, loadDiagramFromData]
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadDiagram: ChartDBContext['loadDiagram'] = useCallback(
|
const loadDiagram: ChartDBContext['loadDiagram'] = useCallback(
|
||||||
async (diagramId: string) => {
|
async (diagramId: string) => {
|
||||||
const diagram = await db.getDiagram(diagramId, {
|
const diagram = await storageDB.getDiagram(diagramId, {
|
||||||
includeRelationships: true,
|
includeRelationships: true,
|
||||||
includeTables: true,
|
includeTables: true,
|
||||||
includeDependencies: true,
|
includeDependencies: true,
|
||||||
includeAreas: true,
|
includeAreas: true,
|
||||||
includeCustomTypes: true,
|
includeCustomTypes: true,
|
||||||
|
includeNotes: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (diagram) {
|
if (diagram) {
|
||||||
@@ -1612,7 +1740,7 @@ export const ChartDBProvider: React.FC<
|
|||||||
|
|
||||||
return diagram;
|
return diagram;
|
||||||
},
|
},
|
||||||
[db, loadDiagramFromData]
|
[storageDB, loadDiagramFromData]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Custom type operations
|
// Custom type operations
|
||||||
@@ -1759,29 +1887,6 @@ export const ChartDBProvider: React.FC<
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const addHiddenTableId: ChartDBContext['addHiddenTableId'] = useCallback(
|
|
||||||
async (tableId: string) => {
|
|
||||||
if (!hiddenTableIds.includes(tableId)) {
|
|
||||||
setHiddenTableIds((prev) => [...prev, tableId]);
|
|
||||||
await hideTableForDiagram(diagramId, tableId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[hiddenTableIds, diagramId, hideTableForDiagram]
|
|
||||||
);
|
|
||||||
|
|
||||||
const removeHiddenTableId: ChartDBContext['removeHiddenTableId'] =
|
|
||||||
useCallback(
|
|
||||||
async (tableId: string) => {
|
|
||||||
if (hiddenTableIds.includes(tableId)) {
|
|
||||||
setHiddenTableIds((prev) =>
|
|
||||||
prev.filter((id) => id !== tableId)
|
|
||||||
);
|
|
||||||
await unhideTableForDiagram(diagramId, tableId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[hiddenTableIds, diagramId, unhideTableForDiagram]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<chartDBContext.Provider
|
<chartDBContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -1792,12 +1897,11 @@ export const ChartDBProvider: React.FC<
|
|||||||
relationships,
|
relationships,
|
||||||
dependencies,
|
dependencies,
|
||||||
areas,
|
areas,
|
||||||
|
notes,
|
||||||
currentDiagram,
|
currentDiagram,
|
||||||
schemas,
|
schemas,
|
||||||
filteredSchemas,
|
|
||||||
events,
|
events,
|
||||||
readonly,
|
readonly,
|
||||||
filterSchemas,
|
|
||||||
updateDiagramData,
|
updateDiagramData,
|
||||||
updateDiagramId,
|
updateDiagramId,
|
||||||
updateDiagramName,
|
updateDiagramName,
|
||||||
@@ -1855,11 +1959,15 @@ export const ChartDBProvider: React.FC<
|
|||||||
removeCustomType,
|
removeCustomType,
|
||||||
removeCustomTypes,
|
removeCustomTypes,
|
||||||
updateCustomType,
|
updateCustomType,
|
||||||
hiddenTableIds,
|
|
||||||
addHiddenTableId,
|
|
||||||
removeHiddenTableId,
|
|
||||||
highlightCustomTypeId,
|
highlightCustomTypeId,
|
||||||
highlightedCustomType,
|
highlightedCustomType,
|
||||||
|
createNote,
|
||||||
|
addNote,
|
||||||
|
addNotes,
|
||||||
|
getNote,
|
||||||
|
removeNote,
|
||||||
|
removeNotes,
|
||||||
|
updateNote,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -8,23 +8,9 @@ export interface ConfigContext {
|
|||||||
config?: Partial<ChartDBConfig>;
|
config?: Partial<ChartDBConfig>;
|
||||||
updateFn?: (config: ChartDBConfig) => ChartDBConfig;
|
updateFn?: (config: ChartDBConfig) => ChartDBConfig;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
getHiddenTablesForDiagram: (diagramId: string) => string[];
|
|
||||||
setHiddenTablesForDiagram: (
|
|
||||||
diagramId: string,
|
|
||||||
hiddenTableIds: string[]
|
|
||||||
) => Promise<void>;
|
|
||||||
hideTableForDiagram: (diagramId: string, tableId: string) => Promise<void>;
|
|
||||||
unhideTableForDiagram: (
|
|
||||||
diagramId: string,
|
|
||||||
tableId: string
|
|
||||||
) => Promise<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConfigContext = createContext<ConfigContext>({
|
export const ConfigContext = createContext<ConfigContext>({
|
||||||
config: undefined,
|
config: undefined,
|
||||||
updateConfig: emptyFn,
|
updateConfig: emptyFn,
|
||||||
getHiddenTablesForDiagram: () => [],
|
|
||||||
setHiddenTablesForDiagram: emptyFn,
|
|
||||||
hideTableForDiagram: emptyFn,
|
|
||||||
unhideTableForDiagram: emptyFn,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { ConfigContext } from './config-context';
|
import { ConfigContext } from './config-context';
|
||||||
|
|
||||||
import { useStorage } from '@/hooks/use-storage';
|
import { useStorage } from '@/hooks/use-storage';
|
||||||
@@ -8,7 +8,7 @@ export const ConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const { getConfig, updateConfig: updateDataConfig } = useStorage();
|
const { getConfig, updateConfig: updateDataConfig } = useStorage();
|
||||||
const [config, setConfig] = React.useState<ChartDBConfig | undefined>();
|
const [config, setConfig] = useState<ChartDBConfig | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadConfig = async () => {
|
const loadConfig = async () => {
|
||||||
@@ -44,84 +44,11 @@ export const ConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHiddenTablesForDiagram = (diagramId: string): string[] => {
|
|
||||||
return config?.hiddenTablesByDiagram?.[diagramId] ?? [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const setHiddenTablesForDiagram = async (
|
|
||||||
diagramId: string,
|
|
||||||
hiddenTableIds: string[]
|
|
||||||
): Promise<void> => {
|
|
||||||
return updateConfig({
|
|
||||||
updateFn: (currentConfig) => ({
|
|
||||||
...currentConfig,
|
|
||||||
hiddenTablesByDiagram: {
|
|
||||||
...currentConfig.hiddenTablesByDiagram,
|
|
||||||
[diagramId]: hiddenTableIds,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const hideTableForDiagram = async (
|
|
||||||
diagramId: string,
|
|
||||||
tableId: string
|
|
||||||
): Promise<void> => {
|
|
||||||
return updateConfig({
|
|
||||||
updateFn: (currentConfig) => {
|
|
||||||
const currentHiddenTables =
|
|
||||||
currentConfig.hiddenTablesByDiagram?.[diagramId] ?? [];
|
|
||||||
if (currentHiddenTables.includes(tableId)) {
|
|
||||||
return currentConfig; // Already hidden, no change needed
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...currentConfig,
|
|
||||||
hiddenTablesByDiagram: {
|
|
||||||
...currentConfig.hiddenTablesByDiagram,
|
|
||||||
[diagramId]: [...currentHiddenTables, tableId],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const unhideTableForDiagram = async (
|
|
||||||
diagramId: string,
|
|
||||||
tableId: string
|
|
||||||
): Promise<void> => {
|
|
||||||
return updateConfig({
|
|
||||||
updateFn: (currentConfig) => {
|
|
||||||
const currentHiddenTables =
|
|
||||||
currentConfig.hiddenTablesByDiagram?.[diagramId] ?? [];
|
|
||||||
const filteredTables = currentHiddenTables.filter(
|
|
||||||
(id) => id !== tableId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (filteredTables.length === currentHiddenTables.length) {
|
|
||||||
return currentConfig; // Not hidden, no change needed
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...currentConfig,
|
|
||||||
hiddenTablesByDiagram: {
|
|
||||||
...currentConfig.hiddenTablesByDiagram,
|
|
||||||
[diagramId]: filteredTables,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigContext.Provider
|
<ConfigContext.Provider
|
||||||
value={{
|
value={{
|
||||||
config,
|
config,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
getHiddenTablesForDiagram,
|
|
||||||
setHiddenTablesForDiagram,
|
|
||||||
hideTableForDiagram,
|
|
||||||
unhideTableForDiagram,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -7,7 +7,6 @@ import type { ExportImageDialogProps } from '@/dialogs/export-image-dialog/expor
|
|||||||
import type { ExportDiagramDialogProps } from '@/dialogs/export-diagram-dialog/export-diagram-dialog';
|
import type { ExportDiagramDialogProps } from '@/dialogs/export-diagram-dialog/export-diagram-dialog';
|
||||||
import type { ImportDiagramDialogProps } from '@/dialogs/import-diagram-dialog/import-diagram-dialog';
|
import type { ImportDiagramDialogProps } from '@/dialogs/import-diagram-dialog/import-diagram-dialog';
|
||||||
import type { CreateRelationshipDialogProps } from '@/dialogs/create-relationship-dialog/create-relationship-dialog';
|
import type { CreateRelationshipDialogProps } from '@/dialogs/create-relationship-dialog/create-relationship-dialog';
|
||||||
import type { ImportDBMLDialogProps } from '@/dialogs/import-dbml-dialog/import-dbml-dialog';
|
|
||||||
import type { OpenDiagramDialogProps } from '@/dialogs/open-diagram-dialog/open-diagram-dialog';
|
import type { OpenDiagramDialogProps } from '@/dialogs/open-diagram-dialog/open-diagram-dialog';
|
||||||
import type { CreateDiagramDialogProps } from '@/dialogs/create-diagram-dialog/create-diagram-dialog';
|
import type { CreateDiagramDialogProps } from '@/dialogs/create-diagram-dialog/create-diagram-dialog';
|
||||||
|
|
||||||
@@ -67,12 +66,6 @@ export interface DialogContext {
|
|||||||
params: Omit<ImportDiagramDialogProps, 'dialog'>
|
params: Omit<ImportDiagramDialogProps, 'dialog'>
|
||||||
) => void;
|
) => void;
|
||||||
closeImportDiagramDialog: () => void;
|
closeImportDiagramDialog: () => void;
|
||||||
|
|
||||||
// Import DBML dialog
|
|
||||||
openImportDBMLDialog: (
|
|
||||||
params?: Omit<ImportDBMLDialogProps, 'dialog'>
|
|
||||||
) => void;
|
|
||||||
closeImportDBMLDialog: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const dialogContext = createContext<DialogContext>({
|
export const dialogContext = createContext<DialogContext>({
|
||||||
@@ -96,6 +89,4 @@ export const dialogContext = createContext<DialogContext>({
|
|||||||
closeExportDiagramDialog: emptyFn,
|
closeExportDiagramDialog: emptyFn,
|
||||||
openImportDiagramDialog: emptyFn,
|
openImportDiagramDialog: emptyFn,
|
||||||
closeImportDiagramDialog: emptyFn,
|
closeImportDiagramDialog: emptyFn,
|
||||||
openImportDBMLDialog: emptyFn,
|
|
||||||
closeImportDBMLDialog: emptyFn,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ import type { ExportImageDialogProps } from '@/dialogs/export-image-dialog/expor
|
|||||||
import { ExportImageDialog } from '@/dialogs/export-image-dialog/export-image-dialog';
|
import { ExportImageDialog } from '@/dialogs/export-image-dialog/export-image-dialog';
|
||||||
import { ExportDiagramDialog } from '@/dialogs/export-diagram-dialog/export-diagram-dialog';
|
import { ExportDiagramDialog } from '@/dialogs/export-diagram-dialog/export-diagram-dialog';
|
||||||
import { ImportDiagramDialog } from '@/dialogs/import-diagram-dialog/import-diagram-dialog';
|
import { ImportDiagramDialog } from '@/dialogs/import-diagram-dialog/import-diagram-dialog';
|
||||||
import type { ImportDBMLDialogProps } from '@/dialogs/import-dbml-dialog/import-dbml-dialog';
|
|
||||||
import { ImportDBMLDialog } from '@/dialogs/import-dbml-dialog/import-dbml-dialog';
|
|
||||||
|
|
||||||
export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
||||||
children,
|
children,
|
||||||
@@ -132,11 +130,6 @@ export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const [openImportDiagramDialog, setOpenImportDiagramDialog] =
|
const [openImportDiagramDialog, setOpenImportDiagramDialog] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
// Import DBML dialog
|
|
||||||
const [openImportDBMLDialog, setOpenImportDBMLDialog] = useState(false);
|
|
||||||
const [importDBMLDialogParams, setImportDBMLDialogParams] =
|
|
||||||
useState<Omit<ImportDBMLDialogProps, 'dialog'>>();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<dialogContext.Provider
|
<dialogContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -165,11 +158,6 @@ export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
openImportDiagramDialog: () => setOpenImportDiagramDialog(true),
|
openImportDiagramDialog: () => setOpenImportDiagramDialog(true),
|
||||||
closeImportDiagramDialog: () =>
|
closeImportDiagramDialog: () =>
|
||||||
setOpenImportDiagramDialog(false),
|
setOpenImportDiagramDialog(false),
|
||||||
openImportDBMLDialog: (params) => {
|
|
||||||
setImportDBMLDialogParams(params);
|
|
||||||
setOpenImportDBMLDialog(true);
|
|
||||||
},
|
|
||||||
closeImportDBMLDialog: () => setOpenImportDBMLDialog(false),
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -204,10 +192,6 @@ export const DialogProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
/>
|
/>
|
||||||
<ExportDiagramDialog dialog={{ open: openExportDiagramDialog }} />
|
<ExportDiagramDialog dialog={{ open: openExportDiagramDialog }} />
|
||||||
<ImportDiagramDialog dialog={{ open: openImportDiagramDialog }} />
|
<ImportDiagramDialog dialog={{ open: openImportDiagramDialog }} />
|
||||||
<ImportDBMLDialog
|
|
||||||
dialog={{ open: openImportDBMLDialog }}
|
|
||||||
{...importDBMLDialogParams}
|
|
||||||
/>
|
|
||||||
</dialogContext.Provider>
|
</dialogContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ export type DiffEventBase<T extends DiffEventType, D> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type DiffCalculatedData = {
|
export type DiffCalculatedData = {
|
||||||
tablesAdded: DBTable[];
|
tablesToAdd: DBTable[];
|
||||||
fieldsAdded: Map<string, DBField[]>;
|
fieldsToAdd: Map<string, DBField[]>;
|
||||||
relationshipsAdded: DBRelationship[];
|
relationshipsToAdd: DBRelationship[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DiffCalculatedEvent = DiffEventBase<
|
export type DiffCalculatedEvent = DiffEventBase<
|
||||||
@@ -44,15 +44,21 @@ export interface DiffContext {
|
|||||||
options?: {
|
options?: {
|
||||||
summaryOnly?: boolean;
|
summaryOnly?: boolean;
|
||||||
};
|
};
|
||||||
}) => void;
|
}) => { foundDiff: boolean };
|
||||||
resetDiff: () => void;
|
resetDiff: () => void;
|
||||||
|
|
||||||
// table diff
|
// table diff
|
||||||
checkIfTableHasChange: ({ tableId }: { tableId: string }) => boolean;
|
checkIfTableHasChange: ({ tableId }: { tableId: string }) => boolean;
|
||||||
checkIfNewTable: ({ tableId }: { tableId: string }) => boolean;
|
checkIfNewTable: ({ tableId }: { tableId: string }) => boolean;
|
||||||
checkIfTableRemoved: ({ tableId }: { tableId: string }) => boolean;
|
checkIfTableRemoved: ({ tableId }: { tableId: string }) => boolean;
|
||||||
getTableNewName: ({ tableId }: { tableId: string }) => string | null;
|
getTableNewName: ({ tableId }: { tableId: string }) => {
|
||||||
getTableNewColor: ({ tableId }: { tableId: string }) => string | null;
|
old: string;
|
||||||
|
new: string;
|
||||||
|
} | null;
|
||||||
|
getTableNewColor: ({ tableId }: { tableId: string }) => {
|
||||||
|
old: string;
|
||||||
|
new: string;
|
||||||
|
} | null;
|
||||||
|
|
||||||
// field diff
|
// field diff
|
||||||
checkIfFieldHasChange: ({
|
checkIfFieldHasChange: ({
|
||||||
@@ -64,17 +70,46 @@ export interface DiffContext {
|
|||||||
}) => boolean;
|
}) => boolean;
|
||||||
checkIfFieldRemoved: ({ fieldId }: { fieldId: string }) => boolean;
|
checkIfFieldRemoved: ({ fieldId }: { fieldId: string }) => boolean;
|
||||||
checkIfNewField: ({ fieldId }: { fieldId: string }) => boolean;
|
checkIfNewField: ({ fieldId }: { fieldId: string }) => boolean;
|
||||||
getFieldNewName: ({ fieldId }: { fieldId: string }) => string | null;
|
getFieldNewName: ({
|
||||||
getFieldNewType: ({ fieldId }: { fieldId: string }) => DataType | null;
|
fieldId,
|
||||||
getFieldNewPrimaryKey: ({ fieldId }: { fieldId: string }) => boolean | null;
|
}: {
|
||||||
getFieldNewNullable: ({ fieldId }: { fieldId: string }) => boolean | null;
|
fieldId: string;
|
||||||
|
}) => { old: string; new: string } | null;
|
||||||
|
getFieldNewType: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: DataType; new: DataType } | null;
|
||||||
|
getFieldNewPrimaryKey: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: boolean; new: boolean } | null;
|
||||||
|
getFieldNewNullable: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: boolean; new: boolean } | null;
|
||||||
getFieldNewCharacterMaximumLength: ({
|
getFieldNewCharacterMaximumLength: ({
|
||||||
fieldId,
|
fieldId,
|
||||||
}: {
|
}: {
|
||||||
fieldId: string;
|
fieldId: string;
|
||||||
}) => string | null;
|
}) => { old: string; new: string } | null;
|
||||||
getFieldNewScale: ({ fieldId }: { fieldId: string }) => number | null;
|
getFieldNewScale: ({
|
||||||
getFieldNewPrecision: ({ fieldId }: { fieldId: string }) => number | null;
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: number; new: number } | null;
|
||||||
|
getFieldNewPrecision: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: number; new: number } | null;
|
||||||
|
getFieldNewIsArray: ({
|
||||||
|
fieldId,
|
||||||
|
}: {
|
||||||
|
fieldId: string;
|
||||||
|
}) => { old: boolean; new: boolean } | null;
|
||||||
|
|
||||||
// relationship diff
|
// relationship diff
|
||||||
checkIfNewRelationship: ({
|
checkIfNewRelationship: ({
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
|
|
||||||
const events = useEventEmitter<DiffEvent>();
|
const events = useEventEmitter<DiffEvent>();
|
||||||
|
|
||||||
const generateNewFieldsMap = useCallback(
|
const generateFieldsToAddMap = useCallback(
|
||||||
({
|
({
|
||||||
diffMap,
|
diffMap,
|
||||||
newDiagram,
|
newDiagram,
|
||||||
@@ -66,7 +66,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const findNewRelationships = useCallback(
|
const findRelationshipsToAdd = useCallback(
|
||||||
({
|
({
|
||||||
diffMap,
|
diffMap,
|
||||||
newDiagram,
|
newDiagram,
|
||||||
@@ -101,7 +101,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
diffMap: DiffMap;
|
diffMap: DiffMap;
|
||||||
}): DiffCalculatedData => {
|
}): DiffCalculatedData => {
|
||||||
return {
|
return {
|
||||||
tablesAdded:
|
tablesToAdd:
|
||||||
newDiagram?.tables?.filter((table) => {
|
newDiagram?.tables?.filter((table) => {
|
||||||
const tableKey = getDiffMapKey({
|
const tableKey = getDiffMapKey({
|
||||||
diffObject: 'table',
|
diffObject: 'table',
|
||||||
@@ -114,17 +114,17 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
);
|
);
|
||||||
}) ?? [],
|
}) ?? [],
|
||||||
|
|
||||||
fieldsAdded: generateNewFieldsMap({
|
fieldsToAdd: generateFieldsToAddMap({
|
||||||
diffMap: diffMap,
|
diffMap: diffMap,
|
||||||
newDiagram: newDiagram,
|
newDiagram: newDiagram,
|
||||||
}),
|
}),
|
||||||
relationshipsAdded: findNewRelationships({
|
relationshipsToAdd: findRelationshipsToAdd({
|
||||||
diffMap: diffMap,
|
diffMap: diffMap,
|
||||||
newDiagram: newDiagram,
|
newDiagram: newDiagram,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[findNewRelationships, generateNewFieldsMap]
|
[findRelationshipsToAdd, generateFieldsToAddMap]
|
||||||
);
|
);
|
||||||
|
|
||||||
const calculateDiff: DiffContext['calculateDiff'] = useCallback(
|
const calculateDiff: DiffContext['calculateDiff'] = useCallback(
|
||||||
@@ -149,6 +149,8 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
newDiagram: newDiagramArg,
|
newDiagram: newDiagramArg,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return { foundDiff: !!newDiffs.size };
|
||||||
},
|
},
|
||||||
[setDiffMap, events, generateDiffCalculatedData]
|
[setDiffMap, events, generateDiffCalculatedData]
|
||||||
);
|
);
|
||||||
@@ -165,7 +167,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(tableNameKey);
|
const diff = diffMap.get(tableNameKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as string;
|
return {
|
||||||
|
new: diff.newValue as string,
|
||||||
|
old: diff.oldValue as string,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +191,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(tableColorKey);
|
const diff = diffMap.get(tableColorKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as string;
|
return {
|
||||||
|
new: diff.newValue as string,
|
||||||
|
old: diff.oldValue as string,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -277,7 +285,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as string;
|
return {
|
||||||
|
old: diff.oldValue as string,
|
||||||
|
new: diff.newValue as string,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +309,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as DataType;
|
return {
|
||||||
|
old: diff.oldValue as DataType,
|
||||||
|
new: diff.newValue as DataType,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +335,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as boolean;
|
return {
|
||||||
|
old: diff.oldValue as boolean,
|
||||||
|
new: diff.newValue as boolean,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +359,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as boolean;
|
return {
|
||||||
|
old: diff.oldValue as boolean,
|
||||||
|
new: diff.newValue as boolean,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +385,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as string;
|
return {
|
||||||
|
old: diff.oldValue as string,
|
||||||
|
new: diff.newValue as string,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,7 +409,10 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as number;
|
return {
|
||||||
|
old: diff.oldValue as number,
|
||||||
|
new: diff.newValue as number,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +435,34 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const diff = diffMap.get(fieldKey);
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
if (diff?.type === 'changed') {
|
if (diff?.type === 'changed') {
|
||||||
return diff.newValue as number;
|
return {
|
||||||
|
old: diff.oldValue as number,
|
||||||
|
new: diff.newValue as number,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
[diffMap]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getFieldNewIsArray = useCallback<DiffContext['getFieldNewIsArray']>(
|
||||||
|
({ fieldId }) => {
|
||||||
|
const fieldKey = getDiffMapKey({
|
||||||
|
diffObject: 'field',
|
||||||
|
objectId: fieldId,
|
||||||
|
attribute: 'isArray',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (diffMap.has(fieldKey)) {
|
||||||
|
const diff = diffMap.get(fieldKey);
|
||||||
|
|
||||||
|
if (diff?.type === 'changed') {
|
||||||
|
return {
|
||||||
|
old: diff.oldValue as boolean,
|
||||||
|
new: diff.newValue as boolean,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,6 +544,7 @@ export const DiffProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
getFieldNewCharacterMaximumLength,
|
getFieldNewCharacterMaximumLength,
|
||||||
getFieldNewScale,
|
getFieldNewScale,
|
||||||
getFieldNewPrecision,
|
getFieldNewPrecision,
|
||||||
|
getFieldNewIsArray,
|
||||||
|
|
||||||
// relationship diff
|
// relationship diff
|
||||||
checkIfNewRelationship,
|
checkIfNewRelationship,
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ export const HistoryProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
addCustomTypes,
|
addCustomTypes,
|
||||||
removeCustomTypes,
|
removeCustomTypes,
|
||||||
updateCustomType,
|
updateCustomType,
|
||||||
|
addNotes,
|
||||||
|
removeNotes,
|
||||||
|
updateNote,
|
||||||
} = useChartDB();
|
} = useChartDB();
|
||||||
|
|
||||||
const redoActionHandlers = useMemo(
|
const redoActionHandlers = useMemo(
|
||||||
@@ -135,6 +138,15 @@ export const HistoryProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
updateHistory: false,
|
updateHistory: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
addNotes: ({ redoData: { notes } }) => {
|
||||||
|
return addNotes(notes, { updateHistory: false });
|
||||||
|
},
|
||||||
|
removeNotes: ({ redoData: { noteIds } }) => {
|
||||||
|
return removeNotes(noteIds, { updateHistory: false });
|
||||||
|
},
|
||||||
|
updateNote: ({ redoData: { noteId, note } }) => {
|
||||||
|
return updateNote(noteId, note, { updateHistory: false });
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
addTables,
|
addTables,
|
||||||
@@ -160,6 +172,9 @@ export const HistoryProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
addCustomTypes,
|
addCustomTypes,
|
||||||
removeCustomTypes,
|
removeCustomTypes,
|
||||||
updateCustomType,
|
updateCustomType,
|
||||||
|
addNotes,
|
||||||
|
removeNotes,
|
||||||
|
updateNote,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -271,6 +286,15 @@ export const HistoryProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
updateHistory: false,
|
updateHistory: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
addNotes: ({ undoData: { noteIds } }) => {
|
||||||
|
return removeNotes(noteIds, { updateHistory: false });
|
||||||
|
},
|
||||||
|
removeNotes: ({ undoData: { notes } }) => {
|
||||||
|
return addNotes(notes, { updateHistory: false });
|
||||||
|
},
|
||||||
|
updateNote: ({ undoData: { noteId, note } }) => {
|
||||||
|
return updateNote(noteId, note, { updateHistory: false });
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
addTables,
|
addTables,
|
||||||
@@ -296,6 +320,9 @@ export const HistoryProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
addCustomTypes,
|
addCustomTypes,
|
||||||
removeCustomTypes,
|
removeCustomTypes,
|
||||||
updateCustomType,
|
updateCustomType,
|
||||||
|
addNotes,
|
||||||
|
removeNotes,
|
||||||
|
updateNote,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { DBRelationship } from '@/lib/domain/db-relationship';
|
|||||||
import type { DBDependency } from '@/lib/domain/db-dependency';
|
import type { DBDependency } from '@/lib/domain/db-dependency';
|
||||||
import type { Area } from '@/lib/domain/area';
|
import type { Area } from '@/lib/domain/area';
|
||||||
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
||||||
|
import type { Note } from '@/lib/domain/note';
|
||||||
|
|
||||||
type Action = keyof ChartDBContext;
|
type Action = keyof ChartDBContext;
|
||||||
|
|
||||||
@@ -161,6 +162,24 @@ type RedoUndoActionRemoveCustomTypes = RedoUndoActionBase<
|
|||||||
{ customTypes: DBCustomType[] }
|
{ customTypes: DBCustomType[] }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
type RedoUndoActionAddNotes = RedoUndoActionBase<
|
||||||
|
'addNotes',
|
||||||
|
{ notes: Note[] },
|
||||||
|
{ noteIds: string[] }
|
||||||
|
>;
|
||||||
|
|
||||||
|
type RedoUndoActionUpdateNote = RedoUndoActionBase<
|
||||||
|
'updateNote',
|
||||||
|
{ noteId: string; note: Partial<Note> },
|
||||||
|
{ noteId: string; note: Partial<Note> }
|
||||||
|
>;
|
||||||
|
|
||||||
|
type RedoUndoActionRemoveNotes = RedoUndoActionBase<
|
||||||
|
'removeNotes',
|
||||||
|
{ noteIds: string[] },
|
||||||
|
{ notes: Note[] }
|
||||||
|
>;
|
||||||
|
|
||||||
export type RedoUndoAction =
|
export type RedoUndoAction =
|
||||||
| RedoUndoActionAddTables
|
| RedoUndoActionAddTables
|
||||||
| RedoUndoActionRemoveTables
|
| RedoUndoActionRemoveTables
|
||||||
@@ -184,7 +203,10 @@ export type RedoUndoAction =
|
|||||||
| RedoUndoActionRemoveAreas
|
| RedoUndoActionRemoveAreas
|
||||||
| RedoUndoActionAddCustomTypes
|
| RedoUndoActionAddCustomTypes
|
||||||
| RedoUndoActionUpdateCustomType
|
| RedoUndoActionUpdateCustomType
|
||||||
| RedoUndoActionRemoveCustomTypes;
|
| RedoUndoActionRemoveCustomTypes
|
||||||
|
| RedoUndoActionAddNotes
|
||||||
|
| RedoUndoActionUpdateNote
|
||||||
|
| RedoUndoActionRemoveNotes;
|
||||||
|
|
||||||
export type RedoActionData<T extends Action> = Extract<
|
export type RedoActionData<T extends Action> = Extract<
|
||||||
RedoUndoAction,
|
RedoUndoAction,
|
||||||
|
|||||||
@@ -2,29 +2,37 @@ import { emptyFn } from '@/lib/utils';
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
export type SidebarSection =
|
export type SidebarSection =
|
||||||
|
| 'dbml'
|
||||||
| 'tables'
|
| 'tables'
|
||||||
| 'relationships'
|
| 'refs'
|
||||||
| 'dependencies'
|
| 'customTypes'
|
||||||
| 'areas'
|
| 'visuals';
|
||||||
| 'customTypes';
|
|
||||||
|
export type VisualsTab = 'areas' | 'notes';
|
||||||
|
|
||||||
export interface LayoutContext {
|
export interface LayoutContext {
|
||||||
openedTableInSidebar: string | undefined;
|
openedTableInSidebar: string | undefined;
|
||||||
openTableFromSidebar: (tableId: string) => void;
|
openTableFromSidebar: (tableId: string) => void;
|
||||||
closeAllTablesInSidebar: () => void;
|
closeAllTablesInSidebar: () => void;
|
||||||
|
|
||||||
openedRelationshipInSidebar: string | undefined;
|
|
||||||
openRelationshipFromSidebar: (relationshipId: string) => void;
|
openRelationshipFromSidebar: (relationshipId: string) => void;
|
||||||
closeAllRelationshipsInSidebar: () => void;
|
closeAllRelationshipsInSidebar: () => void;
|
||||||
|
|
||||||
openedDependencyInSidebar: string | undefined;
|
|
||||||
openDependencyFromSidebar: (dependencyId: string) => void;
|
openDependencyFromSidebar: (dependencyId: string) => void;
|
||||||
closeAllDependenciesInSidebar: () => void;
|
closeAllDependenciesInSidebar: () => void;
|
||||||
|
|
||||||
|
openedRefInSidebar: string | undefined;
|
||||||
|
openRefFromSidebar: (refId: string) => void;
|
||||||
|
closeAllRefsInSidebar: () => void;
|
||||||
|
|
||||||
openedAreaInSidebar: string | undefined;
|
openedAreaInSidebar: string | undefined;
|
||||||
openAreaFromSidebar: (areaId: string) => void;
|
openAreaFromSidebar: (areaId: string) => void;
|
||||||
closeAllAreasInSidebar: () => void;
|
closeAllAreasInSidebar: () => void;
|
||||||
|
|
||||||
|
openedNoteInSidebar: string | undefined;
|
||||||
|
openNoteFromSidebar: (noteId: string) => void;
|
||||||
|
closeAllNotesInSidebar: () => void;
|
||||||
|
|
||||||
openedCustomTypeInSidebar: string | undefined;
|
openedCustomTypeInSidebar: string | undefined;
|
||||||
openCustomTypeFromSidebar: (customTypeId: string) => void;
|
openCustomTypeFromSidebar: (customTypeId: string) => void;
|
||||||
closeAllCustomTypesInSidebar: () => void;
|
closeAllCustomTypesInSidebar: () => void;
|
||||||
@@ -32,32 +40,37 @@ export interface LayoutContext {
|
|||||||
selectedSidebarSection: SidebarSection;
|
selectedSidebarSection: SidebarSection;
|
||||||
selectSidebarSection: (section: SidebarSection) => void;
|
selectSidebarSection: (section: SidebarSection) => void;
|
||||||
|
|
||||||
|
selectedVisualsTab: VisualsTab;
|
||||||
|
selectVisualsTab: (tab: VisualsTab) => void;
|
||||||
|
|
||||||
isSidePanelShowed: boolean;
|
isSidePanelShowed: boolean;
|
||||||
hideSidePanel: () => void;
|
hideSidePanel: () => void;
|
||||||
showSidePanel: () => void;
|
showSidePanel: () => void;
|
||||||
toggleSidePanel: () => void;
|
toggleSidePanel: () => void;
|
||||||
|
|
||||||
isSelectSchemaOpen: boolean;
|
|
||||||
openSelectSchema: () => void;
|
|
||||||
closeSelectSchema: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const layoutContext = createContext<LayoutContext>({
|
export const layoutContext = createContext<LayoutContext>({
|
||||||
openedTableInSidebar: undefined,
|
openedTableInSidebar: undefined,
|
||||||
selectedSidebarSection: 'tables',
|
selectedSidebarSection: 'tables',
|
||||||
|
|
||||||
openedRelationshipInSidebar: undefined,
|
|
||||||
openRelationshipFromSidebar: emptyFn,
|
openRelationshipFromSidebar: emptyFn,
|
||||||
closeAllRelationshipsInSidebar: emptyFn,
|
closeAllRelationshipsInSidebar: emptyFn,
|
||||||
|
|
||||||
openedDependencyInSidebar: undefined,
|
|
||||||
openDependencyFromSidebar: emptyFn,
|
openDependencyFromSidebar: emptyFn,
|
||||||
closeAllDependenciesInSidebar: emptyFn,
|
closeAllDependenciesInSidebar: emptyFn,
|
||||||
|
|
||||||
|
openedRefInSidebar: undefined,
|
||||||
|
openRefFromSidebar: emptyFn,
|
||||||
|
closeAllRefsInSidebar: emptyFn,
|
||||||
|
|
||||||
openedAreaInSidebar: undefined,
|
openedAreaInSidebar: undefined,
|
||||||
openAreaFromSidebar: emptyFn,
|
openAreaFromSidebar: emptyFn,
|
||||||
closeAllAreasInSidebar: emptyFn,
|
closeAllAreasInSidebar: emptyFn,
|
||||||
|
|
||||||
|
openedNoteInSidebar: undefined,
|
||||||
|
openNoteFromSidebar: emptyFn,
|
||||||
|
closeAllNotesInSidebar: emptyFn,
|
||||||
|
|
||||||
openedCustomTypeInSidebar: undefined,
|
openedCustomTypeInSidebar: undefined,
|
||||||
openCustomTypeFromSidebar: emptyFn,
|
openCustomTypeFromSidebar: emptyFn,
|
||||||
closeAllCustomTypesInSidebar: emptyFn,
|
closeAllCustomTypesInSidebar: emptyFn,
|
||||||
@@ -66,12 +79,11 @@ export const layoutContext = createContext<LayoutContext>({
|
|||||||
openTableFromSidebar: emptyFn,
|
openTableFromSidebar: emptyFn,
|
||||||
closeAllTablesInSidebar: emptyFn,
|
closeAllTablesInSidebar: emptyFn,
|
||||||
|
|
||||||
|
selectedVisualsTab: 'areas',
|
||||||
|
selectVisualsTab: emptyFn,
|
||||||
|
|
||||||
isSidePanelShowed: false,
|
isSidePanelShowed: false,
|
||||||
hideSidePanel: emptyFn,
|
hideSidePanel: emptyFn,
|
||||||
showSidePanel: emptyFn,
|
showSidePanel: emptyFn,
|
||||||
toggleSidePanel: emptyFn,
|
toggleSidePanel: emptyFn,
|
||||||
|
|
||||||
isSelectSchemaOpen: false,
|
|
||||||
openSelectSchema: emptyFn,
|
|
||||||
closeSelectSchema: emptyFn,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { LayoutContext, SidebarSection } from './layout-context';
|
import type {
|
||||||
|
LayoutContext,
|
||||||
|
SidebarSection,
|
||||||
|
VisualsTab,
|
||||||
|
} from './layout-context';
|
||||||
import { layoutContext } from './layout-context';
|
import { layoutContext } from './layout-context';
|
||||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||||
|
|
||||||
@@ -10,34 +14,42 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const [openedTableInSidebar, setOpenedTableInSidebar] = React.useState<
|
const [openedTableInSidebar, setOpenedTableInSidebar] = React.useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
const [openedRelationshipInSidebar, setOpenedRelationshipInSidebar] =
|
const [openedRefInSidebar, setOpenedRefInSidebar] = React.useState<
|
||||||
React.useState<string | undefined>();
|
string | undefined
|
||||||
const [openedDependencyInSidebar, setOpenedDependencyInSidebar] =
|
>();
|
||||||
React.useState<string | undefined>();
|
|
||||||
const [openedAreaInSidebar, setOpenedAreaInSidebar] = React.useState<
|
const [openedAreaInSidebar, setOpenedAreaInSidebar] = React.useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
|
const [openedNoteInSidebar, setOpenedNoteInSidebar] = React.useState<
|
||||||
|
string | undefined
|
||||||
|
>();
|
||||||
const [openedCustomTypeInSidebar, setOpenedCustomTypeInSidebar] =
|
const [openedCustomTypeInSidebar, setOpenedCustomTypeInSidebar] =
|
||||||
React.useState<string | undefined>();
|
React.useState<string | undefined>();
|
||||||
const [selectedSidebarSection, setSelectedSidebarSection] =
|
const [selectedSidebarSection, setSelectedSidebarSection] =
|
||||||
React.useState<SidebarSection>('tables');
|
React.useState<SidebarSection>('tables');
|
||||||
|
const [selectedVisualsTab, setSelectedVisualsTab] =
|
||||||
|
React.useState<VisualsTab>('areas');
|
||||||
const [isSidePanelShowed, setIsSidePanelShowed] =
|
const [isSidePanelShowed, setIsSidePanelShowed] =
|
||||||
React.useState<boolean>(isDesktop);
|
React.useState<boolean>(isDesktop);
|
||||||
const [isSelectSchemaOpen, setIsSelectSchemaOpen] =
|
|
||||||
React.useState<boolean>(false);
|
|
||||||
|
|
||||||
const closeAllTablesInSidebar: LayoutContext['closeAllTablesInSidebar'] =
|
const closeAllTablesInSidebar: LayoutContext['closeAllTablesInSidebar'] =
|
||||||
() => setOpenedTableInSidebar('');
|
() => setOpenedTableInSidebar('');
|
||||||
|
|
||||||
const closeAllRelationshipsInSidebar: LayoutContext['closeAllRelationshipsInSidebar'] =
|
const closeAllRelationshipsInSidebar: LayoutContext['closeAllRelationshipsInSidebar'] =
|
||||||
() => setOpenedRelationshipInSidebar('');
|
() => setOpenedRefInSidebar('');
|
||||||
|
|
||||||
const closeAllDependenciesInSidebar: LayoutContext['closeAllDependenciesInSidebar'] =
|
const closeAllDependenciesInSidebar: LayoutContext['closeAllDependenciesInSidebar'] =
|
||||||
() => setOpenedDependencyInSidebar('');
|
() => setOpenedRefInSidebar('');
|
||||||
|
|
||||||
|
const closeAllRefsInSidebar: LayoutContext['closeAllRefsInSidebar'] = () =>
|
||||||
|
setOpenedRefInSidebar('');
|
||||||
|
|
||||||
const closeAllAreasInSidebar: LayoutContext['closeAllAreasInSidebar'] =
|
const closeAllAreasInSidebar: LayoutContext['closeAllAreasInSidebar'] =
|
||||||
() => setOpenedAreaInSidebar('');
|
() => setOpenedAreaInSidebar('');
|
||||||
|
|
||||||
|
const closeAllNotesInSidebar: LayoutContext['closeAllNotesInSidebar'] =
|
||||||
|
() => setOpenedNoteInSidebar('');
|
||||||
|
|
||||||
const closeAllCustomTypesInSidebar: LayoutContext['closeAllCustomTypesInSidebar'] =
|
const closeAllCustomTypesInSidebar: LayoutContext['closeAllCustomTypesInSidebar'] =
|
||||||
() => setOpenedCustomTypeInSidebar('');
|
() => setOpenedCustomTypeInSidebar('');
|
||||||
|
|
||||||
@@ -62,25 +74,41 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
const openRelationshipFromSidebar: LayoutContext['openRelationshipFromSidebar'] =
|
const openRelationshipFromSidebar: LayoutContext['openRelationshipFromSidebar'] =
|
||||||
(relationshipId) => {
|
(relationshipId) => {
|
||||||
showSidePanel();
|
showSidePanel();
|
||||||
setSelectedSidebarSection('relationships');
|
setSelectedSidebarSection('refs');
|
||||||
setOpenedRelationshipInSidebar(relationshipId);
|
setOpenedRefInSidebar(relationshipId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openDependencyFromSidebar: LayoutContext['openDependencyFromSidebar'] =
|
const openDependencyFromSidebar: LayoutContext['openDependencyFromSidebar'] =
|
||||||
(dependencyId) => {
|
(dependencyId) => {
|
||||||
showSidePanel();
|
showSidePanel();
|
||||||
setSelectedSidebarSection('dependencies');
|
setSelectedSidebarSection('refs');
|
||||||
setOpenedDependencyInSidebar(dependencyId);
|
setOpenedRefInSidebar(dependencyId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openRefFromSidebar: LayoutContext['openRefFromSidebar'] = (refId) => {
|
||||||
|
showSidePanel();
|
||||||
|
setSelectedSidebarSection('refs');
|
||||||
|
setOpenedRefInSidebar(refId);
|
||||||
|
};
|
||||||
|
|
||||||
const openAreaFromSidebar: LayoutContext['openAreaFromSidebar'] = (
|
const openAreaFromSidebar: LayoutContext['openAreaFromSidebar'] = (
|
||||||
areaId
|
areaId
|
||||||
) => {
|
) => {
|
||||||
showSidePanel();
|
showSidePanel();
|
||||||
setSelectedSidebarSection('areas');
|
setSelectedSidebarSection('visuals');
|
||||||
|
setSelectedVisualsTab('areas');
|
||||||
setOpenedAreaInSidebar(areaId);
|
setOpenedAreaInSidebar(areaId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openNoteFromSidebar: LayoutContext['openNoteFromSidebar'] = (
|
||||||
|
noteId
|
||||||
|
) => {
|
||||||
|
showSidePanel();
|
||||||
|
setSelectedSidebarSection('visuals');
|
||||||
|
setSelectedVisualsTab('notes');
|
||||||
|
setOpenedNoteInSidebar(noteId);
|
||||||
|
};
|
||||||
|
|
||||||
const openCustomTypeFromSidebar: LayoutContext['openCustomTypeFromSidebar'] =
|
const openCustomTypeFromSidebar: LayoutContext['openCustomTypeFromSidebar'] =
|
||||||
(customTypeId) => {
|
(customTypeId) => {
|
||||||
showSidePanel();
|
showSidePanel();
|
||||||
@@ -88,11 +116,6 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
setOpenedTableInSidebar(customTypeId);
|
setOpenedTableInSidebar(customTypeId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openSelectSchema: LayoutContext['openSelectSchema'] = () =>
|
|
||||||
setIsSelectSchemaOpen(true);
|
|
||||||
|
|
||||||
const closeSelectSchema: LayoutContext['closeSelectSchema'] = () =>
|
|
||||||
setIsSelectSchemaOpen(false);
|
|
||||||
return (
|
return (
|
||||||
<layoutContext.Provider
|
<layoutContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -100,7 +123,6 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
selectedSidebarSection,
|
selectedSidebarSection,
|
||||||
openTableFromSidebar,
|
openTableFromSidebar,
|
||||||
selectSidebarSection: setSelectedSidebarSection,
|
selectSidebarSection: setSelectedSidebarSection,
|
||||||
openedRelationshipInSidebar,
|
|
||||||
openRelationshipFromSidebar,
|
openRelationshipFromSidebar,
|
||||||
closeAllTablesInSidebar,
|
closeAllTablesInSidebar,
|
||||||
closeAllRelationshipsInSidebar,
|
closeAllRelationshipsInSidebar,
|
||||||
@@ -108,18 +130,22 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
hideSidePanel,
|
hideSidePanel,
|
||||||
showSidePanel,
|
showSidePanel,
|
||||||
toggleSidePanel,
|
toggleSidePanel,
|
||||||
isSelectSchemaOpen,
|
|
||||||
openSelectSchema,
|
|
||||||
closeSelectSchema,
|
|
||||||
openedDependencyInSidebar,
|
|
||||||
openDependencyFromSidebar,
|
openDependencyFromSidebar,
|
||||||
closeAllDependenciesInSidebar,
|
closeAllDependenciesInSidebar,
|
||||||
|
openedRefInSidebar,
|
||||||
|
openRefFromSidebar,
|
||||||
|
closeAllRefsInSidebar,
|
||||||
openedAreaInSidebar,
|
openedAreaInSidebar,
|
||||||
openAreaFromSidebar,
|
openAreaFromSidebar,
|
||||||
closeAllAreasInSidebar,
|
closeAllAreasInSidebar,
|
||||||
|
openedNoteInSidebar,
|
||||||
|
openNoteFromSidebar,
|
||||||
|
closeAllNotesInSidebar,
|
||||||
openedCustomTypeInSidebar,
|
openedCustomTypeInSidebar,
|
||||||
openCustomTypeFromSidebar,
|
openCustomTypeFromSidebar,
|
||||||
closeAllCustomTypesInSidebar,
|
closeAllCustomTypesInSidebar,
|
||||||
|
selectedVisualsTab,
|
||||||
|
selectVisualsTab: setSelectedVisualsTab,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import type { Theme } from '../theme-context/theme-context';
|
|||||||
|
|
||||||
export type ScrollAction = 'pan' | 'zoom';
|
export type ScrollAction = 'pan' | 'zoom';
|
||||||
|
|
||||||
export type SchemasFilter = Record<string, string[]>;
|
|
||||||
|
|
||||||
export interface LocalConfigContext {
|
export interface LocalConfigContext {
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
setTheme: (theme: Theme) => void;
|
setTheme: (theme: Theme) => void;
|
||||||
@@ -13,8 +11,8 @@ export interface LocalConfigContext {
|
|||||||
scrollAction: ScrollAction;
|
scrollAction: ScrollAction;
|
||||||
setScrollAction: (action: ScrollAction) => void;
|
setScrollAction: (action: ScrollAction) => void;
|
||||||
|
|
||||||
schemasFilter: SchemasFilter;
|
showDBViews: boolean;
|
||||||
setSchemasFilter: React.Dispatch<React.SetStateAction<SchemasFilter>>;
|
setShowDBViews: (showViews: boolean) => void;
|
||||||
|
|
||||||
showCardinality: boolean;
|
showCardinality: boolean;
|
||||||
setShowCardinality: (showCardinality: boolean) => void;
|
setShowCardinality: (showCardinality: boolean) => void;
|
||||||
@@ -22,20 +20,12 @@ export interface LocalConfigContext {
|
|||||||
showFieldAttributes: boolean;
|
showFieldAttributes: boolean;
|
||||||
setShowFieldAttributes: (showFieldAttributes: boolean) => void;
|
setShowFieldAttributes: (showFieldAttributes: boolean) => void;
|
||||||
|
|
||||||
hideMultiSchemaNotification: boolean;
|
|
||||||
setHideMultiSchemaNotification: (
|
|
||||||
hideMultiSchemaNotification: boolean
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
githubRepoOpened: boolean;
|
githubRepoOpened: boolean;
|
||||||
setGithubRepoOpened: (githubRepoOpened: boolean) => void;
|
setGithubRepoOpened: (githubRepoOpened: boolean) => void;
|
||||||
|
|
||||||
starUsDialogLastOpen: number;
|
starUsDialogLastOpen: number;
|
||||||
setStarUsDialogLastOpen: (lastOpen: number) => void;
|
setStarUsDialogLastOpen: (lastOpen: number) => void;
|
||||||
|
|
||||||
showDependenciesOnCanvas: boolean;
|
|
||||||
setShowDependenciesOnCanvas: (showDependenciesOnCanvas: boolean) => void;
|
|
||||||
|
|
||||||
showMiniMapOnCanvas: boolean;
|
showMiniMapOnCanvas: boolean;
|
||||||
setShowMiniMapOnCanvas: (showMiniMapOnCanvas: boolean) => void;
|
setShowMiniMapOnCanvas: (showMiniMapOnCanvas: boolean) => void;
|
||||||
}
|
}
|
||||||
@@ -47,8 +37,8 @@ export const LocalConfigContext = createContext<LocalConfigContext>({
|
|||||||
scrollAction: 'pan',
|
scrollAction: 'pan',
|
||||||
setScrollAction: emptyFn,
|
setScrollAction: emptyFn,
|
||||||
|
|
||||||
schemasFilter: {},
|
showDBViews: false,
|
||||||
setSchemasFilter: emptyFn,
|
setShowDBViews: emptyFn,
|
||||||
|
|
||||||
showCardinality: true,
|
showCardinality: true,
|
||||||
setShowCardinality: emptyFn,
|
setShowCardinality: emptyFn,
|
||||||
@@ -56,18 +46,12 @@ export const LocalConfigContext = createContext<LocalConfigContext>({
|
|||||||
showFieldAttributes: true,
|
showFieldAttributes: true,
|
||||||
setShowFieldAttributes: emptyFn,
|
setShowFieldAttributes: emptyFn,
|
||||||
|
|
||||||
hideMultiSchemaNotification: false,
|
|
||||||
setHideMultiSchemaNotification: emptyFn,
|
|
||||||
|
|
||||||
githubRepoOpened: false,
|
githubRepoOpened: false,
|
||||||
setGithubRepoOpened: emptyFn,
|
setGithubRepoOpened: emptyFn,
|
||||||
|
|
||||||
starUsDialogLastOpen: 0,
|
starUsDialogLastOpen: 0,
|
||||||
setStarUsDialogLastOpen: emptyFn,
|
setStarUsDialogLastOpen: emptyFn,
|
||||||
|
|
||||||
showDependenciesOnCanvas: false,
|
|
||||||
setShowDependenciesOnCanvas: emptyFn,
|
|
||||||
|
|
||||||
showMiniMapOnCanvas: false,
|
showMiniMapOnCanvas: false,
|
||||||
setShowMiniMapOnCanvas: emptyFn,
|
setShowMiniMapOnCanvas: emptyFn,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
import React, { useEffect } from 'react';
|
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 { LocalConfigContext } from './local-config-context';
|
||||||
import type { Theme } from '../theme-context/theme-context';
|
import type { Theme } from '../theme-context/theme-context';
|
||||||
|
|
||||||
const themeKey = 'theme';
|
const themeKey = 'theme';
|
||||||
const scrollActionKey = 'scroll_action';
|
const scrollActionKey = 'scroll_action';
|
||||||
const schemasFilterKey = 'schemas_filter';
|
|
||||||
const showCardinalityKey = 'show_cardinality';
|
const showCardinalityKey = 'show_cardinality';
|
||||||
const showFieldAttributesKey = 'show_field_attributes';
|
const showFieldAttributesKey = 'show_field_attributes';
|
||||||
const hideMultiSchemaNotificationKey = 'hide_multi_schema_notification';
|
|
||||||
const githubRepoOpenedKey = 'github_repo_opened';
|
const githubRepoOpenedKey = 'github_repo_opened';
|
||||||
const starUsDialogLastOpenKey = 'star_us_dialog_last_open';
|
const starUsDialogLastOpenKey = 'star_us_dialog_last_open';
|
||||||
const showDependenciesOnCanvasKey = 'show_dependencies_on_canvas';
|
|
||||||
const showMiniMapOnCanvasKey = 'show_minimap_on_canvas';
|
const showMiniMapOnCanvasKey = 'show_minimap_on_canvas';
|
||||||
|
const showDBViewsKey = 'show_db_views';
|
||||||
|
|
||||||
export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||||
children,
|
children,
|
||||||
@@ -25,10 +23,8 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
(localStorage.getItem(scrollActionKey) as ScrollAction) || 'pan'
|
(localStorage.getItem(scrollActionKey) as ScrollAction) || 'pan'
|
||||||
);
|
);
|
||||||
|
|
||||||
const [schemasFilter, setSchemasFilter] = React.useState<SchemasFilter>(
|
const [showDBViews, setShowDBViews] = React.useState<boolean>(
|
||||||
JSON.parse(
|
(localStorage.getItem(showDBViewsKey) || 'false') === 'true'
|
||||||
localStorage.getItem(schemasFilterKey) || '{}'
|
|
||||||
) as SchemasFilter
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const [showCardinality, setShowCardinality] = React.useState<boolean>(
|
const [showCardinality, setShowCardinality] = React.useState<boolean>(
|
||||||
@@ -40,12 +36,6 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
(localStorage.getItem(showFieldAttributesKey) || 'true') === 'true'
|
(localStorage.getItem(showFieldAttributesKey) || 'true') === 'true'
|
||||||
);
|
);
|
||||||
|
|
||||||
const [hideMultiSchemaNotification, setHideMultiSchemaNotification] =
|
|
||||||
React.useState<boolean>(
|
|
||||||
(localStorage.getItem(hideMultiSchemaNotificationKey) ||
|
|
||||||
'false') === 'true'
|
|
||||||
);
|
|
||||||
|
|
||||||
const [githubRepoOpened, setGithubRepoOpened] = React.useState<boolean>(
|
const [githubRepoOpened, setGithubRepoOpened] = React.useState<boolean>(
|
||||||
(localStorage.getItem(githubRepoOpenedKey) || 'false') === 'true'
|
(localStorage.getItem(githubRepoOpenedKey) || 'false') === 'true'
|
||||||
);
|
);
|
||||||
@@ -55,12 +45,6 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
parseInt(localStorage.getItem(starUsDialogLastOpenKey) || '0')
|
parseInt(localStorage.getItem(starUsDialogLastOpenKey) || '0')
|
||||||
);
|
);
|
||||||
|
|
||||||
const [showDependenciesOnCanvas, setShowDependenciesOnCanvas] =
|
|
||||||
React.useState<boolean>(
|
|
||||||
(localStorage.getItem(showDependenciesOnCanvasKey) || 'false') ===
|
|
||||||
'true'
|
|
||||||
);
|
|
||||||
|
|
||||||
const [showMiniMapOnCanvas, setShowMiniMapOnCanvas] =
|
const [showMiniMapOnCanvas, setShowMiniMapOnCanvas] =
|
||||||
React.useState<boolean>(
|
React.useState<boolean>(
|
||||||
(localStorage.getItem(showMiniMapOnCanvasKey) || 'true') === 'true'
|
(localStorage.getItem(showMiniMapOnCanvasKey) || 'true') === 'true'
|
||||||
@@ -77,13 +61,6 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
localStorage.setItem(githubRepoOpenedKey, githubRepoOpened.toString());
|
localStorage.setItem(githubRepoOpenedKey, githubRepoOpened.toString());
|
||||||
}, [githubRepoOpened]);
|
}, [githubRepoOpened]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem(
|
|
||||||
hideMultiSchemaNotificationKey,
|
|
||||||
hideMultiSchemaNotification.toString()
|
|
||||||
);
|
|
||||||
}, [hideMultiSchemaNotification]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(themeKey, theme);
|
localStorage.setItem(themeKey, theme);
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
@@ -93,20 +70,13 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
}, [scrollAction]);
|
}, [scrollAction]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(schemasFilterKey, JSON.stringify(schemasFilter));
|
localStorage.setItem(showDBViewsKey, showDBViews.toString());
|
||||||
}, [schemasFilter]);
|
}, [showDBViews]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(showCardinalityKey, showCardinality.toString());
|
localStorage.setItem(showCardinalityKey, showCardinality.toString());
|
||||||
}, [showCardinality]);
|
}, [showCardinality]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem(
|
|
||||||
showDependenciesOnCanvasKey,
|
|
||||||
showDependenciesOnCanvas.toString()
|
|
||||||
);
|
|
||||||
}, [showDependenciesOnCanvas]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
showMiniMapOnCanvasKey,
|
showMiniMapOnCanvasKey,
|
||||||
@@ -121,20 +91,16 @@ export const LocalConfigProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
setTheme,
|
setTheme,
|
||||||
scrollAction,
|
scrollAction,
|
||||||
setScrollAction,
|
setScrollAction,
|
||||||
schemasFilter,
|
showDBViews,
|
||||||
setSchemasFilter,
|
setShowDBViews,
|
||||||
showCardinality,
|
showCardinality,
|
||||||
setShowCardinality,
|
setShowCardinality,
|
||||||
showFieldAttributes,
|
showFieldAttributes,
|
||||||
setShowFieldAttributes,
|
setShowFieldAttributes,
|
||||||
hideMultiSchemaNotification,
|
|
||||||
setHideMultiSchemaNotification,
|
|
||||||
setGithubRepoOpened,
|
setGithubRepoOpened,
|
||||||
githubRepoOpened,
|
githubRepoOpened,
|
||||||
starUsDialogLastOpen,
|
starUsDialogLastOpen,
|
||||||
setStarUsDialogLastOpen,
|
setStarUsDialogLastOpen,
|
||||||
showDependenciesOnCanvas,
|
|
||||||
setShowDependenciesOnCanvas,
|
|
||||||
showMiniMapOnCanvas,
|
showMiniMapOnCanvas,
|
||||||
setShowMiniMapOnCanvas,
|
setShowMiniMapOnCanvas,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -7,12 +7,22 @@ import type { ChartDBConfig } from '@/lib/domain/config';
|
|||||||
import type { DBDependency } from '@/lib/domain/db-dependency';
|
import type { DBDependency } from '@/lib/domain/db-dependency';
|
||||||
import type { Area } from '@/lib/domain/area';
|
import type { Area } from '@/lib/domain/area';
|
||||||
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
||||||
|
import type { DiagramFilter } from '@/lib/domain/diagram-filter/diagram-filter';
|
||||||
|
import type { Note } from '@/lib/domain/note';
|
||||||
|
|
||||||
export interface StorageContext {
|
export interface StorageContext {
|
||||||
// Config operations
|
// Config operations
|
||||||
getConfig: () => Promise<ChartDBConfig | undefined>;
|
getConfig: () => Promise<ChartDBConfig | undefined>;
|
||||||
updateConfig: (config: Partial<ChartDBConfig>) => Promise<void>;
|
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
|
// Diagram operations
|
||||||
addDiagram: (params: { diagram: Diagram }) => Promise<void>;
|
addDiagram: (params: { diagram: Diagram }) => Promise<void>;
|
||||||
listDiagrams: (options?: {
|
listDiagrams: (options?: {
|
||||||
@@ -21,6 +31,7 @@ export interface StorageContext {
|
|||||||
includeDependencies?: boolean;
|
includeDependencies?: boolean;
|
||||||
includeAreas?: boolean;
|
includeAreas?: boolean;
|
||||||
includeCustomTypes?: boolean;
|
includeCustomTypes?: boolean;
|
||||||
|
includeNotes?: boolean;
|
||||||
}) => Promise<Diagram[]>;
|
}) => Promise<Diagram[]>;
|
||||||
getDiagram: (
|
getDiagram: (
|
||||||
id: string,
|
id: string,
|
||||||
@@ -30,6 +41,7 @@ export interface StorageContext {
|
|||||||
includeDependencies?: boolean;
|
includeDependencies?: boolean;
|
||||||
includeAreas?: boolean;
|
includeAreas?: boolean;
|
||||||
includeCustomTypes?: boolean;
|
includeCustomTypes?: boolean;
|
||||||
|
includeNotes?: boolean;
|
||||||
}
|
}
|
||||||
) => Promise<Diagram | undefined>;
|
) => Promise<Diagram | undefined>;
|
||||||
updateDiagram: (params: {
|
updateDiagram: (params: {
|
||||||
@@ -126,12 +138,30 @@ export interface StorageContext {
|
|||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
listCustomTypes: (diagramId: string) => Promise<DBCustomType[]>;
|
listCustomTypes: (diagramId: string) => Promise<DBCustomType[]>;
|
||||||
deleteDiagramCustomTypes: (diagramId: string) => Promise<void>;
|
deleteDiagramCustomTypes: (diagramId: string) => Promise<void>;
|
||||||
|
|
||||||
|
// Note operations
|
||||||
|
addNote: (params: { diagramId: string; note: Note }) => Promise<void>;
|
||||||
|
getNote: (params: {
|
||||||
|
diagramId: string;
|
||||||
|
id: string;
|
||||||
|
}) => Promise<Note | undefined>;
|
||||||
|
updateNote: (params: {
|
||||||
|
id: string;
|
||||||
|
attributes: Partial<Note>;
|
||||||
|
}) => Promise<void>;
|
||||||
|
deleteNote: (params: { diagramId: string; id: string }) => Promise<void>;
|
||||||
|
listNotes: (diagramId: string) => Promise<Note[]>;
|
||||||
|
deleteDiagramNotes: (diagramId: string) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const storageInitialValue: StorageContext = {
|
export const storageInitialValue: StorageContext = {
|
||||||
getConfig: emptyFn,
|
getConfig: emptyFn,
|
||||||
updateConfig: emptyFn,
|
updateConfig: emptyFn,
|
||||||
|
|
||||||
|
getDiagramFilter: emptyFn,
|
||||||
|
updateDiagramFilter: emptyFn,
|
||||||
|
deleteDiagramFilter: emptyFn,
|
||||||
|
|
||||||
addDiagram: emptyFn,
|
addDiagram: emptyFn,
|
||||||
listDiagrams: emptyFn,
|
listDiagrams: emptyFn,
|
||||||
getDiagram: emptyFn,
|
getDiagram: emptyFn,
|
||||||
@@ -174,6 +204,14 @@ export const storageInitialValue: StorageContext = {
|
|||||||
deleteCustomType: emptyFn,
|
deleteCustomType: emptyFn,
|
||||||
listCustomTypes: emptyFn,
|
listCustomTypes: emptyFn,
|
||||||
deleteDiagramCustomTypes: emptyFn,
|
deleteDiagramCustomTypes: emptyFn,
|
||||||
|
|
||||||
|
// Note operations
|
||||||
|
addNote: emptyFn,
|
||||||
|
getNote: emptyFn,
|
||||||
|
updateNote: emptyFn,
|
||||||
|
deleteNote: emptyFn,
|
||||||
|
listNotes: emptyFn,
|
||||||
|
deleteDiagramNotes: emptyFn,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const storageContext =
|
export const storageContext =
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import type { ChartDBConfig } from '@/lib/domain/config';
|
|||||||
import type { DBDependency } from '@/lib/domain/db-dependency';
|
import type { DBDependency } from '@/lib/domain/db-dependency';
|
||||||
import type { Area } from '@/lib/domain/area';
|
import type { Area } from '@/lib/domain/area';
|
||||||
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
import type { DBCustomType } from '@/lib/domain/db-custom-type';
|
||||||
|
import type { DiagramFilter } from '@/lib/domain/diagram-filter/diagram-filter';
|
||||||
|
import type { Note } from '@/lib/domain/note';
|
||||||
|
|
||||||
export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
||||||
children,
|
children,
|
||||||
@@ -40,10 +42,18 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
DBCustomType & { diagramId: string },
|
DBCustomType & { diagramId: string },
|
||||||
'id' // primary key "id" (for the typings only)
|
'id' // primary key "id" (for the typings only)
|
||||||
>;
|
>;
|
||||||
|
notes: EntityTable<
|
||||||
|
Note & { diagramId: string },
|
||||||
|
'id' // primary key "id" (for the typings only)
|
||||||
|
>;
|
||||||
config: EntityTable<
|
config: EntityTable<
|
||||||
ChartDBConfig & { id: number },
|
ChartDBConfig & { id: number },
|
||||||
'id' // primary key "id" (for the typings only)
|
'id' // primary key "id" (for the typings only)
|
||||||
>;
|
>;
|
||||||
|
diagram_filters: EntityTable<
|
||||||
|
DiagramFilter & { diagramId: string },
|
||||||
|
'diagramId' // primary key "id" (for the typings only)
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Schema declaration:
|
// Schema declaration:
|
||||||
@@ -190,6 +200,44 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
config: '++id, defaultDiagramId',
|
config: '++id, defaultDiagramId',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dexieDB
|
||||||
|
.version(12)
|
||||||
|
.stores({
|
||||||
|
diagrams:
|
||||||
|
'++id, name, databaseType, databaseEdition, createdAt, updatedAt',
|
||||||
|
db_tables:
|
||||||
|
'++id, diagramId, name, schema, x, y, fields, indexes, color, createdAt, width, comment, isView, isMaterializedView, order',
|
||||||
|
db_relationships:
|
||||||
|
'++id, diagramId, name, sourceSchema, sourceTableId, targetSchema, targetTableId, sourceFieldId, targetFieldId, type, createdAt',
|
||||||
|
db_dependencies:
|
||||||
|
'++id, diagramId, schema, tableId, dependentSchema, dependentTableId, createdAt',
|
||||||
|
areas: '++id, diagramId, name, x, y, width, height, color',
|
||||||
|
db_custom_types:
|
||||||
|
'++id, diagramId, schema, type, kind, values, fields',
|
||||||
|
config: '++id, defaultDiagramId',
|
||||||
|
diagram_filters: 'diagramId, tableIds, schemasIds',
|
||||||
|
})
|
||||||
|
.upgrade((tx) => {
|
||||||
|
tx.table('config').clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
dexieDB.version(13).stores({
|
||||||
|
diagrams:
|
||||||
|
'++id, name, databaseType, databaseEdition, createdAt, updatedAt',
|
||||||
|
db_tables:
|
||||||
|
'++id, diagramId, name, schema, x, y, fields, indexes, color, createdAt, width, comment, isView, isMaterializedView, order',
|
||||||
|
db_relationships:
|
||||||
|
'++id, diagramId, name, sourceSchema, sourceTableId, targetSchema, targetTableId, sourceFieldId, targetFieldId, type, createdAt',
|
||||||
|
db_dependencies:
|
||||||
|
'++id, diagramId, schema, tableId, dependentSchema, dependentTableId, createdAt',
|
||||||
|
areas: '++id, diagramId, name, x, y, width, height, color',
|
||||||
|
db_custom_types:
|
||||||
|
'++id, diagramId, schema, type, kind, values, fields',
|
||||||
|
config: '++id, defaultDiagramId',
|
||||||
|
diagram_filters: 'diagramId, tableIds, schemasIds',
|
||||||
|
notes: '++id, diagramId, content, x, y, width, height, color',
|
||||||
|
});
|
||||||
|
|
||||||
dexieDB.on('ready', async () => {
|
dexieDB.on('ready', async () => {
|
||||||
const config = await dexieDB.config.get(1);
|
const config = await dexieDB.config.get(1);
|
||||||
|
|
||||||
@@ -217,6 +265,34 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
[db]
|
[db]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getDiagramFilter: StorageContext['getDiagramFilter'] = useCallback(
|
||||||
|
async (diagramId: string): Promise<DiagramFilter | undefined> => {
|
||||||
|
const filter = await db.diagram_filters.get({ diagramId });
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateDiagramFilter: StorageContext['updateDiagramFilter'] =
|
||||||
|
useCallback(
|
||||||
|
async (diagramId, filter): Promise<void> => {
|
||||||
|
await db.diagram_filters.put({
|
||||||
|
diagramId,
|
||||||
|
...filter,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
|
const deleteDiagramFilter: StorageContext['deleteDiagramFilter'] =
|
||||||
|
useCallback(
|
||||||
|
async (diagramId: string): Promise<void> => {
|
||||||
|
await db.diagram_filters.where({ diagramId }).delete();
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
const addTable: StorageContext['addTable'] = useCallback(
|
const addTable: StorageContext['addTable'] = useCallback(
|
||||||
async ({ diagramId, table }) => {
|
async ({ diagramId, table }) => {
|
||||||
await db.db_tables.add({
|
await db.db_tables.add({
|
||||||
@@ -496,6 +572,56 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
[db]
|
[db]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Note operations
|
||||||
|
const addNote: StorageContext['addNote'] = useCallback(
|
||||||
|
async ({ note, diagramId }) => {
|
||||||
|
await db.notes.add({
|
||||||
|
...note,
|
||||||
|
diagramId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getNote: StorageContext['getNote'] = useCallback(
|
||||||
|
async ({ diagramId, id }) => {
|
||||||
|
return await db.notes.get({ id, diagramId });
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateNote: StorageContext['updateNote'] = useCallback(
|
||||||
|
async ({ id, attributes }) => {
|
||||||
|
await db.notes.update(id, attributes);
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
|
const deleteNote: StorageContext['deleteNote'] = useCallback(
|
||||||
|
async ({ diagramId, id }) => {
|
||||||
|
await db.notes.where({ id, diagramId }).delete();
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
|
const listNotes: StorageContext['listNotes'] = useCallback(
|
||||||
|
async (diagramId) => {
|
||||||
|
return await db.notes
|
||||||
|
.where('diagramId')
|
||||||
|
.equals(diagramId)
|
||||||
|
.toArray();
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
|
const deleteDiagramNotes: StorageContext['deleteDiagramNotes'] =
|
||||||
|
useCallback(
|
||||||
|
async (diagramId) => {
|
||||||
|
await db.notes.where('diagramId').equals(diagramId).delete();
|
||||||
|
},
|
||||||
|
[db]
|
||||||
|
);
|
||||||
|
|
||||||
const addDiagram: StorageContext['addDiagram'] = useCallback(
|
const addDiagram: StorageContext['addDiagram'] = useCallback(
|
||||||
async ({ diagram }) => {
|
async ({ diagram }) => {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
@@ -543,9 +669,22 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const notes = diagram.notes ?? [];
|
||||||
|
promises.push(
|
||||||
|
...notes.map((note) => addNote({ diagramId: diagram.id, note }))
|
||||||
|
);
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
},
|
},
|
||||||
[db, addArea, addCustomType, addDependency, addRelationship, addTable]
|
[
|
||||||
|
db,
|
||||||
|
addArea,
|
||||||
|
addCustomType,
|
||||||
|
addDependency,
|
||||||
|
addRelationship,
|
||||||
|
addTable,
|
||||||
|
addNote,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const listDiagrams: StorageContext['listDiagrams'] = useCallback(
|
const listDiagrams: StorageContext['listDiagrams'] = useCallback(
|
||||||
@@ -556,6 +695,7 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
includeDependencies: false,
|
includeDependencies: false,
|
||||||
includeAreas: false,
|
includeAreas: false,
|
||||||
includeCustomTypes: false,
|
includeCustomTypes: false,
|
||||||
|
includeNotes: false,
|
||||||
}
|
}
|
||||||
): Promise<Diagram[]> => {
|
): Promise<Diagram[]> => {
|
||||||
let diagrams = await db.diagrams.toArray();
|
let diagrams = await db.diagrams.toArray();
|
||||||
@@ -609,6 +749,15 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.includeNotes) {
|
||||||
|
diagrams = await Promise.all(
|
||||||
|
diagrams.map(async (diagram) => {
|
||||||
|
diagram.notes = await listNotes(diagram.id);
|
||||||
|
return diagram;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return diagrams;
|
return diagrams;
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@@ -618,6 +767,7 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
listDependencies,
|
listDependencies,
|
||||||
listRelationships,
|
listRelationships,
|
||||||
listTables,
|
listTables,
|
||||||
|
listNotes,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -630,6 +780,7 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
includeDependencies: false,
|
includeDependencies: false,
|
||||||
includeAreas: false,
|
includeAreas: false,
|
||||||
includeCustomTypes: false,
|
includeCustomTypes: false,
|
||||||
|
includeNotes: false,
|
||||||
}
|
}
|
||||||
): Promise<Diagram | undefined> => {
|
): Promise<Diagram | undefined> => {
|
||||||
const diagram = await db.diagrams.get(id);
|
const diagram = await db.diagrams.get(id);
|
||||||
@@ -658,6 +809,10 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
diagram.customTypes = await listCustomTypes(id);
|
diagram.customTypes = await listCustomTypes(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.includeNotes) {
|
||||||
|
diagram.notes = await listNotes(id);
|
||||||
|
}
|
||||||
|
|
||||||
return diagram;
|
return diagram;
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@@ -667,6 +822,7 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
listDependencies,
|
listDependencies,
|
||||||
listRelationships,
|
listRelationships,
|
||||||
listTables,
|
listTables,
|
||||||
|
listNotes,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -695,6 +851,9 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
.where('diagramId')
|
.where('diagramId')
|
||||||
.equals(id)
|
.equals(id)
|
||||||
.modify({ diagramId: attributes.id }),
|
.modify({ diagramId: attributes.id }),
|
||||||
|
db.notes.where('diagramId').equals(id).modify({
|
||||||
|
diagramId: attributes.id,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -710,6 +869,7 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
db.db_dependencies.where('diagramId').equals(id).delete(),
|
db.db_dependencies.where('diagramId').equals(id).delete(),
|
||||||
db.areas.where('diagramId').equals(id).delete(),
|
db.areas.where('diagramId').equals(id).delete(),
|
||||||
db.db_custom_types.where('diagramId').equals(id).delete(),
|
db.db_custom_types.where('diagramId').equals(id).delete(),
|
||||||
|
db.notes.where('diagramId').equals(id).delete(),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
[db]
|
[db]
|
||||||
@@ -756,6 +916,15 @@ export const StorageProvider: React.FC<React.PropsWithChildren> = ({
|
|||||||
deleteCustomType,
|
deleteCustomType,
|
||||||
listCustomTypes,
|
listCustomTypes,
|
||||||
deleteDiagramCustomTypes,
|
deleteDiagramCustomTypes,
|
||||||
|
addNote,
|
||||||
|
getNote,
|
||||||
|
updateNote,
|
||||||
|
deleteNote,
|
||||||
|
listNotes,
|
||||||
|
deleteDiagramNotes,
|
||||||
|
getDiagramFilter,
|
||||||
|
updateDiagramFilter,
|
||||||
|
deleteDiagramFilter,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ import {
|
|||||||
type ValidationResult,
|
type ValidationResult,
|
||||||
} from '@/lib/data/sql-import/sql-validator';
|
} from '@/lib/data/sql-import/sql-validator';
|
||||||
import { SQLValidationStatus } from './sql-validation-status';
|
import { SQLValidationStatus } from './sql-validation-status';
|
||||||
|
import { setupDBMLLanguage } from '@/components/code-snippet/languages/dbml-language';
|
||||||
|
import type { ImportMethod } from '@/lib/import-method/import-method';
|
||||||
|
import { detectImportMethod } from '@/lib/import-method/detect-import-method';
|
||||||
|
import { verifyDBML } from '@/lib/dbml/dbml-import/verify-dbml';
|
||||||
|
import {
|
||||||
|
clearErrorHighlight,
|
||||||
|
highlightErrorLine,
|
||||||
|
} from '@/components/code-snippet/dbml/utils';
|
||||||
|
|
||||||
const calculateContentSizeMB = (content: string): number => {
|
const calculateContentSizeMB = (content: string): number => {
|
||||||
return content.length / (1024 * 1024); // Convert to MB
|
return content.length / (1024 * 1024); // Convert to MB
|
||||||
@@ -55,49 +63,6 @@ const calculateIsLargeFile = (content: string): boolean => {
|
|||||||
const errorScriptOutputMessage =
|
const errorScriptOutputMessage =
|
||||||
'Invalid JSON. Please correct it or contact us at support@chartdb.io for help.';
|
'Invalid JSON. Please correct it or contact us at support@chartdb.io for help.';
|
||||||
|
|
||||||
// Helper to detect if content is likely SQL DDL or JSON
|
|
||||||
const detectContentType = (content: string): 'query' | 'ddl' | null => {
|
|
||||||
if (!content || content.trim().length === 0) return null;
|
|
||||||
|
|
||||||
// Common SQL DDL keywords
|
|
||||||
const ddlKeywords = [
|
|
||||||
'CREATE TABLE',
|
|
||||||
'ALTER TABLE',
|
|
||||||
'DROP TABLE',
|
|
||||||
'CREATE INDEX',
|
|
||||||
'CREATE VIEW',
|
|
||||||
'CREATE PROCEDURE',
|
|
||||||
'CREATE FUNCTION',
|
|
||||||
'CREATE SCHEMA',
|
|
||||||
'CREATE DATABASE',
|
|
||||||
];
|
|
||||||
|
|
||||||
const upperContent = content.toUpperCase();
|
|
||||||
|
|
||||||
// Check for SQL DDL patterns
|
|
||||||
const hasDDLKeywords = ddlKeywords.some((keyword) =>
|
|
||||||
upperContent.includes(keyword)
|
|
||||||
);
|
|
||||||
if (hasDDLKeywords) return 'ddl';
|
|
||||||
|
|
||||||
// Check if it looks like JSON
|
|
||||||
try {
|
|
||||||
// Just check structure, don't need full parse for detection
|
|
||||||
if (
|
|
||||||
(content.trim().startsWith('{') && content.trim().endsWith('}')) ||
|
|
||||||
(content.trim().startsWith('[') && content.trim().endsWith(']'))
|
|
||||||
) {
|
|
||||||
return 'query';
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Not valid JSON, might be partial
|
|
||||||
console.error('Error detecting content type:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can't confidently detect, return null
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ImportDatabaseProps {
|
export interface ImportDatabaseProps {
|
||||||
goBack?: () => void;
|
goBack?: () => void;
|
||||||
onImport: () => void;
|
onImport: () => void;
|
||||||
@@ -111,8 +76,8 @@ export interface ImportDatabaseProps {
|
|||||||
>;
|
>;
|
||||||
keepDialogAfterImport?: boolean;
|
keepDialogAfterImport?: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
importMethod: 'query' | 'ddl';
|
importMethod: ImportMethod;
|
||||||
setImportMethod: (method: 'query' | 'ddl') => void;
|
setImportMethod: (method: ImportMethod) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
||||||
@@ -132,6 +97,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
const { effectiveTheme } = useTheme();
|
const { effectiveTheme } = useTheme();
|
||||||
const [errorMessage, setErrorMessage] = useState('');
|
const [errorMessage, setErrorMessage] = useState('');
|
||||||
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
||||||
|
const decorationsCollection = useRef<editor.IEditorDecorationsCollection>();
|
||||||
const pasteDisposableRef = useRef<IDisposable | null>(null);
|
const pasteDisposableRef = useRef<IDisposable | null>(null);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -146,15 +112,20 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
const [isAutoFixing, setIsAutoFixing] = useState(false);
|
const [isAutoFixing, setIsAutoFixing] = useState(false);
|
||||||
const [showAutoFixButton, setShowAutoFixButton] = useState(false);
|
const [showAutoFixButton, setShowAutoFixButton] = useState(false);
|
||||||
|
|
||||||
|
const clearDecorations = useCallback(() => {
|
||||||
|
clearErrorHighlight(decorationsCollection.current);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setScriptResult('');
|
setScriptResult('');
|
||||||
setErrorMessage('');
|
setErrorMessage('');
|
||||||
setShowCheckJsonButton(false);
|
setShowCheckJsonButton(false);
|
||||||
}, [importMethod, setScriptResult]);
|
}, [importMethod, setScriptResult]);
|
||||||
|
|
||||||
// Check if the ddl is valid
|
// Check if the ddl or dbml is valid
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (importMethod !== 'ddl') {
|
clearDecorations();
|
||||||
|
if (importMethod === 'query') {
|
||||||
setSqlValidation(null);
|
setSqlValidation(null);
|
||||||
setShowAutoFixButton(false);
|
setShowAutoFixButton(false);
|
||||||
return;
|
return;
|
||||||
@@ -163,9 +134,54 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
if (!scriptResult.trim()) {
|
if (!scriptResult.trim()) {
|
||||||
setSqlValidation(null);
|
setSqlValidation(null);
|
||||||
setShowAutoFixButton(false);
|
setShowAutoFixButton(false);
|
||||||
|
setErrorMessage('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (importMethod === 'dbml') {
|
||||||
|
// Validate DBML by parsing it
|
||||||
|
const validateResponse = verifyDBML(scriptResult, { databaseType });
|
||||||
|
if (!validateResponse.hasError) {
|
||||||
|
setErrorMessage('');
|
||||||
|
setSqlValidation({
|
||||||
|
isValid: true,
|
||||||
|
errors: [],
|
||||||
|
warnings: [],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let errorMsg = 'Invalid DBML syntax';
|
||||||
|
let line: number = 1;
|
||||||
|
|
||||||
|
if (validateResponse.parsedError) {
|
||||||
|
errorMsg = validateResponse.parsedError.message;
|
||||||
|
line = validateResponse.parsedError.line;
|
||||||
|
highlightErrorLine({
|
||||||
|
error: validateResponse.parsedError,
|
||||||
|
model: editorRef.current?.getModel(),
|
||||||
|
editorDecorationsCollection:
|
||||||
|
decorationsCollection.current,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setSqlValidation({
|
||||||
|
isValid: false,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
message: errorMsg,
|
||||||
|
line: line,
|
||||||
|
type: 'syntax' as const,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
warnings: [],
|
||||||
|
});
|
||||||
|
setErrorMessage(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowAutoFixButton(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SQL validation
|
||||||
// First run our validation based on database type
|
// First run our validation based on database type
|
||||||
const validation = validateSQL(scriptResult, databaseType);
|
const validation = validateSQL(scriptResult, databaseType);
|
||||||
setSqlValidation(validation);
|
setSqlValidation(validation);
|
||||||
@@ -192,7 +208,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
setErrorMessage(result.error);
|
setErrorMessage(result.error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [importMethod, scriptResult, databaseType]);
|
}, [importMethod, scriptResult, databaseType, clearDecorations]);
|
||||||
|
|
||||||
// Check if the script result is a valid JSON
|
// Check if the script result is a valid JSON
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -320,6 +336,8 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
const handleEditorDidMount = useCallback(
|
const handleEditorDidMount = useCallback(
|
||||||
(editor: editor.IStandaloneCodeEditor) => {
|
(editor: editor.IStandaloneCodeEditor) => {
|
||||||
editorRef.current = editor;
|
editorRef.current = editor;
|
||||||
|
decorationsCollection.current =
|
||||||
|
editor.createDecorationsCollection();
|
||||||
|
|
||||||
// Cleanup previous disposable if it exists
|
// Cleanup previous disposable if it exists
|
||||||
if (pasteDisposableRef.current) {
|
if (pasteDisposableRef.current) {
|
||||||
@@ -338,7 +356,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
const isLargeFile = calculateIsLargeFile(content);
|
const isLargeFile = calculateIsLargeFile(content);
|
||||||
|
|
||||||
// First, detect content type to determine if we should switch modes
|
// First, detect content type to determine if we should switch modes
|
||||||
const detectedType = detectContentType(content);
|
const detectedType = detectImportMethod(content);
|
||||||
if (detectedType && detectedType !== importMethod) {
|
if (detectedType && detectedType !== importMethod) {
|
||||||
// Switch to the detected mode immediately
|
// Switch to the detected mode immediately
|
||||||
setImportMethod(detectedType);
|
setImportMethod(detectedType);
|
||||||
@@ -352,7 +370,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
?.run();
|
?.run();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
// For DDL mode, do NOT format as it can break the SQL
|
// For DDL and DBML modes, do NOT format as it can break the syntax
|
||||||
} else {
|
} else {
|
||||||
// Content type didn't change, apply formatting based on current mode
|
// Content type didn't change, apply formatting based on current mode
|
||||||
if (importMethod === 'query' && !isLargeFile) {
|
if (importMethod === 'query' && !isLargeFile) {
|
||||||
@@ -363,7 +381,7 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
?.run();
|
?.run();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
// For DDL mode or large files, do NOT format
|
// For DDL and DBML modes or large files, do NOT format
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -410,16 +428,25 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
<div className="w-full text-center text-xs text-muted-foreground">
|
<div className="w-full text-center text-xs text-muted-foreground">
|
||||||
{importMethod === 'query'
|
{importMethod === 'query'
|
||||||
? 'Smart Query Output'
|
? 'Smart Query Output'
|
||||||
: 'SQL Script'}
|
: importMethod === 'dbml'
|
||||||
|
? 'DBML Script'
|
||||||
|
: 'SQL Script'}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-hidden">
|
<div className="flex-1 overflow-hidden">
|
||||||
<Suspense fallback={<Spinner />}>
|
<Suspense fallback={<Spinner />}>
|
||||||
<Editor
|
<Editor
|
||||||
value={scriptResult}
|
value={scriptResult}
|
||||||
onChange={debouncedHandleInputChange}
|
onChange={debouncedHandleInputChange}
|
||||||
language={importMethod === 'query' ? 'json' : 'sql'}
|
language={
|
||||||
|
importMethod === 'query'
|
||||||
|
? 'json'
|
||||||
|
: importMethod === 'dbml'
|
||||||
|
? 'dbml'
|
||||||
|
: 'sql'
|
||||||
|
}
|
||||||
loading={<Spinner />}
|
loading={<Spinner />}
|
||||||
onMount={handleEditorDidMount}
|
onMount={handleEditorDidMount}
|
||||||
|
beforeMount={setupDBMLLanguage}
|
||||||
theme={
|
theme={
|
||||||
effectiveTheme === 'dark'
|
effectiveTheme === 'dark'
|
||||||
? 'dbml-dark'
|
? 'dbml-dark'
|
||||||
@@ -430,7 +457,6 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
minimap: { enabled: false },
|
minimap: { enabled: false },
|
||||||
scrollBeyondLastLine: false,
|
scrollBeyondLastLine: false,
|
||||||
automaticLayout: true,
|
automaticLayout: true,
|
||||||
glyphMargin: false,
|
|
||||||
lineNumbers: 'on',
|
lineNumbers: 'on',
|
||||||
guides: {
|
guides: {
|
||||||
indentation: false,
|
indentation: false,
|
||||||
@@ -455,7 +481,9 @@ export const ImportDatabase: React.FC<ImportDatabaseProps> = ({
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{errorMessage || (importMethod === 'ddl' && sqlValidation) ? (
|
{errorMessage ||
|
||||||
|
((importMethod === 'ddl' || importMethod === 'dbml') &&
|
||||||
|
sqlValidation) ? (
|
||||||
<SQLValidationStatus
|
<SQLValidationStatus
|
||||||
validation={sqlValidation}
|
validation={sqlValidation}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ import {
|
|||||||
AvatarImage,
|
AvatarImage,
|
||||||
} from '@/components/avatar/avatar';
|
} from '@/components/avatar/avatar';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Code } from 'lucide-react';
|
import { Code, FileCode } from 'lucide-react';
|
||||||
import { SmartQueryInstructions } from './instructions/smart-query-instructions';
|
import { SmartQueryInstructions } from './instructions/smart-query-instructions';
|
||||||
import { DDLInstructions } from './instructions/ddl-instructions';
|
import { DDLInstructions } from './instructions/ddl-instructions';
|
||||||
|
import { DBMLInstructions } from './instructions/dbml-instructions';
|
||||||
|
import type { ImportMethod } from '@/lib/import-method/import-method';
|
||||||
|
|
||||||
const DatabasesWithoutDDLInstructions: DatabaseType[] = [
|
const DatabasesWithoutDDLInstructions: DatabaseType[] = [
|
||||||
DatabaseType.CLICKHOUSE,
|
DatabaseType.CLICKHOUSE,
|
||||||
@@ -30,8 +32,8 @@ export interface InstructionsSectionProps {
|
|||||||
setDatabaseEdition: React.Dispatch<
|
setDatabaseEdition: React.Dispatch<
|
||||||
React.SetStateAction<DatabaseEdition | undefined>
|
React.SetStateAction<DatabaseEdition | undefined>
|
||||||
>;
|
>;
|
||||||
importMethod: 'query' | 'ddl';
|
importMethod: ImportMethod;
|
||||||
setImportMethod: (method: 'query' | 'ddl') => void;
|
setImportMethod: (method: ImportMethod) => void;
|
||||||
showSSMSInfoDialog: boolean;
|
showSSMSInfoDialog: boolean;
|
||||||
setShowSSMSInfoDialog: (show: boolean) => void;
|
setShowSSMSInfoDialog: (show: boolean) => void;
|
||||||
}
|
}
|
||||||
@@ -115,48 +117,60 @@ export const InstructionsSection: React.FC<InstructionsSectionProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{DatabasesWithoutDDLInstructions.includes(databaseType) ? null : (
|
<div className="flex flex-col gap-1">
|
||||||
<div className="flex flex-col gap-1">
|
<p className="text-sm leading-6 text-primary">
|
||||||
<p className="text-sm leading-6 text-primary">
|
How would you like to import?
|
||||||
How would you like to import?
|
</p>
|
||||||
</p>
|
<ToggleGroup
|
||||||
<ToggleGroup
|
type="single"
|
||||||
type="single"
|
className="ml-1 flex-wrap justify-start gap-2"
|
||||||
className="ml-1 flex-wrap justify-start gap-2"
|
value={importMethod}
|
||||||
value={importMethod}
|
onValueChange={(value) => {
|
||||||
onValueChange={(value) => {
|
let selectedImportMethod: ImportMethod = 'query';
|
||||||
let selectedImportMethod: 'query' | 'ddl' = 'query';
|
if (value) {
|
||||||
if (value) {
|
selectedImportMethod = value as ImportMethod;
|
||||||
selectedImportMethod = value as 'query' | 'ddl';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
setImportMethod(selectedImportMethod);
|
setImportMethod(selectedImportMethod);
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
<ToggleGroupItem
|
||||||
|
value="query"
|
||||||
|
variant="outline"
|
||||||
|
className="h-6 gap-1 p-0 px-2 shadow-none data-[state=on]:bg-slate-200 dark:data-[state=on]:bg-slate-700"
|
||||||
>
|
>
|
||||||
<ToggleGroupItem
|
<Avatar className="h-3 w-4 rounded-none">
|
||||||
value="query"
|
<AvatarImage src={logo} alt="query" />
|
||||||
variant="outline"
|
<AvatarFallback>Query</AvatarFallback>
|
||||||
className="h-6 gap-1 p-0 px-2 shadow-none data-[state=on]:bg-slate-200 dark:data-[state=on]:bg-slate-700"
|
</Avatar>
|
||||||
>
|
Smart Query
|
||||||
<Avatar className="h-3 w-4 rounded-none">
|
</ToggleGroupItem>
|
||||||
<AvatarImage src={logo} alt="query" />
|
{!DatabasesWithoutDDLInstructions.includes(
|
||||||
<AvatarFallback>Query</AvatarFallback>
|
databaseType
|
||||||
</Avatar>
|
) && (
|
||||||
Smart Query
|
|
||||||
</ToggleGroupItem>
|
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
value="ddl"
|
value="ddl"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="h-6 gap-1 p-0 px-2 shadow-none data-[state=on]:bg-slate-200 dark:data-[state=on]:bg-slate-700"
|
className="h-6 gap-1 p-0 px-2 shadow-none data-[state=on]:bg-slate-200 dark:data-[state=on]:bg-slate-700"
|
||||||
>
|
>
|
||||||
<Avatar className="size-4 rounded-none">
|
<Avatar className="size-4 rounded-none">
|
||||||
<Code size={16} />
|
<FileCode size={16} />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
SQL Script
|
SQL Script
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
)}
|
||||||
</div>
|
<ToggleGroupItem
|
||||||
)}
|
value="dbml"
|
||||||
|
variant="outline"
|
||||||
|
className="h-6 gap-1 p-0 px-2 shadow-none data-[state=on]:bg-slate-200 dark:data-[state=on]:bg-slate-700"
|
||||||
|
>
|
||||||
|
<Avatar className="size-4 rounded-none">
|
||||||
|
<Code size={16} />
|
||||||
|
</Avatar>
|
||||||
|
DBML
|
||||||
|
</ToggleGroupItem>
|
||||||
|
</ToggleGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="text-sm font-semibold">Instructions:</div>
|
<div className="text-sm font-semibold">Instructions:</div>
|
||||||
@@ -167,11 +181,16 @@ export const InstructionsSection: React.FC<InstructionsSectionProps> = ({
|
|||||||
showSSMSInfoDialog={showSSMSInfoDialog}
|
showSSMSInfoDialog={showSSMSInfoDialog}
|
||||||
setShowSSMSInfoDialog={setShowSSMSInfoDialog}
|
setShowSSMSInfoDialog={setShowSSMSInfoDialog}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : importMethod === 'ddl' ? (
|
||||||
<DDLInstructions
|
<DDLInstructions
|
||||||
databaseType={databaseType}
|
databaseType={databaseType}
|
||||||
databaseEdition={databaseEdition}
|
databaseEdition={databaseEdition}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<DBMLInstructions
|
||||||
|
databaseType={databaseType}
|
||||||
|
databaseEdition={databaseEdition}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { DatabaseType } from '@/lib/domain/database-type';
|
||||||
|
import type { DatabaseEdition } from '@/lib/domain/database-edition';
|
||||||
|
import { CodeSnippet } from '@/components/code-snippet/code-snippet';
|
||||||
|
import { setupDBMLLanguage } from '@/components/code-snippet/languages/dbml-language';
|
||||||
|
|
||||||
|
export interface DBMLInstructionsProps {
|
||||||
|
databaseType: DatabaseType;
|
||||||
|
databaseEdition?: DatabaseEdition;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DBMLInstructions: React.FC<DBMLInstructionsProps> = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col gap-1 text-sm text-primary">
|
||||||
|
<div>
|
||||||
|
Paste your DBML (Database Markup Language) schema definition
|
||||||
|
here →
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex h-64 flex-col gap-1 text-sm text-primary">
|
||||||
|
<h4 className="text-xs font-medium">Example:</h4>
|
||||||
|
<CodeSnippet
|
||||||
|
className="h-full"
|
||||||
|
allowCopy={false}
|
||||||
|
editorProps={{
|
||||||
|
beforeMount: setupDBMLLanguage,
|
||||||
|
}}
|
||||||
|
code={`Table users {
|
||||||
|
id int [pk]
|
||||||
|
username varchar
|
||||||
|
email varchar
|
||||||
|
}
|
||||||
|
|
||||||
|
Table posts {
|
||||||
|
id int [pk]
|
||||||
|
user_id int [ref: > users.id]
|
||||||
|
title varchar
|
||||||
|
content text
|
||||||
|
}`}
|
||||||
|
language={'dbml'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -43,8 +43,8 @@ const DDLInstructionsMap: Record<DatabaseType, DDLInstruction[]> = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Execute the following command in your terminal:',
|
text: 'Execute the following command in your terminal:',
|
||||||
code: `sqlite3 <database_file_path>\n.dump > <output_file_path>`,
|
code: `sqlite3 <database_file_path>\n".schema" > <output_file_path>`,
|
||||||
example: `sqlite3 my_db.db\n.dump > schema_export.sql`,
|
example: `sqlite3 my_db.db\n".schema" > schema_export.sql`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Open the exported SQL file, copy its contents, and paste them here.',
|
text: 'Open the exported SQL file, copy its contents, and paste them here.',
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export const SQLValidationStatus: React.FC<SQLValidationStatusProps> = ({
|
|||||||
|
|
||||||
{hasErrors ? (
|
{hasErrors ? (
|
||||||
<div className="rounded-md border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950">
|
<div className="rounded-md border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950">
|
||||||
<ScrollArea className="h-24">
|
<ScrollArea className="h-fit max-h-24">
|
||||||
<div className="space-y-3 p-3 pt-2 text-red-700 dark:text-red-300">
|
<div className="space-y-3 p-3 pt-2 text-red-700 dark:text-red-300">
|
||||||
{validation?.errors
|
{validation?.errors
|
||||||
.slice(0, 3)
|
.slice(0, 3)
|
||||||
@@ -137,7 +137,7 @@ export const SQLValidationStatus: React.FC<SQLValidationStatusProps> = ({
|
|||||||
|
|
||||||
{hasWarnings && !hasErrors ? (
|
{hasWarnings && !hasErrors ? (
|
||||||
<div className="rounded-md border border-sky-200 bg-sky-50 dark:border-sky-800 dark:bg-sky-950">
|
<div className="rounded-md border border-sky-200 bg-sky-50 dark:border-sky-800 dark:bg-sky-950">
|
||||||
<ScrollArea className="h-24">
|
<ScrollArea className="h-fit max-h-24">
|
||||||
<div className="space-y-3 p-3 pt-2 text-sky-700 dark:text-sky-300">
|
<div className="space-y-3 p-3 pt-2 text-sky-700 dark:text-sky-300">
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<AlertTriangle className="mt-0.5 size-4 shrink-0 text-sky-700 dark:text-sky-300" />
|
<AlertTriangle className="mt-0.5 size-4 shrink-0 text-sky-700 dark:text-sky-300" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Dialog, DialogContent } from '@/components/dialog/dialog';
|
|||||||
import { DatabaseType } from '@/lib/domain/database-type';
|
import { DatabaseType } from '@/lib/domain/database-type';
|
||||||
import { useStorage } from '@/hooks/use-storage';
|
import { useStorage } from '@/hooks/use-storage';
|
||||||
import type { Diagram } from '@/lib/domain/diagram';
|
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 { useNavigate } from 'react-router-dom';
|
||||||
import { useConfig } from '@/hooks/use-config';
|
import { useConfig } from '@/hooks/use-config';
|
||||||
import type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
import type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
||||||
@@ -22,6 +22,11 @@ import { sqlImportToDiagram } from '@/lib/data/sql-import';
|
|||||||
import type { SelectedTable } from '@/lib/data/import-metadata/filter-metadata';
|
import type { SelectedTable } from '@/lib/data/import-metadata/filter-metadata';
|
||||||
import { filterMetadataByTables } 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';
|
import { MAX_TABLES_WITHOUT_SHOWING_FILTER } from '../common/select-tables/constants';
|
||||||
|
import {
|
||||||
|
defaultDBMLDiagramName,
|
||||||
|
importDBMLToDiagram,
|
||||||
|
} from '@/lib/dbml/dbml-import/dbml-import';
|
||||||
|
import type { ImportMethod } from '@/lib/import-method/import-method';
|
||||||
|
|
||||||
export interface CreateDiagramDialogProps extends BaseDialogProps {}
|
export interface CreateDiagramDialogProps extends BaseDialogProps {}
|
||||||
|
|
||||||
@@ -30,11 +35,11 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { diagramId } = useChartDB();
|
const { diagramId } = useChartDB();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [importMethod, setImportMethod] = useState<'query' | 'ddl'>('query');
|
const [importMethod, setImportMethod] = useState<ImportMethod>('query');
|
||||||
const [databaseType, setDatabaseType] = useState<DatabaseType>(
|
const [databaseType, setDatabaseType] = useState<DatabaseType>(
|
||||||
DatabaseType.GENERIC
|
DatabaseType.GENERIC
|
||||||
);
|
);
|
||||||
const { closeCreateDiagramDialog, openImportDBMLDialog } = useDialog();
|
const { closeCreateDiagramDialog } = useDialog();
|
||||||
const { updateConfig } = useConfig();
|
const { updateConfig } = useConfig();
|
||||||
const [scriptResult, setScriptResult] = useState('');
|
const [scriptResult, setScriptResult] = useState('');
|
||||||
const [databaseEdition, setDatabaseEdition] = useState<
|
const [databaseEdition, setDatabaseEdition] = useState<
|
||||||
@@ -89,6 +94,14 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
|||||||
sourceDatabaseType: databaseType,
|
sourceDatabaseType: databaseType,
|
||||||
targetDatabaseType: databaseType,
|
targetDatabaseType: databaseType,
|
||||||
});
|
});
|
||||||
|
} else if (importMethod === 'dbml') {
|
||||||
|
diagram = await importDBMLToDiagram(scriptResult, {
|
||||||
|
databaseType,
|
||||||
|
});
|
||||||
|
// Update the diagram name if it's the default
|
||||||
|
if (diagram.name === defaultDBMLDiagramName) {
|
||||||
|
diagram.name = `Diagram ${diagramNumber}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let metadata: DatabaseMetadata | undefined = databaseMetadata;
|
let metadata: DatabaseMetadata | undefined = databaseMetadata;
|
||||||
|
|
||||||
@@ -152,10 +165,6 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
|||||||
await updateConfig({ config: { defaultDiagramId: diagram.id } });
|
await updateConfig({ config: { defaultDiagramId: diagram.id } });
|
||||||
closeCreateDiagramDialog();
|
closeCreateDiagramDialog();
|
||||||
navigate(`/diagrams/${diagram.id}`);
|
navigate(`/diagrams/${diagram.id}`);
|
||||||
setTimeout(
|
|
||||||
() => openImportDBMLDialog({ withCreateEmptyDiagram: true }),
|
|
||||||
700
|
|
||||||
);
|
|
||||||
}, [
|
}, [
|
||||||
databaseType,
|
databaseType,
|
||||||
addDiagram,
|
addDiagram,
|
||||||
@@ -164,14 +173,13 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
|||||||
navigate,
|
navigate,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
diagramNumber,
|
diagramNumber,
|
||||||
openImportDBMLDialog,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const importNewDiagramOrFilterTables = useCallback(async () => {
|
const importNewDiagramOrFilterTables = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setIsParsingMetadata(true);
|
setIsParsingMetadata(true);
|
||||||
|
|
||||||
if (importMethod === 'ddl') {
|
if (importMethod === 'ddl' || importMethod === 'dbml') {
|
||||||
await importNewDiagram();
|
await importNewDiagram();
|
||||||
} else {
|
} else {
|
||||||
// Parse metadata asynchronously to avoid blocking the UI
|
// Parse metadata asynchronously to avoid blocking the UI
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { ToggleGroup } from '@/components/toggle/toggle-group';
|
import { ToggleGroup } from '@/components/toggle/toggle-group';
|
||||||
import { DatabaseType } from '@/lib/domain/database-type';
|
import { DatabaseType } from '@/lib/domain/database-type';
|
||||||
import { DatabaseOption } from './database-option';
|
import { DatabaseOption } from './database-option';
|
||||||
import { ExampleOption } from './example-option';
|
import { ExampleOption } from './example-option';
|
||||||
import { Button } from '@/components/button/button';
|
import { Button } from '@/components/button/button';
|
||||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
|
import {
|
||||||
|
Tabs,
|
||||||
|
TabsContent,
|
||||||
|
TabsList,
|
||||||
|
TabsTrigger,
|
||||||
|
} from '@/components/tabs/tabs';
|
||||||
export interface SelectDatabaseContentProps {
|
export interface SelectDatabaseContentProps {
|
||||||
databaseType: DatabaseType;
|
databaseType: DatabaseType;
|
||||||
setDatabaseType: React.Dispatch<React.SetStateAction<DatabaseType>>;
|
setDatabaseType: React.Dispatch<React.SetStateAction<DatabaseType>>;
|
||||||
onContinue: () => void;
|
onContinue: (selectedDatabaseType: DatabaseType) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ROW_SIZE = 3;
|
const ROW_SIZE = 3;
|
||||||
const ROWS = 2;
|
const ROWS = 2;
|
||||||
const TOTAL_SLOTS = ROW_SIZE * ROWS;
|
const TOTAL_SLOTS = ROW_SIZE * ROWS;
|
||||||
const SUPPORTED_DB_TYPES: DatabaseType[] = [
|
|
||||||
|
// Transactional databases - OLTP systems optimized for frequent read/write operations
|
||||||
|
const TRANSACTIONAL_DB_TYPES: DatabaseType[] = [
|
||||||
DatabaseType.MYSQL,
|
DatabaseType.MYSQL,
|
||||||
DatabaseType.POSTGRESQL,
|
DatabaseType.POSTGRESQL,
|
||||||
DatabaseType.MARIADB,
|
DatabaseType.MARIADB,
|
||||||
@@ -22,69 +30,87 @@ const SUPPORTED_DB_TYPES: DatabaseType[] = [
|
|||||||
DatabaseType.SQL_SERVER,
|
DatabaseType.SQL_SERVER,
|
||||||
DatabaseType.ORACLE,
|
DatabaseType.ORACLE,
|
||||||
DatabaseType.COCKROACHDB,
|
DatabaseType.COCKROACHDB,
|
||||||
DatabaseType.CLICKHOUSE,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Analytical databases - OLAP systems optimized for complex queries and analytics
|
||||||
|
const ANALYTICAL_DB_TYPES: DatabaseType[] = [DatabaseType.CLICKHOUSE];
|
||||||
|
|
||||||
export const SelectDatabaseContent: React.FC<SelectDatabaseContentProps> = ({
|
export const SelectDatabaseContent: React.FC<SelectDatabaseContentProps> = ({
|
||||||
databaseType,
|
databaseType,
|
||||||
setDatabaseType,
|
setDatabaseType,
|
||||||
onContinue,
|
onContinue,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [activeTab, setActiveTab] = useState<'transactional' | 'analytical'>(
|
||||||
|
'transactional'
|
||||||
|
);
|
||||||
const [currentRow, setCurrentRow] = useState(0);
|
const [currentRow, setCurrentRow] = useState(0);
|
||||||
|
|
||||||
|
const currentDbTypes =
|
||||||
|
activeTab === 'transactional'
|
||||||
|
? TRANSACTIONAL_DB_TYPES
|
||||||
|
: ANALYTICAL_DB_TYPES;
|
||||||
|
|
||||||
const currentDatabasesTypes = useMemo(
|
const currentDatabasesTypes = useMemo(
|
||||||
() =>
|
() =>
|
||||||
SUPPORTED_DB_TYPES.slice(
|
currentDbTypes.slice(
|
||||||
currentRow * ROW_SIZE,
|
currentRow * ROW_SIZE,
|
||||||
currentRow * ROW_SIZE + TOTAL_SLOTS
|
currentRow * ROW_SIZE + TOTAL_SLOTS
|
||||||
),
|
),
|
||||||
[currentRow]
|
[currentRow, currentDbTypes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasNextRow = useMemo(
|
const hasNextRow = useMemo(
|
||||||
() => (currentRow + 1) * ROW_SIZE < SUPPORTED_DB_TYPES.length,
|
() => (currentRow + 1) * ROW_SIZE < currentDbTypes.length,
|
||||||
[currentRow]
|
[currentRow, currentDbTypes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasPreviousRow = useMemo(() => currentRow > 0, [currentRow]);
|
const hasPreviousRow = useMemo(() => currentRow > 0, [currentRow]);
|
||||||
|
|
||||||
const toggleRow = () => {
|
const toggleRow = useCallback(() => {
|
||||||
if (currentRow === 0 && hasNextRow) {
|
if (currentRow === 0 && hasNextRow) {
|
||||||
setCurrentRow(currentRow + 1);
|
setCurrentRow(currentRow + 1);
|
||||||
} else if (currentRow > 0) {
|
} else if (currentRow > 0) {
|
||||||
setCurrentRow(currentRow - 1);
|
setCurrentRow(currentRow - 1);
|
||||||
}
|
}
|
||||||
};
|
}, [currentRow, hasNextRow]);
|
||||||
|
|
||||||
return (
|
const handleTabChange = useCallback((value: string) => {
|
||||||
<div className="flex flex-1 flex-col items-center justify-center gap-4">
|
setActiveTab(value as 'transactional' | 'analytical');
|
||||||
<ToggleGroup
|
setCurrentRow(0); // Reset to first row when switching tabs
|
||||||
value={databaseType}
|
}, []);
|
||||||
onValueChange={(value: DatabaseType) => {
|
|
||||||
if (!value) {
|
|
||||||
setDatabaseType(DatabaseType.GENERIC);
|
|
||||||
} else {
|
|
||||||
setDatabaseType(value);
|
|
||||||
onContinue();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
type="single"
|
|
||||||
className="grid grid-flow-row grid-cols-3 gap-6"
|
|
||||||
>
|
|
||||||
{Array.from({ length: TOTAL_SLOTS }).map((_, index) =>
|
|
||||||
currentDatabasesTypes?.[index] ? (
|
|
||||||
<DatabaseOption
|
|
||||||
key={currentDatabasesTypes[index]}
|
|
||||||
type={currentDatabasesTypes[index]}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="col-span-3 flex flex-1 flex-col gap-1">
|
const renderDatabaseGrid = useCallback(
|
||||||
|
() => (
|
||||||
|
<div className="flex min-h-[280px] flex-col md:min-h-[370px]">
|
||||||
|
<ToggleGroup
|
||||||
|
value={databaseType}
|
||||||
|
onValueChange={(value: DatabaseType) => {
|
||||||
|
if (!value) {
|
||||||
|
setDatabaseType(DatabaseType.GENERIC);
|
||||||
|
} else {
|
||||||
|
setDatabaseType(value);
|
||||||
|
onContinue(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="single"
|
||||||
|
className="grid grid-flow-row grid-cols-3 content-start gap-4"
|
||||||
|
>
|
||||||
|
{Array.from({ length: TOTAL_SLOTS }).map((_, index) =>
|
||||||
|
currentDatabasesTypes?.[index] ? (
|
||||||
|
<DatabaseOption
|
||||||
|
key={currentDatabasesTypes[index]}
|
||||||
|
type={currentDatabasesTypes[index]}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
|
</ToggleGroup>
|
||||||
|
|
||||||
|
<div className="mt-auto flex flex-col gap-1 pt-4">
|
||||||
{hasNextRow || hasPreviousRow ? (
|
{hasNextRow || hasPreviousRow ? (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={toggleRow}
|
onClick={toggleRow}
|
||||||
className="col-span-3 h-8"
|
className="h-8"
|
||||||
>
|
>
|
||||||
{currentRow === 0 ? (
|
{currentRow === 0 ? (
|
||||||
<div className="flex h-8 w-full cursor-pointer flex-row items-center justify-center gap-2 py-3 text-center md:h-12">
|
<div className="flex h-8 w-full cursor-pointer flex-row items-center justify-center gap-2 py-3 text-center md:h-12">
|
||||||
@@ -105,7 +131,55 @@ export const SelectDatabaseContent: React.FC<SelectDatabaseContentProps> = ({
|
|||||||
) : null}
|
) : null}
|
||||||
<ExampleOption />
|
<ExampleOption />
|
||||||
</div>
|
</div>
|
||||||
</ToggleGroup>
|
</div>
|
||||||
|
),
|
||||||
|
[
|
||||||
|
databaseType,
|
||||||
|
currentDatabasesTypes,
|
||||||
|
hasNextRow,
|
||||||
|
hasPreviousRow,
|
||||||
|
onContinue,
|
||||||
|
setDatabaseType,
|
||||||
|
toggleRow,
|
||||||
|
currentRow,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-1 flex-col items-center gap-2">
|
||||||
|
<Tabs
|
||||||
|
defaultValue="transactional"
|
||||||
|
value={activeTab}
|
||||||
|
onValueChange={handleTabChange}
|
||||||
|
className="w-auto"
|
||||||
|
>
|
||||||
|
<TabsList className="mb-2 grid size-auto grid-cols-2 gap-1 rounded-xl border bg-background p-1">
|
||||||
|
<TabsTrigger
|
||||||
|
value="transactional"
|
||||||
|
className="gap-1.5 rounded-lg px-3 py-1 text-sm font-medium transition-all data-[state=active]:bg-sky-600 data-[state=active]:text-white data-[state=inactive]:text-muted-foreground data-[state=active]:shadow-sm data-[state=inactive]:hover:bg-muted/50 data-[state=inactive]:hover:text-foreground dark:data-[state=active]:bg-sky-500"
|
||||||
|
>
|
||||||
|
Transactional
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value="analytical"
|
||||||
|
className="gap-1.5 rounded-lg px-3 py-1 text-sm font-medium transition-all data-[state=active]:bg-sky-600 data-[state=active]:text-white data-[state=inactive]:text-muted-foreground data-[state=active]:shadow-sm data-[state=inactive]:hover:bg-muted/50 data-[state=inactive]:hover:text-foreground dark:data-[state=active]:bg-sky-500"
|
||||||
|
>
|
||||||
|
Analytical
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent
|
||||||
|
value="transactional"
|
||||||
|
className="mt-0 focus-visible:ring-0 focus-visible:ring-offset-0"
|
||||||
|
>
|
||||||
|
{renderDatabaseGrid()}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent
|
||||||
|
value="analytical"
|
||||||
|
className="mt-0 focus-visible:ring-0 focus-visible:ring-offset-0"
|
||||||
|
>
|
||||||
|
{renderDatabaseGrid()}
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export const SelectDatabase: React.FC<SelectDatabaseProps> = ({
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={createNewDiagram}
|
onClick={createNewDiagram}
|
||||||
|
disabled={databaseType === DatabaseType.GENERIC}
|
||||||
>
|
>
|
||||||
{t('new_diagram_dialog.empty_diagram')}
|
{t('new_diagram_dialog.empty_diagram')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -218,8 +218,14 @@ export const CreateRelationshipDialog: React.FC<
|
|||||||
closeCreateRelationshipDialog();
|
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>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{t('create_relationship_dialog.title')}
|
{t('create_relationship_dialog.title')}
|
||||||
|
|||||||
@@ -17,15 +17,21 @@ import { useDialog } from '@/hooks/use-dialog';
|
|||||||
import {
|
import {
|
||||||
exportBaseSQL,
|
exportBaseSQL,
|
||||||
exportSQL,
|
exportSQL,
|
||||||
} from '@/lib/data/export-metadata/export-sql-script';
|
} from '@/lib/data/sql-export/export-sql-script';
|
||||||
import { databaseTypeToLabelMap } from '@/lib/databases';
|
import { databaseTypeToLabelMap } from '@/lib/databases';
|
||||||
import { DatabaseType } from '@/lib/domain/database-type';
|
import { DatabaseType } from '@/lib/domain/database-type';
|
||||||
import { shouldShowTablesBySchemaFilter } from '@/lib/domain/db-table';
|
|
||||||
import { Annoyed, Sparkles } from 'lucide-react';
|
import { Annoyed, Sparkles } from 'lucide-react';
|
||||||
import React, { useCallback, useEffect, useRef } from 'react';
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||||
import type { Diagram } from '@/lib/domain/diagram';
|
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 {
|
export interface ExportSQLDialogProps extends BaseDialogProps {
|
||||||
targetDatabaseType: DatabaseType;
|
targetDatabaseType: DatabaseType;
|
||||||
@@ -36,7 +42,8 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
|||||||
targetDatabaseType,
|
targetDatabaseType,
|
||||||
}) => {
|
}) => {
|
||||||
const { closeExportSQLDialog } = useDialog();
|
const { closeExportSQLDialog } = useDialog();
|
||||||
const { currentDiagram, filteredSchemas } = useChartDB();
|
const { currentDiagram } = useChartDB();
|
||||||
|
const { filter } = useDiagramFilter();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [script, setScript] = React.useState<string>();
|
const [script, setScript] = React.useState<string>();
|
||||||
const [error, setError] = React.useState<boolean>(false);
|
const [error, setError] = React.useState<boolean>(false);
|
||||||
@@ -48,7 +55,16 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
|||||||
const filteredDiagram: Diagram = {
|
const filteredDiagram: Diagram = {
|
||||||
...currentDiagram,
|
...currentDiagram,
|
||||||
tables: currentDiagram.tables?.filter((table) =>
|
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) => {
|
relationships: currentDiagram.relationships?.filter((rel) => {
|
||||||
const sourceTable = currentDiagram.tables?.find(
|
const sourceTable = currentDiagram.tables?.find(
|
||||||
@@ -60,11 +76,20 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
|||||||
return (
|
return (
|
||||||
sourceTable &&
|
sourceTable &&
|
||||||
targetTable &&
|
targetTable &&
|
||||||
shouldShowTablesBySchemaFilter(
|
filterRelationship({
|
||||||
sourceTable,
|
tableA: {
|
||||||
filteredSchemas
|
id: sourceTable.id,
|
||||||
) &&
|
schema: sourceTable.schema,
|
||||||
shouldShowTablesBySchemaFilter(targetTable, filteredSchemas)
|
},
|
||||||
|
tableB: {
|
||||||
|
id: targetTable.id,
|
||||||
|
schema: targetTable.schema,
|
||||||
|
},
|
||||||
|
filter,
|
||||||
|
options: {
|
||||||
|
defaultSchema: defaultSchemas[targetDatabaseType],
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
dependencies: currentDiagram.dependencies?.filter((dep) => {
|
dependencies: currentDiagram.dependencies?.filter((dep) => {
|
||||||
@@ -77,11 +102,20 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
|
|||||||
return (
|
return (
|
||||||
table &&
|
table &&
|
||||||
dependentTable &&
|
dependentTable &&
|
||||||
shouldShowTablesBySchemaFilter(table, filteredSchemas) &&
|
filterDependency({
|
||||||
shouldShowTablesBySchemaFilter(
|
tableA: {
|
||||||
dependentTable,
|
id: table.id,
|
||||||
filteredSchemas
|
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,
|
signal: abortControllerRef.current?.signal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [targetDatabaseType, currentDiagram, filteredSchemas]);
|
}, [targetDatabaseType, currentDiagram, filter]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!dialog.open) {
|
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 type { DatabaseMetadata } from '@/lib/data/import-metadata/metadata-types/database-metadata';
|
||||||
import { loadDatabaseMetadata } 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 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 { useChartDB } from '@/hooks/use-chartdb';
|
||||||
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
@@ -15,6 +15,8 @@ import { useReactFlow } from '@xyflow/react';
|
|||||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||||
import { useAlert } from '@/context/alert-context/alert-context';
|
import { useAlert } from '@/context/alert-context/alert-context';
|
||||||
import { sqlImportToDiagram } from '@/lib/data/sql-import';
|
import { sqlImportToDiagram } from '@/lib/data/sql-import';
|
||||||
|
import { importDBMLToDiagram } from '@/lib/dbml/dbml-import/dbml-import';
|
||||||
|
import type { ImportMethod } from '@/lib/import-method/import-method';
|
||||||
|
|
||||||
export interface ImportDatabaseDialogProps extends BaseDialogProps {
|
export interface ImportDatabaseDialogProps extends BaseDialogProps {
|
||||||
databaseType: DatabaseType;
|
databaseType: DatabaseType;
|
||||||
@@ -24,7 +26,7 @@ export const ImportDatabaseDialog: React.FC<ImportDatabaseDialogProps> = ({
|
|||||||
dialog,
|
dialog,
|
||||||
databaseType,
|
databaseType,
|
||||||
}) => {
|
}) => {
|
||||||
const [importMethod, setImportMethod] = useState<'query' | 'ddl'>('query');
|
const [importMethod, setImportMethod] = useState<ImportMethod>('query');
|
||||||
const { closeImportDatabaseDialog } = useDialog();
|
const { closeImportDatabaseDialog } = useDialog();
|
||||||
const { showAlert } = useAlert();
|
const { showAlert } = useAlert();
|
||||||
const {
|
const {
|
||||||
@@ -65,6 +67,10 @@ export const ImportDatabaseDialog: React.FC<ImportDatabaseDialogProps> = ({
|
|||||||
sourceDatabaseType: databaseType,
|
sourceDatabaseType: databaseType,
|
||||||
targetDatabaseType: databaseType,
|
targetDatabaseType: databaseType,
|
||||||
});
|
});
|
||||||
|
} else if (importMethod === 'dbml') {
|
||||||
|
diagram = await importDBMLToDiagram(scriptResult, {
|
||||||
|
databaseType,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const databaseMetadata: DatabaseMetadata =
|
const databaseMetadata: DatabaseMetadata =
|
||||||
loadDatabaseMetadata(scriptResult);
|
loadDatabaseMetadata(scriptResult);
|
||||||
|
|||||||
@@ -1,359 +0,0 @@
|
|||||||
import React, {
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
Suspense,
|
|
||||||
useRef,
|
|
||||||
} from 'react';
|
|
||||||
import type * as monaco from 'monaco-editor';
|
|
||||||
import { useDialog } from '@/hooks/use-dialog';
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogClose,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogInternalContent,
|
|
||||||
DialogTitle,
|
|
||||||
} from '@/components/dialog/dialog';
|
|
||||||
import { Button } from '@/components/button/button';
|
|
||||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
|
||||||
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,
|
|
||||||
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';
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ImportDBMLDialog: React.FC<ImportDBMLDialogProps> = ({
|
|
||||||
dialog,
|
|
||||||
withCreateEmptyDiagram,
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const initialDBML = `// Use DBML to define your database structure
|
|
||||||
// Simple Blog System with Comments Example
|
|
||||||
|
|
||||||
Table users {
|
|
||||||
id integer [primary key]
|
|
||||||
name varchar
|
|
||||||
email varchar
|
|
||||||
}
|
|
||||||
|
|
||||||
Table posts {
|
|
||||||
id integer [primary key]
|
|
||||||
title varchar
|
|
||||||
content text
|
|
||||||
user_id integer
|
|
||||||
created_at timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
Table comments {
|
|
||||||
id integer [primary key]
|
|
||||||
content text
|
|
||||||
post_id integer
|
|
||||||
user_id integer
|
|
||||||
created_at timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relationships
|
|
||||||
Ref: posts.user_id > users.id // Each post belongs to one user
|
|
||||||
Ref: comments.post_id > posts.id // Each comment belongs to one post
|
|
||||||
Ref: comments.user_id > users.id // Each comment is written by one user`;
|
|
||||||
|
|
||||||
const [dbmlContent, setDBMLContent] = useState<string>(initialDBML);
|
|
||||||
const { closeImportDBMLDialog } = useDialog();
|
|
||||||
const [errorMessage, setErrorMessage] = useState<string | undefined>();
|
|
||||||
const { effectiveTheme } = useTheme();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const {
|
|
||||||
addTables,
|
|
||||||
addRelationships,
|
|
||||||
tables,
|
|
||||||
relationships,
|
|
||||||
removeTables,
|
|
||||||
removeRelationships,
|
|
||||||
} = useChartDB();
|
|
||||||
const { reorderTables } = useCanvas();
|
|
||||||
const [reorder, setReorder] = useState(false);
|
|
||||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
|
|
||||||
const decorationsCollection =
|
|
||||||
useRef<monaco.editor.IEditorDecorationsCollection>();
|
|
||||||
|
|
||||||
const handleEditorDidMount = (
|
|
||||||
editor: monaco.editor.IStandaloneCodeEditor
|
|
||||||
) => {
|
|
||||||
editorRef.current = editor;
|
|
||||||
decorationsCollection.current = editor.createDecorationsCollection();
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (reorder) {
|
|
||||||
reorderTables({
|
|
||||||
updateHistory: false,
|
|
||||||
});
|
|
||||||
setReorder(false);
|
|
||||||
}
|
|
||||||
}, [reorder, reorderTables]);
|
|
||||||
|
|
||||||
const clearDecorations = useCallback(() => {
|
|
||||||
clearErrorHighlight(decorationsCollection.current);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const validateDBML = useCallback(
|
|
||||||
async (content: string) => {
|
|
||||||
// Clear previous errors
|
|
||||||
setErrorMessage(undefined);
|
|
||||||
clearDecorations();
|
|
||||||
|
|
||||||
if (!content.trim()) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const preprocessedContent = preprocessDBML(content);
|
|
||||||
const sanitizedContent = sanitizeDBML(preprocessedContent);
|
|
||||||
const parser = new Parser();
|
|
||||||
parser.parse(sanitizedContent, 'dbml');
|
|
||||||
} catch (e) {
|
|
||||||
const parsedError = parseDBMLError(e);
|
|
||||||
if (parsedError) {
|
|
||||||
setErrorMessage(
|
|
||||||
t('import_dbml_dialog.error.description') +
|
|
||||||
` (1 error found - in line ${parsedError.line})`
|
|
||||||
);
|
|
||||||
highlightErrorLine({
|
|
||||||
error: parsedError,
|
|
||||||
model: editorRef.current?.getModel(),
|
|
||||||
editorDecorationsCollection:
|
|
||||||
decorationsCollection.current,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setErrorMessage(
|
|
||||||
e instanceof Error ? e.message : JSON.stringify(e)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[clearDecorations, t]
|
|
||||||
);
|
|
||||||
|
|
||||||
const debouncedValidateRef = useRef<((value: string) => void) | null>(null);
|
|
||||||
|
|
||||||
// Set up debounced validation
|
|
||||||
useEffect(() => {
|
|
||||||
debouncedValidateRef.current = debounce((value: string) => {
|
|
||||||
validateDBML(value);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
debouncedValidateRef.current = null;
|
|
||||||
};
|
|
||||||
}, [validateDBML]);
|
|
||||||
|
|
||||||
// Trigger validation when content changes
|
|
||||||
useEffect(() => {
|
|
||||||
if (debouncedValidateRef.current) {
|
|
||||||
debouncedValidateRef.current(dbmlContent);
|
|
||||||
}
|
|
||||||
}, [dbmlContent]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!dialog.open) {
|
|
||||||
setErrorMessage(undefined);
|
|
||||||
clearDecorations();
|
|
||||||
setDBMLContent(initialDBML);
|
|
||||||
}
|
|
||||||
}, [dialog.open, initialDBML, clearDecorations]);
|
|
||||||
|
|
||||||
const handleImport = useCallback(async () => {
|
|
||||||
if (!dbmlContent.trim() || errorMessage) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const importedDiagram = await importDBMLToDiagram(dbmlContent);
|
|
||||||
const tableIdsToRemove = tables
|
|
||||||
.filter((table) =>
|
|
||||||
importedDiagram.tables?.some(
|
|
||||||
(t: DBTable) =>
|
|
||||||
t.name === table.name && t.schema === table.schema
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.map((table) => table.id);
|
|
||||||
// Find relationships that need to be removed
|
|
||||||
const relationshipIdsToRemove = relationships
|
|
||||||
.filter((relationship) => {
|
|
||||||
const sourceTable = tables.find(
|
|
||||||
(table: DBTable) =>
|
|
||||||
table.id === relationship.sourceTableId
|
|
||||||
);
|
|
||||||
const targetTable = tables.find(
|
|
||||||
(table: DBTable) =>
|
|
||||||
table.id === relationship.targetTableId
|
|
||||||
);
|
|
||||||
if (!sourceTable || !targetTable) return true;
|
|
||||||
const replacementSourceTable = importedDiagram.tables?.find(
|
|
||||||
(table: DBTable) =>
|
|
||||||
table.name === sourceTable.name &&
|
|
||||||
table.schema === sourceTable.schema
|
|
||||||
);
|
|
||||||
const replacementTargetTable = importedDiagram.tables?.find(
|
|
||||||
(table: DBTable) =>
|
|
||||||
table.name === targetTable.name &&
|
|
||||||
table.schema === targetTable.schema
|
|
||||||
);
|
|
||||||
return replacementSourceTable || replacementTargetTable;
|
|
||||||
})
|
|
||||||
.map((relationship) => relationship.id);
|
|
||||||
|
|
||||||
// Remove existing items
|
|
||||||
await Promise.all([
|
|
||||||
removeTables(tableIdsToRemove, { updateHistory: false }),
|
|
||||||
removeRelationships(relationshipIdsToRemove, {
|
|
||||||
updateHistory: false,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Add new items
|
|
||||||
await Promise.all([
|
|
||||||
addTables(importedDiagram.tables ?? [], {
|
|
||||||
updateHistory: false,
|
|
||||||
}),
|
|
||||||
addRelationships(importedDiagram.relationships ?? [], {
|
|
||||||
updateHistory: false,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
setReorder(true);
|
|
||||||
closeImportDBMLDialog();
|
|
||||||
} catch (e) {
|
|
||||||
toast({
|
|
||||||
title: t('import_dbml_dialog.error.title'),
|
|
||||||
variant: 'destructive',
|
|
||||||
description: (
|
|
||||||
<>
|
|
||||||
<div>{t('import_dbml_dialog.error.description')}</div>
|
|
||||||
{e instanceof Error ? e.message : JSON.stringify(e)}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
dbmlContent,
|
|
||||||
closeImportDBMLDialog,
|
|
||||||
tables,
|
|
||||||
relationships,
|
|
||||||
removeTables,
|
|
||||||
removeRelationships,
|
|
||||||
addTables,
|
|
||||||
addRelationships,
|
|
||||||
errorMessage,
|
|
||||||
toast,
|
|
||||||
setReorder,
|
|
||||||
t,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
{...dialog}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
closeImportDBMLDialog();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DialogContent
|
|
||||||
className="flex h-[80vh] max-h-screen w-full flex-col md:max-w-[900px]"
|
|
||||||
showClose
|
|
||||||
>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>
|
|
||||||
{withCreateEmptyDiagram
|
|
||||||
? t('import_dbml_dialog.example_title')
|
|
||||||
: t('import_dbml_dialog.title')}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
{t('import_dbml_dialog.description')}
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<DialogInternalContent>
|
|
||||||
<Suspense fallback={<Spinner />}>
|
|
||||||
<Editor
|
|
||||||
value={dbmlContent}
|
|
||||||
onChange={(value) => setDBMLContent(value || '')}
|
|
||||||
language="dbml"
|
|
||||||
onMount={handleEditorDidMount}
|
|
||||||
theme={
|
|
||||||
effectiveTheme === 'dark'
|
|
||||||
? 'dbml-dark'
|
|
||||||
: 'dbml-light'
|
|
||||||
}
|
|
||||||
beforeMount={setupDBMLLanguage}
|
|
||||||
options={{
|
|
||||||
minimap: { enabled: false },
|
|
||||||
scrollBeyondLastLine: false,
|
|
||||||
automaticLayout: true,
|
|
||||||
glyphMargin: true,
|
|
||||||
lineNumbers: 'on',
|
|
||||||
scrollbar: {
|
|
||||||
vertical: 'visible',
|
|
||||||
horizontal: 'visible',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
className="size-full"
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
</DialogInternalContent>
|
|
||||||
<DialogFooter>
|
|
||||||
<div className="flex w-full items-center justify-between">
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button variant="secondary">
|
|
||||||
{withCreateEmptyDiagram
|
|
||||||
? t('import_dbml_dialog.skip_and_empty')
|
|
||||||
: t('import_dbml_dialog.cancel')}
|
|
||||||
</Button>
|
|
||||||
</DialogClose>
|
|
||||||
{errorMessage ? (
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<AlertCircle className="size-4 text-destructive" />
|
|
||||||
|
|
||||||
<span className="text-xs text-destructive">
|
|
||||||
{errorMessage ||
|
|
||||||
t(
|
|
||||||
'import_dbml_dialog.error.description'
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
onClick={handleImport}
|
|
||||||
disabled={!dbmlContent.trim() || !!errorMessage}
|
|
||||||
>
|
|
||||||
{withCreateEmptyDiagram
|
|
||||||
? t('import_dbml_dialog.show_example')
|
|
||||||
: t('import_dbml_dialog.import')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -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 { useNavigate } from 'react-router-dom';
|
||||||
import type { BaseDialogProps } from '../common/base-dialog-props';
|
import type { BaseDialogProps } from '../common/base-dialog-props';
|
||||||
import { useDebounce } from '@/hooks/use-debounce';
|
import { useDebounce } from '@/hooks/use-debounce';
|
||||||
|
import { DiagramRowActionsMenu } from './diagram-row-actions-menu/diagram-row-actions-menu';
|
||||||
|
|
||||||
export interface OpenDiagramDialogProps extends BaseDialogProps {
|
export interface OpenDiagramDialogProps extends BaseDialogProps {
|
||||||
canClose?: boolean;
|
canClose?: boolean;
|
||||||
@@ -46,21 +47,22 @@ export const OpenDiagramDialog: React.FC<OpenDiagramDialogProps> = ({
|
|||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
|
|
||||||
useEffect(() => {
|
const fetchDiagrams = useCallback(async () => {
|
||||||
setSelectedDiagramId(undefined);
|
const diagrams = await listDiagrams({ includeTables: true });
|
||||||
}, [dialog.open]);
|
setDiagrams(
|
||||||
|
diagrams.sort(
|
||||||
|
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [listDiagrams]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchDiagrams = async () => {
|
if (!dialog.open) {
|
||||||
const diagrams = await listDiagrams({ includeTables: true });
|
return;
|
||||||
setDiagrams(
|
}
|
||||||
diagrams.sort(
|
setSelectedDiagramId(undefined);
|
||||||
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
fetchDiagrams();
|
fetchDiagrams();
|
||||||
}, [listDiagrams, setDiagrams, dialog.open]);
|
}, [dialog.open, fetchDiagrams]);
|
||||||
|
|
||||||
const openDiagram = useCallback(
|
const openDiagram = useCallback(
|
||||||
(diagramId: string) => {
|
(diagramId: string) => {
|
||||||
@@ -166,6 +168,7 @@ export const OpenDiagramDialog: React.FC<OpenDiagramDialogProps> = ({
|
|||||||
'open_diagram_dialog.table_columns.tables_count'
|
'open_diagram_dialog.table_columns.tables_count'
|
||||||
)}
|
)}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
|
<TableHead />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -221,6 +224,19 @@ export const OpenDiagramDialog: React.FC<OpenDiagramDialogProps> = ({
|
|||||||
<TableCell className="text-center">
|
<TableCell className="text-center">
|
||||||
{diagram.tables?.length}
|
{diagram.tables?.length}
|
||||||
</TableCell>
|
</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>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
|||||||
allowSchemaCreation = false,
|
allowSchemaCreation = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { databaseType, filteredSchemas, filterSchemas } = useChartDB();
|
const { databaseType } = useChartDB();
|
||||||
const [selectedSchemaId, setSelectedSchemaId] = useState<string>(
|
const [selectedSchemaId, setSelectedSchemaId] = useState<string>(
|
||||||
table?.schema
|
table?.schema
|
||||||
? schemaNameToSchemaId(table.schema)
|
? schemaNameToSchemaId(table.schema)
|
||||||
@@ -93,7 +93,6 @@ export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
|||||||
const { closeTableSchemaDialog } = useDialog();
|
const { closeTableSchemaDialog } = useDialog();
|
||||||
|
|
||||||
const handleConfirm = useCallback(() => {
|
const handleConfirm = useCallback(() => {
|
||||||
let createdSchemaId: string;
|
|
||||||
if (isCreatingNew && newSchemaName.trim()) {
|
if (isCreatingNew && newSchemaName.trim()) {
|
||||||
const newSchema: DBSchema = {
|
const newSchema: DBSchema = {
|
||||||
id: schemaNameToSchemaId(newSchemaName.trim()),
|
id: schemaNameToSchemaId(newSchemaName.trim()),
|
||||||
@@ -101,30 +100,14 @@ export const TableSchemaDialog: React.FC<TableSchemaDialogProps> = ({
|
|||||||
tableCount: 0,
|
tableCount: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
createdSchemaId = newSchema.id;
|
|
||||||
|
|
||||||
onConfirm({ schema: newSchema });
|
onConfirm({ schema: newSchema });
|
||||||
} else {
|
} else {
|
||||||
const schema = schemas.find((s) => s.id === selectedSchemaId);
|
const schema = schemas.find((s) => s.id === selectedSchemaId);
|
||||||
if (!schema) return;
|
if (!schema) return;
|
||||||
|
|
||||||
createdSchemaId = schema.id;
|
|
||||||
onConfirm({ schema });
|
onConfirm({ schema });
|
||||||
}
|
}
|
||||||
|
}, [onConfirm, selectedSchemaId, schemas, isCreatingNew, newSchemaName]);
|
||||||
filterSchemas([
|
|
||||||
...(filteredSchemas ?? schemas.map((s) => s.id)),
|
|
||||||
createdSchemaId,
|
|
||||||
]);
|
|
||||||
}, [
|
|
||||||
onConfirm,
|
|
||||||
selectedSchemaId,
|
|
||||||
schemas,
|
|
||||||
isCreatingNew,
|
|
||||||
newSchemaName,
|
|
||||||
filteredSchemas,
|
|
||||||
filterSchemas,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const schemaOptions: SelectBoxOption[] = useMemo(
|
const schemaOptions: SelectBoxOption[] = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
181
src/hooks/use-focus-on.ts
Normal file
181
src/hooks/use-focus-on.ts
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
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 focusOnNote = useCallback(
|
||||||
|
(noteId: string, options: FocusOptions = {}) => {
|
||||||
|
const { select = true } = options;
|
||||||
|
|
||||||
|
if (select) {
|
||||||
|
setNodes((nodes) =>
|
||||||
|
nodes.map((node) =>
|
||||||
|
node.id === noteId
|
||||||
|
? {
|
||||||
|
...node,
|
||||||
|
selected: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...node,
|
||||||
|
selected: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fitView({
|
||||||
|
duration: 500,
|
||||||
|
maxZoom: 1,
|
||||||
|
minZoom: 1,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: noteId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
focusOnNote,
|
||||||
|
focusOnRelationship,
|
||||||
|
};
|
||||||
|
};
|
||||||
379
src/hooks/use-update-table-field.ts
Normal file
379
src/hooks/use-update-table-field.ts
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
import { useCallback, useMemo, useState, useEffect, useRef } from 'react';
|
||||||
|
import { useChartDB } from './use-chartdb';
|
||||||
|
import { useDebounce } from './use-debounce-v2';
|
||||||
|
import type { DatabaseType, DBField, DBTable } from '@/lib/domain';
|
||||||
|
import type {
|
||||||
|
SelectBoxOption,
|
||||||
|
SelectBoxProps,
|
||||||
|
} from '@/components/select-box/select-box';
|
||||||
|
import {
|
||||||
|
dataTypeDataToDataType,
|
||||||
|
sortedDataTypeMap,
|
||||||
|
supportsArrayDataType,
|
||||||
|
autoIncrementAlwaysOn,
|
||||||
|
requiresNotNull,
|
||||||
|
} from '@/lib/data/data-types/data-types';
|
||||||
|
import { generateDBFieldSuffix } from '@/lib/domain/db-field';
|
||||||
|
import type { DataTypeData } from '@/lib/data/data-types/data-types';
|
||||||
|
|
||||||
|
const generateFieldRegexPatterns = (
|
||||||
|
dataType: DataTypeData,
|
||||||
|
databaseType: DatabaseType
|
||||||
|
): {
|
||||||
|
regex?: string;
|
||||||
|
extractRegex?: RegExp;
|
||||||
|
} => {
|
||||||
|
const typeName = dataType.name;
|
||||||
|
const supportsArrays = supportsArrayDataType(dataType.id, databaseType);
|
||||||
|
const arrayPattern = supportsArrays ? '(\\[\\])?' : '';
|
||||||
|
|
||||||
|
if (!dataType.fieldAttributes) {
|
||||||
|
// For types without field attributes, support plain type + optional array notation
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}${arrayPattern}$`,
|
||||||
|
extractRegex: new RegExp(`^${typeName}${arrayPattern}$`),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldAttributes = dataType.fieldAttributes;
|
||||||
|
|
||||||
|
if (fieldAttributes.hasCharMaxLength) {
|
||||||
|
if (fieldAttributes.hasCharMaxLengthOption) {
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}\\((\\d+|[mM][aA][xX])\\)${arrayPattern}$`,
|
||||||
|
extractRegex: supportsArrays
|
||||||
|
? /\((\d+|max)\)(\[\])?/i
|
||||||
|
: /\((\d+|max)\)/i,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}\\(\\d+\\)${arrayPattern}$`,
|
||||||
|
extractRegex: supportsArrays ? /\((\d+)\)(\[\])?/ : /\((\d+)\)/,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldAttributes.precision && fieldAttributes.scale) {
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}\\s*\\(\\s*\\d+\\s*(?:,\\s*\\d+\\s*)?\\)${arrayPattern}$`,
|
||||||
|
extractRegex: new RegExp(
|
||||||
|
`${typeName}\\s*\\(\\s*(\\d+)\\s*(?:,\\s*(\\d+)\\s*)?\\)${arrayPattern}`
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldAttributes.precision) {
|
||||||
|
return {
|
||||||
|
regex: `^${typeName}\\s*\\(\\s*\\d+\\s*\\)${arrayPattern}$`,
|
||||||
|
extractRegex: supportsArrays ? /\((\d+)\)(\[\])?/ : /\((\d+)\)/,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { regex: undefined, extractRegex: undefined };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateTableField = (
|
||||||
|
table: DBTable,
|
||||||
|
field: DBField,
|
||||||
|
customUpdateField?: (attrs: Partial<DBField>) => void
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
|
databaseType,
|
||||||
|
customTypes,
|
||||||
|
updateField: chartDBUpdateField,
|
||||||
|
removeField: chartDBRemoveField,
|
||||||
|
} = useChartDB();
|
||||||
|
|
||||||
|
// Local state for responsive UI
|
||||||
|
const [localFieldName, setLocalFieldName] = useState(field.name);
|
||||||
|
const [localNullable, setLocalNullable] = useState(field.nullable);
|
||||||
|
const [localPrimaryKey, setLocalPrimaryKey] = useState(field.primaryKey);
|
||||||
|
|
||||||
|
const lastFieldNameRef = useRef<string>(field.name);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (localFieldName === lastFieldNameRef.current) {
|
||||||
|
lastFieldNameRef.current = field.name;
|
||||||
|
setLocalFieldName(field.name);
|
||||||
|
}
|
||||||
|
}, [field.name, localFieldName]);
|
||||||
|
|
||||||
|
// Update local state when field properties change externally
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalNullable(field.nullable);
|
||||||
|
setLocalPrimaryKey(field.primaryKey);
|
||||||
|
}, [field.nullable, field.primaryKey]);
|
||||||
|
|
||||||
|
// Use custom updateField if provided, otherwise use the chartDB one
|
||||||
|
const updateField = useMemo(
|
||||||
|
() =>
|
||||||
|
customUpdateField
|
||||||
|
? (
|
||||||
|
_tableId: string,
|
||||||
|
_fieldId: string,
|
||||||
|
attrs: Partial<DBField>
|
||||||
|
) => customUpdateField(attrs)
|
||||||
|
: chartDBUpdateField,
|
||||||
|
[customUpdateField, chartDBUpdateField]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate primary key fields for validation
|
||||||
|
const primaryKeyFields = useMemo(() => {
|
||||||
|
return table.fields.filter((f) => f.primaryKey);
|
||||||
|
}, [table.fields]);
|
||||||
|
|
||||||
|
const primaryKeyCount = useMemo(
|
||||||
|
() => primaryKeyFields.length,
|
||||||
|
[primaryKeyFields.length]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Generate data type options for select box
|
||||||
|
const dataFieldOptions = useMemo(() => {
|
||||||
|
const standardTypes: SelectBoxOption[] = sortedDataTypeMap[
|
||||||
|
databaseType
|
||||||
|
].map((type) => {
|
||||||
|
const regexPatterns = generateFieldRegexPatterns(
|
||||||
|
type,
|
||||||
|
databaseType
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: type.name,
|
||||||
|
value: type.id,
|
||||||
|
regex: regexPatterns.regex,
|
||||||
|
extractRegex: regexPatterns.extractRegex,
|
||||||
|
group: customTypes?.length ? 'Standard Types' : undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!customTypes?.length) {
|
||||||
|
return standardTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom types as options
|
||||||
|
const customTypeOptions: SelectBoxOption[] = customTypes.map(
|
||||||
|
(type) => ({
|
||||||
|
label: type.name,
|
||||||
|
value: type.name,
|
||||||
|
description:
|
||||||
|
type.kind === 'enum' ? `${type.values?.join(' | ')}` : '',
|
||||||
|
group: 'Custom Types',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return [...standardTypes, ...customTypeOptions];
|
||||||
|
}, [databaseType, customTypes]);
|
||||||
|
|
||||||
|
// Handle data type change
|
||||||
|
const handleDataTypeChange = useCallback<
|
||||||
|
NonNullable<SelectBoxProps['onChange']>
|
||||||
|
>(
|
||||||
|
(value, regexMatches) => {
|
||||||
|
const dataType = sortedDataTypeMap[databaseType].find(
|
||||||
|
(v) => v.id === value
|
||||||
|
) ?? {
|
||||||
|
id: value as string,
|
||||||
|
name: value as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
let characterMaximumLength: string | undefined = undefined;
|
||||||
|
let precision: number | undefined = undefined;
|
||||||
|
let scale: number | undefined = undefined;
|
||||||
|
let isArray: boolean | undefined = undefined;
|
||||||
|
|
||||||
|
if (regexMatches?.length) {
|
||||||
|
// Check if the last captured group is the array indicator []
|
||||||
|
const lastMatch = regexMatches[regexMatches.length - 1];
|
||||||
|
const hasArrayIndicator = lastMatch === '[]';
|
||||||
|
|
||||||
|
if (dataType?.fieldAttributes?.hasCharMaxLength) {
|
||||||
|
characterMaximumLength = regexMatches[1]?.toLowerCase();
|
||||||
|
} else if (
|
||||||
|
dataType?.fieldAttributes?.precision &&
|
||||||
|
dataType?.fieldAttributes?.scale
|
||||||
|
) {
|
||||||
|
precision = parseInt(regexMatches[1]);
|
||||||
|
scale = regexMatches[2]
|
||||||
|
? parseInt(regexMatches[2])
|
||||||
|
: undefined;
|
||||||
|
} else if (dataType?.fieldAttributes?.precision) {
|
||||||
|
precision = parseInt(regexMatches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set isArray if the array indicator was found and the type supports arrays
|
||||||
|
if (hasArrayIndicator) {
|
||||||
|
const typeId = value as string;
|
||||||
|
if (supportsArrayDataType(typeId, databaseType)) {
|
||||||
|
isArray = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Explicitly set to false/undefined if no array indicator
|
||||||
|
isArray = undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
dataType?.fieldAttributes?.hasCharMaxLength &&
|
||||||
|
field.characterMaximumLength
|
||||||
|
) {
|
||||||
|
characterMaximumLength = field.characterMaximumLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType?.fieldAttributes?.precision && field.precision) {
|
||||||
|
precision = field.precision;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType?.fieldAttributes?.scale && field.scale) {
|
||||||
|
scale = field.scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTypeName = dataType?.name ?? (value as string);
|
||||||
|
const typeRequiresNotNull = requiresNotNull(newTypeName);
|
||||||
|
const shouldForceIncrement = autoIncrementAlwaysOn(newTypeName);
|
||||||
|
|
||||||
|
updateField(table.id, field.id, {
|
||||||
|
characterMaximumLength,
|
||||||
|
precision,
|
||||||
|
scale,
|
||||||
|
isArray,
|
||||||
|
...(typeRequiresNotNull ? { nullable: false } : {}),
|
||||||
|
increment: shouldForceIncrement ? true : undefined,
|
||||||
|
default: undefined,
|
||||||
|
type: dataTypeDataToDataType(
|
||||||
|
dataType ?? {
|
||||||
|
id: value as string,
|
||||||
|
name: value as string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[
|
||||||
|
updateField,
|
||||||
|
databaseType,
|
||||||
|
field.characterMaximumLength,
|
||||||
|
field.precision,
|
||||||
|
field.scale,
|
||||||
|
field.id,
|
||||||
|
table.id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Debounced update for field name
|
||||||
|
const debouncedNameUpdate = useDebounce(
|
||||||
|
useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
if (value.trim() !== field.name) {
|
||||||
|
updateField(table.id, field.id, { name: value });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateField, table.id, field.id, field.name]
|
||||||
|
),
|
||||||
|
300 // 300ms debounce for text input
|
||||||
|
);
|
||||||
|
|
||||||
|
// Debounced update for nullable toggle
|
||||||
|
const debouncedNullableUpdate = useDebounce(
|
||||||
|
useCallback(
|
||||||
|
(value: boolean) => {
|
||||||
|
const updates: Partial<DBField> = { nullable: value };
|
||||||
|
|
||||||
|
// If setting to nullable, clear increment (auto-increment requires NOT NULL)
|
||||||
|
if (value && field.increment) {
|
||||||
|
updates.increment = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateField(table.id, field.id, updates);
|
||||||
|
},
|
||||||
|
[updateField, table.id, field.id, field.increment]
|
||||||
|
),
|
||||||
|
100 // 100ms debounce for toggle
|
||||||
|
);
|
||||||
|
|
||||||
|
// Debounced update for primary key toggle
|
||||||
|
const debouncedPrimaryKeyUpdate = useDebounce(
|
||||||
|
useCallback(
|
||||||
|
(value: boolean, primaryKeyCount: number) => {
|
||||||
|
if (value) {
|
||||||
|
// When setting as primary key
|
||||||
|
const updates: Partial<DBField> = {
|
||||||
|
primaryKey: true,
|
||||||
|
};
|
||||||
|
// Only auto-set unique if this will be the only primary key
|
||||||
|
if (primaryKeyCount === 0) {
|
||||||
|
updates.unique = true;
|
||||||
|
}
|
||||||
|
updateField(table.id, field.id, updates);
|
||||||
|
} else {
|
||||||
|
// When removing primary key
|
||||||
|
updateField(table.id, field.id, {
|
||||||
|
primaryKey: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateField, table.id, field.id]
|
||||||
|
),
|
||||||
|
100 // 100ms debounce for toggle
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle primary key toggle with optimistic update
|
||||||
|
const handlePrimaryKeyToggle = useCallback(
|
||||||
|
(value: boolean) => {
|
||||||
|
setLocalPrimaryKey(value);
|
||||||
|
debouncedPrimaryKeyUpdate(value, primaryKeyCount);
|
||||||
|
},
|
||||||
|
[primaryKeyCount, debouncedPrimaryKeyUpdate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle nullable toggle with optimistic update
|
||||||
|
const handleNullableToggle = useCallback(
|
||||||
|
(value: boolean) => {
|
||||||
|
setLocalNullable(value);
|
||||||
|
debouncedNullableUpdate(value);
|
||||||
|
},
|
||||||
|
[debouncedNullableUpdate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle name change with optimistic update
|
||||||
|
const handleNameChange = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
setLocalFieldName(value);
|
||||||
|
debouncedNameUpdate(value);
|
||||||
|
},
|
||||||
|
[debouncedNameUpdate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Utility function to generate field suffix for display
|
||||||
|
const generateFieldSuffix = useCallback(
|
||||||
|
(typeId?: string) => {
|
||||||
|
return generateDBFieldSuffix(
|
||||||
|
{
|
||||||
|
...field,
|
||||||
|
isArray: field.isArray && typeId === field.type.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
databaseType,
|
||||||
|
forceExtended: true,
|
||||||
|
typeId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[field, databaseType]
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeField = useCallback(() => {
|
||||||
|
chartDBRemoveField(table.id, field.id);
|
||||||
|
}, [chartDBRemoveField, table.id, field.id]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataFieldOptions,
|
||||||
|
handleDataTypeChange,
|
||||||
|
handlePrimaryKeyToggle,
|
||||||
|
handleNullableToggle,
|
||||||
|
handleNameChange,
|
||||||
|
generateFieldSuffix,
|
||||||
|
primaryKeyCount,
|
||||||
|
fieldName: localFieldName,
|
||||||
|
nullable: localNullable,
|
||||||
|
primaryKey: localPrimaryKey,
|
||||||
|
removeField,
|
||||||
|
};
|
||||||
|
};
|
||||||
42
src/hooks/use-update-table.ts
Normal file
42
src/hooks/use-update-table.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { useCallback, useState, useEffect } from 'react';
|
||||||
|
import { useChartDB } from './use-chartdb';
|
||||||
|
import { useDebounce } from './use-debounce-v2';
|
||||||
|
import type { DBTable } from '@/lib/domain';
|
||||||
|
|
||||||
|
// Hook for updating table properties with debouncing for performance
|
||||||
|
export const useUpdateTable = (table: DBTable) => {
|
||||||
|
const { updateTable: chartDBUpdateTable } = useChartDB();
|
||||||
|
const [localTableName, setLocalTableName] = useState(table.name);
|
||||||
|
|
||||||
|
// Debounced update function
|
||||||
|
const debouncedUpdate = useDebounce(
|
||||||
|
useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
if (value.trim() && value.trim() !== table.name) {
|
||||||
|
chartDBUpdateTable(table.id, { name: value.trim() });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[chartDBUpdateTable, table.id, table.name]
|
||||||
|
),
|
||||||
|
1000 // 1000ms debounce
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update local state immediately for responsive UI
|
||||||
|
const handleTableNameChange = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
setLocalTableName(value);
|
||||||
|
debouncedUpdate(value);
|
||||||
|
},
|
||||||
|
[debouncedUpdate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update local state when table name changes externally
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalTableName(table.name);
|
||||||
|
}, [table.name]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tableName: localTableName,
|
||||||
|
handleTableNameChange,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ar: LanguageTranslation = {
|
export const ar: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'جديد',
|
||||||
|
browse: 'تصفح',
|
||||||
|
tables: 'الجداول',
|
||||||
|
refs: 'المراجع',
|
||||||
|
dependencies: 'التبعيات',
|
||||||
|
custom_types: 'الأنواع المخصصة',
|
||||||
|
visuals: 'مرئيات',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ملف',
|
actions: 'الإجراءات',
|
||||||
new: 'جديد',
|
new: 'جديد...',
|
||||||
open: 'فتح',
|
browse: 'تصفح...',
|
||||||
save: 'حفظ',
|
save: 'حفظ',
|
||||||
import: 'استيراد قاعدة بيانات',
|
import: 'استيراد قاعدة بيانات',
|
||||||
export_sql: 'SQL تصدير',
|
export_sql: 'SQL تصدير',
|
||||||
export_as: 'تصدير كـ',
|
export_as: 'تصدير كـ',
|
||||||
delete_diagram: 'حذف الرسم البياني',
|
delete_diagram: 'حذف',
|
||||||
exit: 'خروج',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'تحرير',
|
edit: 'تحرير',
|
||||||
@@ -29,6 +37,7 @@ export const ar: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'إخفاء خصائص الحقل',
|
hide_field_attributes: 'إخفاء خصائص الحقل',
|
||||||
show_field_attributes: 'إظهار خصائص الحقل',
|
show_field_attributes: 'إظهار خصائص الحقل',
|
||||||
zoom_on_scroll: 'تكبير/تصغير عند التمرير',
|
zoom_on_scroll: 'تكبير/تصغير عند التمرير',
|
||||||
|
show_views: 'عروض قاعدة البيانات',
|
||||||
theme: 'المظهر',
|
theme: 'المظهر',
|
||||||
show_dependencies: 'إظهار الاعتمادات',
|
show_dependencies: 'إظهار الاعتمادات',
|
||||||
hide_dependencies: 'إخفاء الاعتمادات',
|
hide_dependencies: 'إخفاء الاعتمادات',
|
||||||
@@ -65,22 +74,13 @@ export const ar: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'إعادة ترتيب الرسم البياني',
|
title: 'ترتيب تلقائي للرسم البياني',
|
||||||
description:
|
description:
|
||||||
'هذا الإجراء سيقوم بإعادة ترتيب الجداول في المخطط بشكل تلقائي. هل تريد المتابعة؟',
|
'هذا الإجراء سيقوم بإعادة ترتيب الجداول في المخطط بشكل تلقائي. هل تريد المتابعة؟',
|
||||||
reorder: 'إعادة ترتيب',
|
reorder: 'ترتيب تلقائي',
|
||||||
cancel: 'إلغاء',
|
cancel: 'إلغاء',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'مخططات متعددة',
|
|
||||||
description:
|
|
||||||
'{{formattedSchemas}} :مخططات في هذا الرسم البياني. يتم حاليا عرض {{schemasCount}} هناك',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'لا شيء',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'فشل النسخ',
|
title: 'فشل النسخ',
|
||||||
@@ -115,14 +115,11 @@ export const ar: LanguageTranslation = {
|
|||||||
copied: '!تم النسخ',
|
copied: '!تم النسخ',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: ':المخطط',
|
|
||||||
filter_by_schema: 'تصفية حسب المخطط',
|
|
||||||
search_schema: '...بحث في المخطط',
|
|
||||||
no_schemas_found: '.لم يتم العثور على مخططات',
|
|
||||||
view_all_options: '...عرض جميع الخيارات',
|
view_all_options: '...عرض جميع الخيارات',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'الجداول',
|
tables: 'الجداول',
|
||||||
add_table: 'إضافة جدول',
|
add_table: 'إضافة جدول',
|
||||||
|
add_view: 'إضافة عرض',
|
||||||
filter: 'تصفية',
|
filter: 'تصفية',
|
||||||
collapse: 'طي الكل',
|
collapse: 'طي الكل',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const ar: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'خصائص الحقل',
|
title: 'خصائص الحقل',
|
||||||
unique: 'فريد',
|
unique: 'فريد',
|
||||||
|
auto_increment: 'زيادة تلقائية',
|
||||||
comments: 'تعليقات',
|
comments: 'تعليقات',
|
||||||
no_comments: 'لا يوجد تعليقات',
|
no_comments: 'لا يوجد تعليقات',
|
||||||
delete_field: 'حذف الحقل',
|
delete_field: 'حذف الحقل',
|
||||||
@@ -162,6 +160,7 @@ export const ar: LanguageTranslation = {
|
|||||||
title: 'خصائص الفهرس',
|
title: 'خصائص الفهرس',
|
||||||
name: 'الإسم',
|
name: 'الإسم',
|
||||||
unique: 'فريد',
|
unique: 'فريد',
|
||||||
|
index_type: 'نوع الفهرس',
|
||||||
delete_index: 'حذف الفهرس',
|
delete_index: 'حذف الفهرس',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -178,12 +177,15 @@ export const ar: LanguageTranslation = {
|
|||||||
description: 'أنشئ جدولاً للبدء',
|
description: 'أنشئ جدولاً للبدء',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'العلاقات',
|
refs: 'المراجع',
|
||||||
filter: 'تصفية',
|
filter: 'تصفية',
|
||||||
add_relationship: 'إضافة علاقة',
|
|
||||||
collapse: 'طي الكل',
|
collapse: 'طي الكل',
|
||||||
|
add_relationship: 'إضافة علاقة',
|
||||||
|
relationships: 'العلاقات',
|
||||||
|
dependencies: 'الاعتمادات',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'العلاقة',
|
||||||
primary: 'الجدول الأساسي',
|
primary: 'الجدول الأساسي',
|
||||||
foreign: 'الجدول المرتبط',
|
foreign: 'الجدول المرتبط',
|
||||||
cardinality: 'الكاردينالية',
|
cardinality: 'الكاردينالية',
|
||||||
@@ -193,16 +195,8 @@ export const ar: LanguageTranslation = {
|
|||||||
delete_relationship: 'حذف',
|
delete_relationship: 'حذف',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'لا توجد علاقات',
|
|
||||||
description: 'إنشئ علاقة لربط الجداول',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'الاعتمادات',
|
|
||||||
filter: 'تصفية',
|
|
||||||
collapse: 'طي الكل',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'الاعتماد',
|
||||||
table: 'الجدول',
|
table: 'الجدول',
|
||||||
dependent_table: 'عرض الاعتمادات',
|
dependent_table: 'عرض الاعتمادات',
|
||||||
delete_dependency: 'حذف',
|
delete_dependency: 'حذف',
|
||||||
@@ -212,8 +206,8 @@ export const ar: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'لا توجد اعتمادات',
|
title: 'لا توجد علاقات',
|
||||||
description: 'إنشاء اعتماد للبدء',
|
description: 'إنشاء علاقة للبدء',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -238,6 +232,33 @@ export const ar: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'مرئيات',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'ملاحظات',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'تصفية',
|
||||||
|
add_note: 'إضافة ملاحظة',
|
||||||
|
no_results: 'لم يتم العثور على ملاحظات',
|
||||||
|
clear: 'مسح التصفية',
|
||||||
|
empty_state: {
|
||||||
|
title: 'لا توجد ملاحظات',
|
||||||
|
description: 'أنشئ ملاحظة لإضافة تعليقات نصية على اللوحة',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'ملاحظة فارغة',
|
||||||
|
note_actions: {
|
||||||
|
title: 'إجراءات الملاحظة',
|
||||||
|
edit_content: 'تحرير المحتوى',
|
||||||
|
delete_note: 'حذف الملاحظة',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -254,6 +275,7 @@ export const ar: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'لم يتم تحديد قيم التعداد',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +298,7 @@ export const ar: LanguageTranslation = {
|
|||||||
show_all: 'عرض الكل',
|
show_all: 'عرض الكل',
|
||||||
undo: 'تراجع',
|
undo: 'تراجع',
|
||||||
redo: 'إعادة',
|
redo: 'إعادة',
|
||||||
reorder_diagram: 'إعادة ترتيب الرسم البياني',
|
reorder_diagram: 'ترتيب تلقائي للرسم البياني',
|
||||||
highlight_overlapping_tables: 'تمييز الجداول المتداخلة',
|
highlight_overlapping_tables: 'تمييز الجداول المتداخلة',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
filter: 'Filter Tables',
|
filter: 'Filter Tables',
|
||||||
@@ -313,13 +335,13 @@ export const ar: LanguageTranslation = {
|
|||||||
cancel: 'إلغاء',
|
cancel: 'إلغاء',
|
||||||
import_from_file: 'استيراد من ملف',
|
import_from_file: 'استيراد من ملف',
|
||||||
back: 'رجوع',
|
back: 'رجوع',
|
||||||
empty_diagram: 'مخطط فارغ',
|
empty_diagram: 'قاعدة بيانات فارغة',
|
||||||
continue: 'متابعة',
|
continue: 'متابعة',
|
||||||
import: 'استيراد',
|
import: 'استيراد',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'فتح مخطط',
|
title: 'فتح قاعدة بيانات',
|
||||||
description: 'اختر مخططًا لفتحه من القائمة ادناه',
|
description: 'اختر مخططًا لفتحه من القائمة ادناه',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'الإسم',
|
name: 'الإسم',
|
||||||
@@ -329,6 +351,12 @@ export const ar: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'إلغاء',
|
cancel: 'إلغاء',
|
||||||
open: 'فتح',
|
open: 'فتح',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'فتح',
|
||||||
|
duplicate: 'تكرار',
|
||||||
|
delete: 'حذف',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -474,9 +502,11 @@ export const ar: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'جدول جديد',
|
new_table: 'جدول جديد',
|
||||||
|
new_view: 'عرض جديد',
|
||||||
new_relationship: 'علاقة جديدة',
|
new_relationship: 'علاقة جديدة',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'ملاحظة جديدة',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -495,6 +525,8 @@ export const ar: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'اللغة',
|
change_language: 'اللغة',
|
||||||
},
|
},
|
||||||
|
on: 'تشغيل',
|
||||||
|
off: 'إيقاف',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const bn: LanguageTranslation = {
|
export const bn: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'নতুন',
|
||||||
|
browse: 'ব্রাউজ',
|
||||||
|
tables: 'টেবিল',
|
||||||
|
refs: 'রেফস',
|
||||||
|
dependencies: 'নির্ভরতা',
|
||||||
|
custom_types: 'কাস্টম টাইপ',
|
||||||
|
visuals: 'ভিজ্যুয়াল',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ফাইল',
|
actions: 'কার্য',
|
||||||
new: 'নতুন',
|
new: 'নতুন...',
|
||||||
open: 'খুলুন',
|
browse: 'ব্রাউজ করুন...',
|
||||||
save: 'সংরক্ষণ করুন',
|
save: 'সংরক্ষণ করুন',
|
||||||
import: 'ডাটাবেস আমদানি করুন',
|
import: 'ডাটাবেস আমদানি করুন',
|
||||||
export_sql: 'SQL রপ্তানি করুন',
|
export_sql: 'SQL রপ্তানি করুন',
|
||||||
export_as: 'রূপে রপ্তানি করুন',
|
export_as: 'রূপে রপ্তানি করুন',
|
||||||
delete_diagram: 'ডায়াগ্রাম মুছুন',
|
delete_diagram: 'মুছুন',
|
||||||
exit: 'প্রস্থান করুন',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'সম্পাদনা',
|
edit: 'সম্পাদনা',
|
||||||
@@ -29,6 +37,7 @@ export const bn: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'ফিল্ড অ্যাট্রিবিউট লুকান',
|
hide_field_attributes: 'ফিল্ড অ্যাট্রিবিউট লুকান',
|
||||||
show_field_attributes: 'ফিল্ড অ্যাট্রিবিউট দেখান',
|
show_field_attributes: 'ফিল্ড অ্যাট্রিবিউট দেখান',
|
||||||
zoom_on_scroll: 'স্ক্রলে জুম করুন',
|
zoom_on_scroll: 'স্ক্রলে জুম করুন',
|
||||||
|
show_views: 'ডাটাবেস ভিউ',
|
||||||
theme: 'থিম',
|
theme: 'থিম',
|
||||||
show_dependencies: 'নির্ভরতাগুলি দেখান',
|
show_dependencies: 'নির্ভরতাগুলি দেখান',
|
||||||
hide_dependencies: 'নির্ভরতাগুলি লুকান',
|
hide_dependencies: 'নির্ভরতাগুলি লুকান',
|
||||||
@@ -66,22 +75,13 @@ export const bn: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'ডায়াগ্রাম পুনর্বিন্যাস করুন',
|
title: 'স্বয়ংক্রিয় ডায়াগ্রাম সাজান',
|
||||||
description:
|
description:
|
||||||
'এই কাজটি ডায়াগ্রামের সমস্ত টেবিল পুনর্বিন্যাস করবে। আপনি কি চালিয়ে যেতে চান?',
|
'এই কাজটি ডায়াগ্রামের সমস্ত টেবিল পুনর্বিন্যাস করবে। আপনি কি চালিয়ে যেতে চান?',
|
||||||
reorder: 'পুনর্বিন্যাস করুন',
|
reorder: 'স্বয়ংক্রিয় সাজান',
|
||||||
cancel: 'বাতিল করুন',
|
cancel: 'বাতিল করুন',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'বহু স্কিমা',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} স্কিমা এই ডায়াগ্রামে রয়েছে। বর্তমানে প্রদর্শিত: {{formattedSchemas}}।',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'কিছুই না',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'কপি ব্যর্থ হয়েছে',
|
title: 'কপি ব্যর্থ হয়েছে',
|
||||||
@@ -116,14 +116,11 @@ export const bn: LanguageTranslation = {
|
|||||||
copied: 'অনুলিপি সম্পন্ন!',
|
copied: 'অনুলিপি সম্পন্ন!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'স্কিমা:',
|
|
||||||
filter_by_schema: 'স্কিমা দ্বারা ফিল্টার করুন',
|
|
||||||
search_schema: 'স্কিমা খুঁজুন...',
|
|
||||||
no_schemas_found: 'কোনো স্কিমা পাওয়া যায়নি।',
|
|
||||||
view_all_options: 'সমস্ত বিকল্প দেখুন...',
|
view_all_options: 'সমস্ত বিকল্প দেখুন...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'টেবিল',
|
tables: 'টেবিল',
|
||||||
add_table: 'টেবিল যোগ করুন',
|
add_table: 'টেবিল যোগ করুন',
|
||||||
|
add_view: 'ভিউ যোগ করুন',
|
||||||
filter: 'ফিল্টার',
|
filter: 'ফিল্টার',
|
||||||
collapse: 'সব ভাঁজ করুন',
|
collapse: 'সব ভাঁজ করুন',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const bn: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'ফিল্ড কর্ম',
|
title: 'ফিল্ড কর্ম',
|
||||||
unique: 'অদ্বিতীয়',
|
unique: 'অদ্বিতীয়',
|
||||||
|
auto_increment: 'স্বয়ংক্রিয় বৃদ্ধি',
|
||||||
comments: 'মন্তব্য',
|
comments: 'মন্তব্য',
|
||||||
no_comments: 'কোনো মন্তব্য নেই',
|
no_comments: 'কোনো মন্তব্য নেই',
|
||||||
delete_field: 'ফিল্ড মুছুন',
|
delete_field: 'ফিল্ড মুছুন',
|
||||||
@@ -164,6 +162,7 @@ export const bn: LanguageTranslation = {
|
|||||||
title: 'ইনডেক্স কর্ম',
|
title: 'ইনডেক্স কর্ম',
|
||||||
name: 'নাম',
|
name: 'নাম',
|
||||||
unique: 'অদ্বিতীয়',
|
unique: 'অদ্বিতীয়',
|
||||||
|
index_type: 'ইনডেক্স ধরন',
|
||||||
delete_index: 'ইনডেক্স মুছুন',
|
delete_index: 'ইনডেক্স মুছুন',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,14 +179,17 @@ export const bn: LanguageTranslation = {
|
|||||||
description: 'শুরু করতে একটি টেবিল তৈরি করুন',
|
description: 'শুরু করতে একটি টেবিল তৈরি করুন',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'সম্পর্ক',
|
refs: 'রেফস',
|
||||||
filter: 'ফিল্টার',
|
filter: 'ফিল্টার',
|
||||||
add_relationship: 'সম্পর্ক যোগ করুন',
|
|
||||||
collapse: 'সব ভাঁজ করুন',
|
collapse: 'সব ভাঁজ করুন',
|
||||||
|
add_relationship: 'সম্পর্ক যোগ করুন',
|
||||||
|
relationships: 'সম্পর্ক',
|
||||||
|
dependencies: 'নির্ভরতাগুলি',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'সম্পর্ক',
|
||||||
primary: 'প্রাথমিক টেবিল',
|
primary: 'প্রাথমিক টেবিল',
|
||||||
foreign: 'বিদেশি টেবিল',
|
foreign: 'রেফারেন্স করা টেবিল',
|
||||||
cardinality: 'কার্ডিনালিটি',
|
cardinality: 'কার্ডিনালিটি',
|
||||||
delete_relationship: 'মুছুন',
|
delete_relationship: 'মুছুন',
|
||||||
relationship_actions: {
|
relationship_actions: {
|
||||||
@@ -195,27 +197,19 @@ export const bn: LanguageTranslation = {
|
|||||||
delete_relationship: 'মুছুন',
|
delete_relationship: 'মুছুন',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'কোনো সম্পর্ক নেই',
|
|
||||||
description: 'টেবিল সংযোগ করতে একটি সম্পর্ক তৈরি করুন',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'নির্ভরতাগুলি',
|
|
||||||
filter: 'ফিল্টার',
|
|
||||||
collapse: 'ভাঁজ করুন',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'নির্ভরতা',
|
||||||
table: 'টেবিল',
|
table: 'টেবিল',
|
||||||
dependent_table: 'নির্ভরশীল টেবিল',
|
dependent_table: 'নির্ভরশীল ভিউ',
|
||||||
delete_dependency: 'নির্ভরতা মুছুন',
|
delete_dependency: 'মুছুন',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'কর্ম',
|
title: 'কর্ম',
|
||||||
delete_dependency: 'নির্ভরতা মুছুন',
|
delete_dependency: 'মুছুন',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'কোনো নির্ভরতাগুলি নেই',
|
title: 'কোনো সম্পর্ক নেই',
|
||||||
description: 'এই অংশে কোনো নির্ভরতা উপলব্ধ নেই।',
|
description: 'শুরু করতে একটি সম্পর্ক তৈরি করুন',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -239,6 +233,35 @@ export const bn: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'ভিজ্যুয়াল',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'নোট',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'ফিল্টার',
|
||||||
|
add_note: 'নোট যোগ করুন',
|
||||||
|
no_results: 'কোনো নোট পাওয়া যায়নি',
|
||||||
|
clear: 'ফিল্টার সাফ করুন',
|
||||||
|
empty_state: {
|
||||||
|
title: 'কোনো নোট নেই',
|
||||||
|
description:
|
||||||
|
'ক্যানভাসে টেক্সট টীকা যোগ করতে একটি নোট তৈরি করুন',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'খালি নোট',
|
||||||
|
note_actions: {
|
||||||
|
title: 'নোট ক্রিয়া',
|
||||||
|
edit_content: 'বিষয়বস্তু সম্পাদনা',
|
||||||
|
delete_note: 'নোট মুছুন',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -255,6 +278,7 @@ export const bn: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'কোন enum মান সংজ্ঞায়িত নেই',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -277,7 +301,7 @@ export const bn: LanguageTranslation = {
|
|||||||
show_all: 'সব দেখান',
|
show_all: 'সব দেখান',
|
||||||
undo: 'পূর্বাবস্থায় ফিরুন',
|
undo: 'পূর্বাবস্থায় ফিরুন',
|
||||||
redo: 'পুনরায় করুন',
|
redo: 'পুনরায় করুন',
|
||||||
reorder_diagram: 'ডায়াগ্রাম পুনর্বিন্যাস করুন',
|
reorder_diagram: 'স্বয়ংক্রিয় ডায়াগ্রাম সাজান',
|
||||||
highlight_overlapping_tables: 'ওভারল্যাপিং টেবিল হাইলাইট করুন',
|
highlight_overlapping_tables: 'ওভারল্যাপিং টেবিল হাইলাইট করুন',
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -315,13 +339,13 @@ export const bn: LanguageTranslation = {
|
|||||||
cancel: 'বাতিল করুন',
|
cancel: 'বাতিল করুন',
|
||||||
back: 'ফিরে যান',
|
back: 'ফিরে যান',
|
||||||
import_from_file: 'ফাইল থেকে আমদানি করুন',
|
import_from_file: 'ফাইল থেকে আমদানি করুন',
|
||||||
empty_diagram: 'ফাঁকা চিত্র',
|
empty_diagram: 'খালি ডাটাবেস',
|
||||||
continue: 'চালিয়ে যান',
|
continue: 'চালিয়ে যান',
|
||||||
import: 'আমদানি করুন',
|
import: 'আমদানি করুন',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'চিত্র খুলুন',
|
title: 'ডেটাবেস খুলুন',
|
||||||
description: 'নিচের তালিকা থেকে একটি চিত্র নির্বাচন করুন।',
|
description: 'নিচের তালিকা থেকে একটি চিত্র নির্বাচন করুন।',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'নাম',
|
name: 'নাম',
|
||||||
@@ -331,6 +355,12 @@ export const bn: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'বাতিল করুন',
|
cancel: 'বাতিল করুন',
|
||||||
open: 'খুলুন',
|
open: 'খুলুন',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'খুলুন',
|
||||||
|
duplicate: 'ডুপ্লিকেট',
|
||||||
|
delete: 'মুছুন',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -479,9 +509,11 @@ export const bn: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'নতুন টেবিল',
|
new_table: 'নতুন টেবিল',
|
||||||
|
new_view: 'নতুন ভিউ',
|
||||||
new_relationship: 'নতুন সম্পর্ক',
|
new_relationship: 'নতুন সম্পর্ক',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'নতুন নোট',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -500,6 +532,9 @@ export const bn: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'ভাষা পরিবর্তন করুন',
|
change_language: 'ভাষা পরিবর্তন করুন',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'চালু',
|
||||||
|
off: 'বন্ধ',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const de: LanguageTranslation = {
|
export const de: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Neu',
|
||||||
|
browse: 'Durchsuchen',
|
||||||
|
tables: 'Tabellen',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'Abhängigkeiten',
|
||||||
|
custom_types: 'Benutzerdefinierte Typen',
|
||||||
|
visuals: 'Darstellungen',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Datei',
|
actions: 'Aktionen',
|
||||||
new: 'Neu',
|
new: 'Neu...',
|
||||||
open: 'Öffnen',
|
browse: 'Durchsuchen...',
|
||||||
save: 'Speichern',
|
save: 'Speichern',
|
||||||
import: 'Datenbank importieren',
|
import: 'Datenbank importieren',
|
||||||
export_sql: 'SQL exportieren',
|
export_sql: 'SQL exportieren',
|
||||||
export_as: 'Exportieren als',
|
export_as: 'Exportieren als',
|
||||||
delete_diagram: 'Diagramm löschen',
|
delete_diagram: 'Löschen',
|
||||||
exit: 'Beenden',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Bearbeiten',
|
edit: 'Bearbeiten',
|
||||||
@@ -29,6 +37,7 @@ export const de: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Feldattribute ausblenden',
|
hide_field_attributes: 'Feldattribute ausblenden',
|
||||||
show_field_attributes: 'Feldattribute anzeigen',
|
show_field_attributes: 'Feldattribute anzeigen',
|
||||||
zoom_on_scroll: 'Zoom beim Scrollen',
|
zoom_on_scroll: 'Zoom beim Scrollen',
|
||||||
|
show_views: 'Datenbankansichten',
|
||||||
theme: 'Stil',
|
theme: 'Stil',
|
||||||
show_dependencies: 'Abhängigkeiten anzeigen',
|
show_dependencies: 'Abhängigkeiten anzeigen',
|
||||||
hide_dependencies: 'Abhängigkeiten ausblenden',
|
hide_dependencies: 'Abhängigkeiten ausblenden',
|
||||||
@@ -66,22 +75,13 @@ export const de: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Diagramm neu anordnen',
|
title: 'Diagramm automatisch anordnen',
|
||||||
description:
|
description:
|
||||||
'Diese Aktion wird alle Tabellen im Diagramm neu anordnen. Möchten Sie fortfahren?',
|
'Diese Aktion wird alle Tabellen im Diagramm neu anordnen. Möchten Sie fortfahren?',
|
||||||
reorder: 'Neu anordnen',
|
reorder: 'Automatisch anordnen',
|
||||||
cancel: 'Abbrechen',
|
cancel: 'Abbrechen',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Mehrere Schemas',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} Schemas in diesem Diagramm. Derzeit angezeigt: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'Keine',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Kopieren fehlgeschlagen',
|
title: 'Kopieren fehlgeschlagen',
|
||||||
@@ -117,14 +117,11 @@ export const de: LanguageTranslation = {
|
|||||||
copied: 'Kopiert!',
|
copied: 'Kopiert!',
|
||||||
|
|
||||||
side_panel: {
|
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...',
|
view_all_options: 'Alle Optionen anzeigen...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tabellen',
|
tables: 'Tabellen',
|
||||||
add_table: 'Tabelle hinzufügen',
|
add_table: 'Tabelle hinzufügen',
|
||||||
|
add_view: 'Ansicht hinzufügen',
|
||||||
filter: 'Filter',
|
filter: 'Filter',
|
||||||
collapse: 'Alle einklappen',
|
collapse: 'Alle einklappen',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -150,6 +147,7 @@ export const de: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Feldattribute',
|
title: 'Feldattribute',
|
||||||
unique: 'Eindeutig',
|
unique: 'Eindeutig',
|
||||||
|
auto_increment: 'Automatisch hochzählen',
|
||||||
comments: 'Kommentare',
|
comments: 'Kommentare',
|
||||||
no_comments: 'Keine Kommentare',
|
no_comments: 'Keine Kommentare',
|
||||||
delete_field: 'Feld löschen',
|
delete_field: 'Feld löschen',
|
||||||
@@ -165,6 +163,7 @@ export const de: LanguageTranslation = {
|
|||||||
title: 'Indexattribute',
|
title: 'Indexattribute',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
unique: 'Eindeutig',
|
unique: 'Eindeutig',
|
||||||
|
index_type: 'Indextyp',
|
||||||
delete_index: 'Index löschen',
|
delete_index: 'Index löschen',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -181,32 +180,26 @@ export const de: LanguageTranslation = {
|
|||||||
description: 'Erstellen Sie eine Tabelle, um zu beginnen',
|
description: 'Erstellen Sie eine Tabelle, um zu beginnen',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Beziehungen',
|
refs: 'Refs',
|
||||||
filter: 'Filter',
|
filter: 'Filter',
|
||||||
add_relationship: 'Beziehung hinzufügen',
|
|
||||||
collapse: 'Alle einklappen',
|
collapse: 'Alle einklappen',
|
||||||
|
add_relationship: 'Beziehung hinzufügen',
|
||||||
|
relationships: 'Beziehungen',
|
||||||
|
dependencies: 'Abhängigkeiten',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Beziehung',
|
||||||
primary: 'Primäre Tabelle',
|
primary: 'Primäre Tabelle',
|
||||||
foreign: 'Referenzierte Tabelle',
|
foreign: 'Referenzierte Tabelle',
|
||||||
cardinality: 'Kardinalität',
|
cardinality: 'Kardinalität',
|
||||||
delete_relationship: 'Beziehung löschen',
|
delete_relationship: 'Löschen',
|
||||||
relationship_actions: {
|
relationship_actions: {
|
||||||
title: 'Aktionen',
|
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: {
|
||||||
|
dependency: 'Abhängigkeit',
|
||||||
table: 'Tabelle',
|
table: 'Tabelle',
|
||||||
dependent_table: 'Abhängige Ansicht',
|
dependent_table: 'Abhängige Ansicht',
|
||||||
delete_dependency: 'Löschen',
|
delete_dependency: 'Löschen',
|
||||||
@@ -216,8 +209,8 @@ export const de: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Keine Abhängigkeiten',
|
title: 'Keine Beziehungen',
|
||||||
description: 'Erstellen Sie eine Ansicht, um zu beginnen',
|
description: 'Erstellen Sie eine Beziehung, um zu beginnen',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -241,6 +234,35 @@ export const de: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Darstellungen',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'Notizen',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Filter',
|
||||||
|
add_note: 'Notiz hinzufügen',
|
||||||
|
no_results: 'Keine Notizen gefunden',
|
||||||
|
clear: 'Filter löschen',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Keine Notizen',
|
||||||
|
description:
|
||||||
|
'Erstellen Sie eine Notiz, um Textanmerkungen auf der Leinwand hinzuzufügen',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Leere Notiz',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Notiz-Aktionen',
|
||||||
|
edit_content: 'Inhalt bearbeiten',
|
||||||
|
delete_note: 'Notiz löschen',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -257,6 +279,7 @@ export const de: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Keine Enum-Werte definiert',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -279,7 +302,7 @@ export const de: LanguageTranslation = {
|
|||||||
show_all: 'Alle anzeigen',
|
show_all: 'Alle anzeigen',
|
||||||
undo: 'Rückgängig',
|
undo: 'Rückgängig',
|
||||||
redo: 'Wiederholen',
|
redo: 'Wiederholen',
|
||||||
reorder_diagram: 'Diagramm neu anordnen',
|
reorder_diagram: 'Diagramm automatisch anordnen',
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
@@ -319,13 +342,13 @@ export const de: LanguageTranslation = {
|
|||||||
back: 'Zurück',
|
back: 'Zurück',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: 'Leeres Diagramm',
|
empty_diagram: 'Leere Datenbank',
|
||||||
continue: 'Weiter',
|
continue: 'Weiter',
|
||||||
import: 'Importieren',
|
import: 'Importieren',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Diagramm öffnen',
|
title: 'Datenbank öffnen',
|
||||||
description: 'Wählen Sie ein Diagramm aus der Liste unten aus.',
|
description: 'Wählen Sie ein Diagramm aus der Liste unten aus.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
@@ -335,6 +358,12 @@ export const de: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Abbrechen',
|
cancel: 'Abbrechen',
|
||||||
open: 'Öffnen',
|
open: 'Öffnen',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Öffnen',
|
||||||
|
duplicate: 'Duplizieren',
|
||||||
|
delete: 'Löschen',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -483,9 +512,11 @@ export const de: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Neue Tabelle',
|
new_table: 'Neue Tabelle',
|
||||||
|
new_view: 'Neue Ansicht',
|
||||||
new_relationship: 'Neue Beziehung',
|
new_relationship: 'Neue Beziehung',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'Neue Notiz',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -505,6 +536,9 @@ export const de: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Sprache',
|
change_language: 'Sprache',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Ein',
|
||||||
|
off: 'Aus',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata } from '../types';
|
|||||||
|
|
||||||
export const en = {
|
export const en = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'New',
|
||||||
|
browse: 'Browse',
|
||||||
|
tables: 'Tables',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'Dependencies',
|
||||||
|
custom_types: 'Custom Types',
|
||||||
|
visuals: 'Visuals',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'File',
|
actions: 'Actions',
|
||||||
new: 'New',
|
new: 'New...',
|
||||||
open: 'Open',
|
browse: 'Browse...',
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
import: 'Import',
|
import: 'Import',
|
||||||
export_sql: 'Export SQL',
|
export_sql: 'Export SQL',
|
||||||
export_as: 'Export as',
|
export_as: 'Export as',
|
||||||
delete_diagram: 'Delete Diagram',
|
delete_diagram: 'Delete',
|
||||||
exit: 'Exit',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Edit',
|
edit: 'Edit',
|
||||||
@@ -29,6 +37,7 @@ export const en = {
|
|||||||
hide_field_attributes: 'Hide Field Attributes',
|
hide_field_attributes: 'Hide Field Attributes',
|
||||||
show_field_attributes: 'Show Field Attributes',
|
show_field_attributes: 'Show Field Attributes',
|
||||||
zoom_on_scroll: 'Zoom on Scroll',
|
zoom_on_scroll: 'Zoom on Scroll',
|
||||||
|
show_views: 'Database Views',
|
||||||
theme: 'Theme',
|
theme: 'Theme',
|
||||||
show_dependencies: 'Show Dependencies',
|
show_dependencies: 'Show Dependencies',
|
||||||
hide_dependencies: 'Hide Dependencies',
|
hide_dependencies: 'Hide Dependencies',
|
||||||
@@ -64,21 +73,13 @@ export const en = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Reorder Diagram',
|
title: 'Auto Arrange Diagram',
|
||||||
description:
|
description:
|
||||||
'This action will rearrange all tables in the diagram. Do you want to continue?',
|
'This action will rearrange all tables in the diagram. Do you want to continue?',
|
||||||
reorder: 'Reorder',
|
reorder: 'Auto Arrange',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Multiple Schemas',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} schemas in this diagram. Currently displaying: {{formattedSchemas}}.',
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'none',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Copy failed',
|
title: 'Copy failed',
|
||||||
@@ -113,14 +114,11 @@ export const en = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
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...',
|
view_all_options: 'View all Options...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tables',
|
tables: 'Tables',
|
||||||
add_table: 'Add Table',
|
add_table: 'Add Table',
|
||||||
|
add_view: 'Add View',
|
||||||
filter: 'Filter',
|
filter: 'Filter',
|
||||||
collapse: 'Collapse All',
|
collapse: 'Collapse All',
|
||||||
clear: 'Clear Filter',
|
clear: 'Clear Filter',
|
||||||
@@ -144,6 +142,7 @@ export const en = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Field Attributes',
|
title: 'Field Attributes',
|
||||||
unique: 'Unique',
|
unique: 'Unique',
|
||||||
|
auto_increment: 'Auto Increment',
|
||||||
character_length: 'Max Length',
|
character_length: 'Max Length',
|
||||||
precision: 'Precision',
|
precision: 'Precision',
|
||||||
scale: 'Scale',
|
scale: 'Scale',
|
||||||
@@ -157,6 +156,7 @@ export const en = {
|
|||||||
title: 'Index Attributes',
|
title: 'Index Attributes',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
unique: 'Unique',
|
unique: 'Unique',
|
||||||
|
index_type: 'Index Type',
|
||||||
delete_index: 'Delete Index',
|
delete_index: 'Delete Index',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -173,12 +173,15 @@ export const en = {
|
|||||||
description: 'Create a table to get started',
|
description: 'Create a table to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Relationships',
|
refs: 'Refs',
|
||||||
filter: 'Filter',
|
filter: 'Filter',
|
||||||
add_relationship: 'Add Relationship',
|
|
||||||
collapse: 'Collapse All',
|
collapse: 'Collapse All',
|
||||||
|
add_relationship: 'Add Relationship',
|
||||||
|
relationships: 'Relationships',
|
||||||
|
dependencies: 'Dependencies',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Relationship',
|
||||||
primary: 'Primary Table',
|
primary: 'Primary Table',
|
||||||
foreign: 'Referenced Table',
|
foreign: 'Referenced Table',
|
||||||
cardinality: 'Cardinality',
|
cardinality: 'Cardinality',
|
||||||
@@ -188,16 +191,8 @@ export const en = {
|
|||||||
delete_relationship: 'Delete',
|
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: 'Dependency',
|
||||||
table: 'Table',
|
table: 'Table',
|
||||||
dependent_table: 'Dependent View',
|
dependent_table: 'Dependent View',
|
||||||
delete_dependency: 'Delete',
|
delete_dependency: 'Delete',
|
||||||
@@ -207,8 +202,8 @@ export const en = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'No dependencies',
|
title: 'No relationships',
|
||||||
description: 'Create a view to get started',
|
description: 'Create a relationship to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -232,6 +227,34 @@ export const en = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visuals',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'Notes',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Filter',
|
||||||
|
add_note: 'Add Note',
|
||||||
|
no_results: 'No notes found',
|
||||||
|
clear: 'Clear Filter',
|
||||||
|
empty_state: {
|
||||||
|
title: 'No Notes',
|
||||||
|
description:
|
||||||
|
'Create a note to add text annotations on the canvas',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Empty note',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Note Actions',
|
||||||
|
edit_content: 'Edit Content',
|
||||||
|
delete_note: 'Delete Note',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
filter: 'Filter',
|
filter: 'Filter',
|
||||||
@@ -247,6 +270,7 @@ export const en = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'No enum values defined',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -269,7 +293,7 @@ export const en = {
|
|||||||
show_all: 'Show All',
|
show_all: 'Show All',
|
||||||
undo: 'Undo',
|
undo: 'Undo',
|
||||||
redo: 'Redo',
|
redo: 'Redo',
|
||||||
reorder_diagram: 'Reorder Diagram',
|
reorder_diagram: 'Auto Arrange Diagram',
|
||||||
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -305,13 +329,13 @@ export const en = {
|
|||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
back: 'Back',
|
back: 'Back',
|
||||||
empty_diagram: 'Empty diagram',
|
empty_diagram: 'Empty database',
|
||||||
continue: 'Continue',
|
continue: 'Continue',
|
||||||
import: 'Import',
|
import: 'Import',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Open Diagram',
|
title: 'Open Database',
|
||||||
description: 'Select a diagram to open from the list below.',
|
description: 'Select a diagram to open from the list below.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
@@ -321,6 +345,12 @@ export const en = {
|
|||||||
},
|
},
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
open: 'Open',
|
open: 'Open',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Open',
|
||||||
|
duplicate: 'Duplicate',
|
||||||
|
delete: 'Delete',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -468,8 +498,10 @@ export const en = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'New Table',
|
new_table: 'New Table',
|
||||||
|
new_view: 'New View',
|
||||||
new_relationship: 'New Relationship',
|
new_relationship: 'New Relationship',
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'New Note',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -488,6 +520,9 @@ export const en = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Language',
|
change_language: 'Language',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'On',
|
||||||
|
off: 'Off',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const es: LanguageTranslation = {
|
export const es: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Nuevo',
|
||||||
|
browse: 'Examinar',
|
||||||
|
tables: 'Tablas',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'Dependencias',
|
||||||
|
custom_types: 'Tipos Personalizados',
|
||||||
|
visuals: 'Visuales',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Archivo',
|
actions: 'Acciones',
|
||||||
new: 'Nuevo',
|
new: 'Nuevo...',
|
||||||
open: 'Abrir',
|
browse: 'Examinar...',
|
||||||
save: 'Guardar',
|
save: 'Guardar',
|
||||||
import: 'Importar Base de Datos',
|
import: 'Importar Base de Datos',
|
||||||
export_sql: 'Exportar SQL',
|
export_sql: 'Exportar SQL',
|
||||||
export_as: 'Exportar como',
|
export_as: 'Exportar como',
|
||||||
delete_diagram: 'Eliminar Diagrama',
|
delete_diagram: 'Eliminar',
|
||||||
exit: 'Salir',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Editar',
|
edit: 'Editar',
|
||||||
@@ -29,6 +37,7 @@ export const es: LanguageTranslation = {
|
|||||||
show_sidebar: 'Mostrar Barra Lateral',
|
show_sidebar: 'Mostrar Barra Lateral',
|
||||||
hide_sidebar: 'Ocultar Barra Lateral',
|
hide_sidebar: 'Ocultar Barra Lateral',
|
||||||
zoom_on_scroll: 'Zoom al Desplazarse',
|
zoom_on_scroll: 'Zoom al Desplazarse',
|
||||||
|
show_views: 'Vistas de Base de Datos',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Mostrar dependencias',
|
show_dependencies: 'Mostrar dependencias',
|
||||||
hide_dependencies: 'Ocultar dependencias',
|
hide_dependencies: 'Ocultar dependencias',
|
||||||
@@ -65,10 +74,10 @@ export const es: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Reordenar Diagrama',
|
title: 'Organizar Diagrama Automáticamente',
|
||||||
description:
|
description:
|
||||||
'Esta acción reorganizará todas las tablas en el diagrama. ¿Deseas continuar?',
|
'Esta acción reorganizará todas las tablas en el diagrama. ¿Deseas continuar?',
|
||||||
reorder: 'Reordenar',
|
reorder: 'Organizar Automáticamente',
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -106,14 +115,11 @@ export const es: LanguageTranslation = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
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...',
|
view_all_options: 'Ver todas las opciones...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tablas',
|
tables: 'Tablas',
|
||||||
add_table: 'Agregar Tabla',
|
add_table: 'Agregar Tabla',
|
||||||
|
add_view: 'Agregar Vista',
|
||||||
filter: 'Filtrar',
|
filter: 'Filtrar',
|
||||||
collapse: 'Colapsar Todo',
|
collapse: 'Colapsar Todo',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -139,6 +145,7 @@ export const es: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Atributos del Campo',
|
title: 'Atributos del Campo',
|
||||||
unique: 'Único',
|
unique: 'Único',
|
||||||
|
auto_increment: 'Autoincremento',
|
||||||
comments: 'Comentarios',
|
comments: 'Comentarios',
|
||||||
no_comments: 'Sin comentarios',
|
no_comments: 'Sin comentarios',
|
||||||
delete_field: 'Eliminar Campo',
|
delete_field: 'Eliminar Campo',
|
||||||
@@ -154,6 +161,7 @@ export const es: LanguageTranslation = {
|
|||||||
title: 'Atributos del Índice',
|
title: 'Atributos del Índice',
|
||||||
name: 'Nombre',
|
name: 'Nombre',
|
||||||
unique: 'Único',
|
unique: 'Único',
|
||||||
|
index_type: 'Tipo de Índice',
|
||||||
delete_index: 'Eliminar Índice',
|
delete_index: 'Eliminar Índice',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -170,14 +178,17 @@ export const es: LanguageTranslation = {
|
|||||||
description: 'Crea una tabla para comenzar',
|
description: 'Crea una tabla para comenzar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Relaciones',
|
refs: 'Refs',
|
||||||
add_relationship: 'Agregar Relación',
|
|
||||||
filter: 'Filtrar',
|
filter: 'Filtrar',
|
||||||
collapse: 'Colapsar Todo',
|
collapse: 'Colapsar Todo',
|
||||||
|
add_relationship: 'Agregar Relación',
|
||||||
|
relationships: 'Relaciones',
|
||||||
|
dependencies: 'Dependencias',
|
||||||
relationship: {
|
relationship: {
|
||||||
primary: 'Primaria',
|
relationship: 'Relación',
|
||||||
foreign: 'Foránea',
|
primary: 'Tabla Primaria',
|
||||||
|
foreign: 'Tabla Referenciada',
|
||||||
cardinality: 'Cardinalidad',
|
cardinality: 'Cardinalidad',
|
||||||
delete_relationship: 'Eliminar',
|
delete_relationship: 'Eliminar',
|
||||||
relationship_actions: {
|
relationship_actions: {
|
||||||
@@ -185,18 +196,10 @@ export const es: LanguageTranslation = {
|
|||||||
delete_relationship: 'Eliminar',
|
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: {
|
||||||
|
dependency: 'Dependencia',
|
||||||
table: 'Tabla',
|
table: 'Tabla',
|
||||||
dependent_table: 'Vista dependiente',
|
dependent_table: 'Vista Dependiente',
|
||||||
delete_dependency: 'Eliminar',
|
delete_dependency: 'Eliminar',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'Acciones',
|
title: 'Acciones',
|
||||||
@@ -204,8 +207,8 @@ export const es: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Sin dependencias',
|
title: 'Sin relaciones',
|
||||||
description: 'Crea una vista para comenzar',
|
description: 'Crea una relación para comenzar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -229,6 +232,35 @@ export const es: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visuales',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'Notas',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Filtrar',
|
||||||
|
add_note: 'Agregar Nota',
|
||||||
|
no_results: 'No se encontraron notas',
|
||||||
|
clear: 'Limpiar Filtro',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Sin Notas',
|
||||||
|
description:
|
||||||
|
'Crea una nota para agregar anotaciones de texto en el lienzo',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Nota vacía',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Acciones de Nota',
|
||||||
|
edit_content: 'Editar Contenido',
|
||||||
|
delete_note: 'Eliminar Nota',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -245,6 +277,7 @@ export const es: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'No hay valores de enum definidos',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -267,7 +300,7 @@ export const es: LanguageTranslation = {
|
|||||||
show_all: 'Mostrar Todo',
|
show_all: 'Mostrar Todo',
|
||||||
undo: 'Deshacer',
|
undo: 'Deshacer',
|
||||||
redo: 'Rehacer',
|
redo: 'Rehacer',
|
||||||
reorder_diagram: 'Reordenar Diagrama',
|
reorder_diagram: 'Organizar Diagrama Automáticamente',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -306,13 +339,13 @@ export const es: LanguageTranslation = {
|
|||||||
back: 'Atrás',
|
back: 'Atrás',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: 'Diagrama vacío',
|
empty_diagram: 'Base de datos vacía',
|
||||||
continue: 'Continuar',
|
continue: 'Continuar',
|
||||||
import: 'Importar',
|
import: 'Importar',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Abrir Diagrama',
|
title: 'Abrir Base de Datos',
|
||||||
description:
|
description:
|
||||||
'Selecciona un diagrama para abrir de la lista a continuación.',
|
'Selecciona un diagrama para abrir de la lista a continuación.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -323,6 +356,12 @@ export const es: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
open: 'Abrir',
|
open: 'Abrir',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Abrir',
|
||||||
|
duplicate: 'Duplicar',
|
||||||
|
delete: 'Eliminar',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -424,14 +463,6 @@ export const es: LanguageTranslation = {
|
|||||||
confirm: '¡Claro!',
|
confirm: '¡Claro!',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Múltiples Esquemas',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} esquemas en este diagrama. Actualmente mostrando: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'nada',
|
|
||||||
},
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
export_diagram_dialog: {
|
export_diagram_dialog: {
|
||||||
title: 'Export Diagram',
|
title: 'Export Diagram',
|
||||||
@@ -480,9 +511,11 @@ export const es: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Nueva Tabla',
|
new_table: 'Nueva Tabla',
|
||||||
|
new_view: 'Nueva Vista',
|
||||||
new_relationship: 'Nueva Relación',
|
new_relationship: 'Nueva Relación',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'Nueva Nota',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -502,6 +535,9 @@ export const es: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Idioma',
|
change_language: 'Idioma',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Encendido',
|
||||||
|
off: 'Apagado',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const fr: LanguageTranslation = {
|
export const fr: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Nouveau',
|
||||||
|
browse: 'Parcourir',
|
||||||
|
tables: 'Tables',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'Dépendances',
|
||||||
|
custom_types: 'Types Personnalisés',
|
||||||
|
visuals: 'Visuels',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Fichier',
|
actions: 'Actions',
|
||||||
new: 'Nouveau',
|
new: 'Nouveau...',
|
||||||
open: 'Ouvrir',
|
browse: 'Parcourir...',
|
||||||
save: 'Enregistrer',
|
save: 'Enregistrer',
|
||||||
import: 'Importer Base de Données',
|
import: 'Importer Base de Données',
|
||||||
export_sql: 'Exporter SQL',
|
export_sql: 'Exporter SQL',
|
||||||
export_as: 'Exporter en tant que',
|
export_as: 'Exporter en tant que',
|
||||||
delete_diagram: 'Supprimer le Diagramme',
|
delete_diagram: 'Supprimer',
|
||||||
exit: 'Quitter',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Édition',
|
edit: 'Édition',
|
||||||
@@ -29,6 +37,7 @@ export const fr: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Masquer les Attributs de Champ',
|
hide_field_attributes: 'Masquer les Attributs de Champ',
|
||||||
show_field_attributes: 'Afficher les Attributs de Champ',
|
show_field_attributes: 'Afficher les Attributs de Champ',
|
||||||
zoom_on_scroll: 'Zoom sur le Défilement',
|
zoom_on_scroll: 'Zoom sur le Défilement',
|
||||||
|
show_views: 'Vues de Base de Données',
|
||||||
theme: 'Thème',
|
theme: 'Thème',
|
||||||
show_dependencies: 'Afficher les Dépendances',
|
show_dependencies: 'Afficher les Dépendances',
|
||||||
hide_dependencies: 'Masquer les Dépendances',
|
hide_dependencies: 'Masquer les Dépendances',
|
||||||
@@ -64,10 +73,10 @@ export const fr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Réorganiser le Diagramme',
|
title: 'Organiser Automatiquement le Diagramme',
|
||||||
description:
|
description:
|
||||||
'Cette action réorganisera toutes les tables dans le diagramme. Voulez-vous continuer ?',
|
'Cette action réorganisera toutes les tables dans le diagramme. Voulez-vous continuer ?',
|
||||||
reorder: 'Réorganiser',
|
reorder: 'Organiser Automatiquement',
|
||||||
cancel: 'Annuler',
|
cancel: 'Annuler',
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -105,14 +114,11 @@ export const fr: LanguageTranslation = {
|
|||||||
copied: 'Copié !',
|
copied: 'Copié !',
|
||||||
|
|
||||||
side_panel: {
|
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...',
|
view_all_options: 'Voir toutes les Options...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tables',
|
tables: 'Tables',
|
||||||
add_table: 'Ajouter une Table',
|
add_table: 'Ajouter une Table',
|
||||||
|
add_view: 'Ajouter une Vue',
|
||||||
filter: 'Filtrer',
|
filter: 'Filtrer',
|
||||||
collapse: 'Réduire Tout',
|
collapse: 'Réduire Tout',
|
||||||
clear: 'Effacer le Filtre',
|
clear: 'Effacer le Filtre',
|
||||||
@@ -137,6 +143,7 @@ export const fr: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Attributs du Champ',
|
title: 'Attributs du Champ',
|
||||||
unique: 'Unique',
|
unique: 'Unique',
|
||||||
|
auto_increment: 'Auto-incrément',
|
||||||
comments: 'Commentaires',
|
comments: 'Commentaires',
|
||||||
no_comments: 'Pas de commentaires',
|
no_comments: 'Pas de commentaires',
|
||||||
delete_field: 'Supprimer le Champ',
|
delete_field: 'Supprimer le Champ',
|
||||||
@@ -152,6 +159,7 @@ export const fr: LanguageTranslation = {
|
|||||||
title: "Attributs de l'Index",
|
title: "Attributs de l'Index",
|
||||||
name: 'Nom',
|
name: 'Nom',
|
||||||
unique: 'Unique',
|
unique: 'Unique',
|
||||||
|
index_type: "Type d'index",
|
||||||
delete_index: "Supprimer l'Index",
|
delete_index: "Supprimer l'Index",
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -168,12 +176,15 @@ export const fr: LanguageTranslation = {
|
|||||||
description: 'Créez une table pour commencer',
|
description: 'Créez une table pour commencer',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Relations',
|
refs: 'Refs',
|
||||||
filter: 'Filtrer',
|
filter: 'Filtrer',
|
||||||
add_relationship: 'Ajouter une Relation',
|
|
||||||
collapse: 'Réduire Tout',
|
collapse: 'Réduire Tout',
|
||||||
|
add_relationship: 'Ajouter une Relation',
|
||||||
|
relationships: 'Relations',
|
||||||
|
dependencies: 'Dépendances',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Relation',
|
||||||
primary: 'Table Principale',
|
primary: 'Table Principale',
|
||||||
foreign: 'Table Référencée',
|
foreign: 'Table Référencée',
|
||||||
cardinality: 'Cardinalité',
|
cardinality: 'Cardinalité',
|
||||||
@@ -183,16 +194,8 @@ export const fr: LanguageTranslation = {
|
|||||||
delete_relationship: 'Supprimer',
|
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: {
|
||||||
|
dependency: 'Dépendance',
|
||||||
table: 'Table',
|
table: 'Table',
|
||||||
dependent_table: 'Vue Dépendante',
|
dependent_table: 'Vue Dépendante',
|
||||||
delete_dependency: 'Supprimer',
|
delete_dependency: 'Supprimer',
|
||||||
@@ -202,8 +205,8 @@ export const fr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Aucune dépendance',
|
title: 'Aucune relation',
|
||||||
description: 'Créez une vue pour commencer',
|
description: 'Créez une relation pour commencer',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -227,6 +230,35 @@ export const fr: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visuels',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'Notes',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Filtrer',
|
||||||
|
add_note: 'Ajouter une Note',
|
||||||
|
no_results: 'Aucune note trouvée',
|
||||||
|
clear: 'Effacer le Filtre',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Pas de Notes',
|
||||||
|
description:
|
||||||
|
'Créez une note pour ajouter des annotations de texte sur le canevas',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Note vide',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Actions de Note',
|
||||||
|
edit_content: 'Modifier le Contenu',
|
||||||
|
delete_note: 'Supprimer la Note',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -243,6 +275,7 @@ export const fr: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: "Aucune valeur d'énumération définie",
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -265,7 +298,7 @@ export const fr: LanguageTranslation = {
|
|||||||
show_all: 'Afficher Tout',
|
show_all: 'Afficher Tout',
|
||||||
undo: 'Annuler',
|
undo: 'Annuler',
|
||||||
redo: 'Rétablir',
|
redo: 'Rétablir',
|
||||||
reorder_diagram: 'Réorganiser le Diagramme',
|
reorder_diagram: 'Organiser Automatiquement le Diagramme',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -303,13 +336,13 @@ export const fr: LanguageTranslation = {
|
|||||||
cancel: 'Annuler',
|
cancel: 'Annuler',
|
||||||
back: 'Retour',
|
back: 'Retour',
|
||||||
import_from_file: "Importer à partir d'un fichier",
|
import_from_file: "Importer à partir d'un fichier",
|
||||||
empty_diagram: 'Diagramme vide',
|
empty_diagram: 'Base de données vide',
|
||||||
continue: 'Continuer',
|
continue: 'Continuer',
|
||||||
import: 'Importer',
|
import: 'Importer',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Ouvrir Diagramme',
|
title: 'Ouvrir Base de Données',
|
||||||
description:
|
description:
|
||||||
'Sélectionnez un diagramme à ouvrir dans la liste ci-dessous.',
|
'Sélectionnez un diagramme à ouvrir dans la liste ci-dessous.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -320,6 +353,12 @@ export const fr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Annuler',
|
cancel: 'Annuler',
|
||||||
open: 'Ouvrir',
|
open: 'Ouvrir',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Ouvrir',
|
||||||
|
duplicate: 'Dupliquer',
|
||||||
|
delete: 'Supprimer',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -357,15 +396,6 @@ export const fr: LanguageTranslation = {
|
|||||||
transparent_description: 'Remove background color from image.',
|
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}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'Aucun',
|
|
||||||
},
|
|
||||||
|
|
||||||
new_table_schema_dialog: {
|
new_table_schema_dialog: {
|
||||||
title: 'Sélectionner un Schéma',
|
title: 'Sélectionner un Schéma',
|
||||||
description:
|
description:
|
||||||
@@ -477,9 +507,11 @@ export const fr: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Nouvelle Table',
|
new_table: 'Nouvelle Table',
|
||||||
|
new_view: 'Nouvelle Vue',
|
||||||
new_relationship: 'Nouvelle Relation',
|
new_relationship: 'Nouvelle Relation',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'Nouvelle Note',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -499,6 +531,9 @@ export const fr: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Langue',
|
change_language: 'Langue',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Activé',
|
||||||
|
off: 'Désactivé',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const gu: LanguageTranslation = {
|
export const gu: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'નવું',
|
||||||
|
browse: 'બ્રાઉજ',
|
||||||
|
tables: 'ટેબલો',
|
||||||
|
refs: 'રેફ્સ',
|
||||||
|
dependencies: 'નિર્ભરતાઓ',
|
||||||
|
custom_types: 'કસ્ટમ ટાઇપ',
|
||||||
|
visuals: 'Visuals',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ફાઇલ',
|
actions: 'ક્રિયાઓ',
|
||||||
new: 'નવું',
|
new: 'નવું...',
|
||||||
open: 'ખોલો',
|
browse: 'બ્રાઉજ કરો...',
|
||||||
save: 'સાચવો',
|
save: 'સાચવો',
|
||||||
import: 'ડેટાબેસ આયાત કરો',
|
import: 'ડેટાબેસ આયાત કરો',
|
||||||
export_sql: 'SQL નિકાસ કરો',
|
export_sql: 'SQL નિકાસ કરો',
|
||||||
export_as: 'રૂપે નિકાસ કરો',
|
export_as: 'રૂપે નિકાસ કરો',
|
||||||
delete_diagram: 'ડાયાગ્રામ કાઢી નાખો',
|
delete_diagram: 'કાઢી નાખો',
|
||||||
exit: 'બહાર જાઓ',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'ફેરફાર',
|
edit: 'ફેરફાર',
|
||||||
@@ -29,6 +37,7 @@ export const gu: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ છુપાવો',
|
hide_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ છુપાવો',
|
||||||
show_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ બતાવો',
|
show_field_attributes: 'ફીલ્ડ અટ્રિબ્યુટ્સ બતાવો',
|
||||||
zoom_on_scroll: 'સ્ક્રોલ પર ઝૂમ કરો',
|
zoom_on_scroll: 'સ્ક્રોલ પર ઝૂમ કરો',
|
||||||
|
show_views: 'ડેટાબેઝ વ્યૂઝ',
|
||||||
theme: 'થિમ',
|
theme: 'થિમ',
|
||||||
show_dependencies: 'નિર્ભરતાઓ બતાવો',
|
show_dependencies: 'નિર્ભરતાઓ બતાવો',
|
||||||
hide_dependencies: 'નિર્ભરતાઓ છુપાવો',
|
hide_dependencies: 'નિર્ભરતાઓ છુપાવો',
|
||||||
@@ -66,22 +75,13 @@ export const gu: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'ડાયાગ્રામ ફરી વ્યવસ્થિત કરો',
|
title: 'ડાયાગ્રામ ઑટોમેટિક ગોઠવો',
|
||||||
description:
|
description:
|
||||||
'આ ક્રિયા ડાયાગ્રામમાં બધી ટેબલ્સને ફરીથી વ્યવસ્થિત કરશે. શું તમે ચાલુ રાખવા માંગો છો?',
|
'આ ક્રિયા ડાયાગ્રામમાં બધી ટેબલ્સને ફરીથી વ્યવસ્થિત કરશે. શું તમે ચાલુ રાખવા માંગો છો?',
|
||||||
reorder: 'ફરી વ્યવસ્થિત કરો',
|
reorder: 'ઑટોમેટિક ગોઠવો',
|
||||||
cancel: 'રદ કરો',
|
cancel: 'રદ કરો',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'કઈંક વધારે સ્કીમા',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} સ્કીમા આ ડાયાગ્રામમાં છે. હાલમાં દર્શાવેલ છે: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'કઈ નહીં',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'નકલ નિષ્ફળ',
|
title: 'નકલ નિષ્ફળ',
|
||||||
@@ -116,14 +116,11 @@ export const gu: LanguageTranslation = {
|
|||||||
copied: 'નકલ થયું!',
|
copied: 'નકલ થયું!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'સ્કીમા:',
|
|
||||||
filter_by_schema: 'સ્કીમા દ્વારા ફિલ્ટર કરો',
|
|
||||||
search_schema: 'સ્કીમા શોધો...',
|
|
||||||
no_schemas_found: 'કોઈ સ્કીમા મળ્યા નથી.',
|
|
||||||
view_all_options: 'બધા વિકલ્પો જુઓ...',
|
view_all_options: 'બધા વિકલ્પો જુઓ...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'ટેબલ્સ',
|
tables: 'ટેબલ્સ',
|
||||||
add_table: 'ટેબલ ઉમેરો',
|
add_table: 'ટેબલ ઉમેરો',
|
||||||
|
add_view: 'વ્યૂ ઉમેરો',
|
||||||
filter: 'ફિલ્ટર',
|
filter: 'ફિલ્ટર',
|
||||||
collapse: 'બધાને સકુચિત કરો',
|
collapse: 'બધાને સકુચિત કરો',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -150,6 +147,7 @@ export const gu: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'ફીલ્ડ લક્ષણો',
|
title: 'ફીલ્ડ લક્ષણો',
|
||||||
unique: 'અદ્વિતીય',
|
unique: 'અદ્વિતીય',
|
||||||
|
auto_increment: 'ઑટો ઇન્ક્રિમેન્ટ',
|
||||||
comments: 'ટિપ્પણીઓ',
|
comments: 'ટિપ્પણીઓ',
|
||||||
no_comments: 'કોઈ ટિપ્પણીઓ નથી',
|
no_comments: 'કોઈ ટિપ્પણીઓ નથી',
|
||||||
delete_field: 'ફીલ્ડ કાઢી નાખો',
|
delete_field: 'ફીલ્ડ કાઢી નાખો',
|
||||||
@@ -165,6 +163,7 @@ export const gu: LanguageTranslation = {
|
|||||||
title: 'ઇન્ડેક્સ લક્ષણો',
|
title: 'ઇન્ડેક્સ લક્ષણો',
|
||||||
name: 'નામ',
|
name: 'નામ',
|
||||||
unique: 'અદ્વિતીય',
|
unique: 'અદ્વિતીય',
|
||||||
|
index_type: 'ઇન્ડેક્સ પ્રકાર',
|
||||||
delete_index: 'ઇન્ડેક્સ કાઢી નાખો',
|
delete_index: 'ઇન્ડેક્સ કાઢી નાખો',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -181,14 +180,17 @@ export const gu: LanguageTranslation = {
|
|||||||
description: 'શરૂ કરવા માટે એક ટેબલ બનાવો',
|
description: 'શરૂ કરવા માટે એક ટેબલ બનાવો',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'સંબંધો',
|
refs: 'રેફ્સ',
|
||||||
filter: 'ફિલ્ટર',
|
filter: 'ફિલ્ટર',
|
||||||
add_relationship: 'સંબંધ ઉમેરો',
|
|
||||||
collapse: 'બધાને સકુચિત કરો',
|
collapse: 'બધાને સકુચિત કરો',
|
||||||
|
add_relationship: 'સંબંધ ઉમેરો',
|
||||||
|
relationships: 'સંબંધો',
|
||||||
|
dependencies: 'નિર્ભરતાઓ',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'સંબંધ',
|
||||||
primary: 'પ્રાથમિક ટેબલ',
|
primary: 'પ્રાથમિક ટેબલ',
|
||||||
foreign: 'સંદર્ભ ટેબલ',
|
foreign: 'સંદર્ભિત ટેબલ',
|
||||||
cardinality: 'કાર્ડિનાલિટી',
|
cardinality: 'કાર્ડિનાલિટી',
|
||||||
delete_relationship: 'કાઢી નાખો',
|
delete_relationship: 'કાઢી નાખો',
|
||||||
relationship_actions: {
|
relationship_actions: {
|
||||||
@@ -196,27 +198,19 @@ export const gu: LanguageTranslation = {
|
|||||||
delete_relationship: 'કાઢી નાખો',
|
delete_relationship: 'કાઢી નાખો',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'કોઈ સંબંધો નથી',
|
|
||||||
description: 'ટેબલ્સ કનેક્ટ કરવા માટે એક સંબંધ બનાવો',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'નિર્ભરતાઓ',
|
|
||||||
filter: 'ફિલ્ટર',
|
|
||||||
collapse: 'સિકોડો',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'નિર્ભરતા',
|
||||||
table: 'ટેબલ',
|
table: 'ટેબલ',
|
||||||
dependent_table: 'આધાર રાખેલું ટેબલ',
|
dependent_table: 'નિર્ભરશીલ વ્યૂ',
|
||||||
delete_dependency: 'નિર્ભરતા કાઢી નાખો',
|
delete_dependency: 'કાઢી નાખો',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'ક્રિયાઓ',
|
title: 'ક્રિયાઓ',
|
||||||
delete_dependency: 'નિર્ભરતા કાઢી નાખો',
|
delete_dependency: 'કાઢી નાખો',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'કોઈ નિર્ભરતાઓ નથી',
|
title: 'કોઈ સંબંધો નથી',
|
||||||
description: 'આ વિભાગમાં કોઈ નિર્ભરતા ઉપલબ્ધ નથી.',
|
description: 'શરૂ કરવા માટે એક સંબંધ બનાવો',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -240,6 +234,35 @@ export const gu: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visuals',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'નોંધો',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'ફિલ્ટર',
|
||||||
|
add_note: 'નોંધ ઉમેરો',
|
||||||
|
no_results: 'કોઈ નોંધો મળી નથી',
|
||||||
|
clear: 'ફિલ્ટર સાફ કરો',
|
||||||
|
empty_state: {
|
||||||
|
title: 'કોઈ નોંધો નથી',
|
||||||
|
description:
|
||||||
|
'કેનવાસ પર ટેક્સ્ટ એનોટેશન ઉમેરવા માટે નોંધ બનાવો',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'ખાલી નોંધ',
|
||||||
|
note_actions: {
|
||||||
|
title: 'નોંધ ક્રિયાઓ',
|
||||||
|
edit_content: 'સામગ્રી સંપાદિત કરો',
|
||||||
|
delete_note: 'નોંધ કાઢી નાખો',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -256,6 +279,7 @@ export const gu: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'કોઈ enum મૂલ્યો વ્યાખ્યાયિત નથી',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -278,7 +302,7 @@ export const gu: LanguageTranslation = {
|
|||||||
show_all: 'બધું બતાવો',
|
show_all: 'બધું બતાવો',
|
||||||
undo: 'અનડુ',
|
undo: 'અનડુ',
|
||||||
redo: 'રીડુ',
|
redo: 'રીડુ',
|
||||||
reorder_diagram: 'ડાયાગ્રામ ફરીથી વ્યવસ્થિત કરો',
|
reorder_diagram: 'ડાયાગ્રામ ઑટોમેટિક ગોઠવો',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -315,13 +339,13 @@ export const gu: LanguageTranslation = {
|
|||||||
cancel: 'રદ કરો',
|
cancel: 'રદ કરો',
|
||||||
back: 'પાછા',
|
back: 'પાછા',
|
||||||
import_from_file: 'ફાઇલમાંથી આયાત કરો',
|
import_from_file: 'ફાઇલમાંથી આયાત કરો',
|
||||||
empty_diagram: 'ખાલી ડાયાગ્રામ',
|
empty_diagram: 'ખાલી ડેટાબેસ',
|
||||||
continue: 'ચાલુ રાખો',
|
continue: 'ચાલુ રાખો',
|
||||||
import: 'આયાત કરો',
|
import: 'આયાત કરો',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'ડાયાગ્રામ ખોલો',
|
title: 'ડેટાબેસ ખોલો',
|
||||||
description: 'નીચેની યાદીમાંથી એક ડાયાગ્રામ પસંદ કરો.',
|
description: 'નીચેની યાદીમાંથી એક ડાયાગ્રામ પસંદ કરો.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'નામ',
|
name: 'નામ',
|
||||||
@@ -331,6 +355,12 @@ export const gu: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'રદ કરો',
|
cancel: 'રદ કરો',
|
||||||
open: 'ખોલો',
|
open: 'ખોલો',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'ખોલો',
|
||||||
|
duplicate: 'ડુપ્લિકેટ',
|
||||||
|
delete: 'કાઢી નાખો',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -480,9 +510,11 @@ export const gu: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'નવું ટેબલ',
|
new_table: 'નવું ટેબલ',
|
||||||
|
new_view: 'નવું વ્યૂ',
|
||||||
new_relationship: 'નવો સંબંધ',
|
new_relationship: 'નવો સંબંધ',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'નવી નોંધ',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -501,6 +533,9 @@ export const gu: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'ભાષા બદલો',
|
change_language: 'ભાષા બદલો',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'ચાલુ',
|
||||||
|
off: 'બંધ',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const hi: LanguageTranslation = {
|
export const hi: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'नया',
|
||||||
|
browse: 'ब्राउज़',
|
||||||
|
tables: 'टेबल',
|
||||||
|
refs: 'रेफ्स',
|
||||||
|
dependencies: 'निर्भरताएं',
|
||||||
|
custom_types: 'कस्टम टाइप',
|
||||||
|
visuals: 'Visuals',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'फ़ाइल',
|
actions: 'कार्य',
|
||||||
new: 'नया',
|
new: 'नया...',
|
||||||
open: 'खोलें',
|
browse: 'ब्राउज़ करें...',
|
||||||
save: 'सहेजें',
|
save: 'सहेजें',
|
||||||
import: 'डेटाबेस आयात करें',
|
import: 'डेटाबेस आयात करें',
|
||||||
export_sql: 'SQL निर्यात करें',
|
export_sql: 'SQL निर्यात करें',
|
||||||
export_as: 'के रूप में निर्यात करें',
|
export_as: 'के रूप में निर्यात करें',
|
||||||
delete_diagram: 'आरेख हटाएँ',
|
delete_diagram: 'हटाएँ',
|
||||||
exit: 'बाहर जाएँ',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'संपादित करें',
|
edit: 'संपादित करें',
|
||||||
@@ -29,6 +37,7 @@ export const hi: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'फ़ील्ड विशेषताएँ छिपाएँ',
|
hide_field_attributes: 'फ़ील्ड विशेषताएँ छिपाएँ',
|
||||||
show_field_attributes: 'फ़ील्ड विशेषताएँ दिखाएँ',
|
show_field_attributes: 'फ़ील्ड विशेषताएँ दिखाएँ',
|
||||||
zoom_on_scroll: 'स्क्रॉल पर ज़ूम',
|
zoom_on_scroll: 'स्क्रॉल पर ज़ूम',
|
||||||
|
show_views: 'डेटाबेस व्यू',
|
||||||
theme: 'थीम',
|
theme: 'थीम',
|
||||||
show_dependencies: 'निर्भरता दिखाएँ',
|
show_dependencies: 'निर्भरता दिखाएँ',
|
||||||
hide_dependencies: 'निर्भरता छिपाएँ',
|
hide_dependencies: 'निर्भरता छिपाएँ',
|
||||||
@@ -65,22 +74,13 @@ export const hi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'आरेख पुनः व्यवस्थित करें',
|
title: 'आरेख स्वचालित व्यवस्थित करें',
|
||||||
description:
|
description:
|
||||||
'यह क्रिया आरेख में सभी तालिकाओं को पुनः व्यवस्थित कर देगी। क्या आप जारी रखना चाहते हैं?',
|
'यह क्रिया आरेख में सभी तालिकाओं को पुनः व्यवस्थित कर देगी। क्या आप जारी रखना चाहते हैं?',
|
||||||
reorder: 'पुनः व्यवस्थित करें',
|
reorder: 'स्वचालित व्यवस्थित करें',
|
||||||
cancel: 'रद्द करें',
|
cancel: 'रद्द करें',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'एकाधिक स्कीमा',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} स्कीमा इस आरेख में हैं। वर्तमान में प्रदर्शित: {{formattedSchemas}}।',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'कोई नहीं',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'कॉपी असफल',
|
title: 'कॉपी असफल',
|
||||||
@@ -116,14 +116,11 @@ export const hi: LanguageTranslation = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'स्कीमा:',
|
|
||||||
filter_by_schema: 'स्कीमा द्वारा फ़िल्टर करें',
|
|
||||||
search_schema: 'स्कीमा खोजें...',
|
|
||||||
no_schemas_found: 'कोई स्कीमा नहीं मिला।',
|
|
||||||
view_all_options: 'सभी विकल्प देखें...',
|
view_all_options: 'सभी विकल्प देखें...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'तालिकाएँ',
|
tables: 'तालिकाएँ',
|
||||||
add_table: 'तालिका जोड़ें',
|
add_table: 'तालिका जोड़ें',
|
||||||
|
add_view: 'व्यू जोड़ें',
|
||||||
filter: 'फ़िल्टर',
|
filter: 'फ़िल्टर',
|
||||||
collapse: 'सभी को संक्षिप्त करें',
|
collapse: 'सभी को संक्षिप्त करें',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const hi: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'फ़ील्ड विशेषताएँ',
|
title: 'फ़ील्ड विशेषताएँ',
|
||||||
unique: 'अद्वितीय',
|
unique: 'अद्वितीय',
|
||||||
|
auto_increment: 'ऑटो इंक्रीमेंट',
|
||||||
comments: 'टिप्पणियाँ',
|
comments: 'टिप्पणियाँ',
|
||||||
no_comments: 'कोई टिप्पणी नहीं',
|
no_comments: 'कोई टिप्पणी नहीं',
|
||||||
delete_field: 'फ़ील्ड हटाएँ',
|
delete_field: 'फ़ील्ड हटाएँ',
|
||||||
@@ -164,6 +162,7 @@ export const hi: LanguageTranslation = {
|
|||||||
title: 'सूचकांक विशेषताएँ',
|
title: 'सूचकांक विशेषताएँ',
|
||||||
name: 'नाम',
|
name: 'नाम',
|
||||||
unique: 'अद्वितीय',
|
unique: 'अद्वितीय',
|
||||||
|
index_type: 'इंडेक्स प्रकार',
|
||||||
delete_index: 'सूचकांक हटाएँ',
|
delete_index: 'सूचकांक हटाएँ',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,12 +179,15 @@ export const hi: LanguageTranslation = {
|
|||||||
description: 'शुरू करने के लिए एक तालिका बनाएँ',
|
description: 'शुरू करने के लिए एक तालिका बनाएँ',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'संबंध',
|
refs: 'रेफ्स',
|
||||||
filter: 'फ़िल्टर',
|
filter: 'फ़िल्टर',
|
||||||
add_relationship: 'संबंध जोड़ें',
|
|
||||||
collapse: 'सभी को संक्षिप्त करें',
|
collapse: 'सभी को संक्षिप्त करें',
|
||||||
|
add_relationship: 'संबंध जोड़ें',
|
||||||
|
relationships: 'संबंध',
|
||||||
|
dependencies: 'निर्भरताएँ',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'संबंध',
|
||||||
primary: 'प्राथमिक तालिका',
|
primary: 'प्राथमिक तालिका',
|
||||||
foreign: 'संदर्भित तालिका',
|
foreign: 'संदर्भित तालिका',
|
||||||
cardinality: 'कार्डिनैलिटी',
|
cardinality: 'कार्डिनैलिटी',
|
||||||
@@ -195,28 +197,19 @@ export const hi: LanguageTranslation = {
|
|||||||
delete_relationship: 'हटाएँ',
|
delete_relationship: 'हटाएँ',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'कोई संबंध नहीं',
|
|
||||||
description:
|
|
||||||
'तालिकाओं को कनेक्ट करने के लिए एक संबंध बनाएँ',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'निर्भरताएँ',
|
|
||||||
filter: 'फ़िल्टर',
|
|
||||||
collapse: 'सिकोड़ें',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'निर्भरता',
|
||||||
table: 'तालिका',
|
table: 'तालिका',
|
||||||
dependent_table: 'आश्रित तालिका',
|
dependent_table: 'आश्रित दृश्य',
|
||||||
delete_dependency: 'निर्भरता हटाएँ',
|
delete_dependency: 'हटाएँ',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'कार्रवाइयाँ',
|
title: 'क्रियाएँ',
|
||||||
delete_dependency: 'निर्भरता हटाएँ',
|
delete_dependency: 'हटाएँ',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'कोई निर्भरता नहीं',
|
title: 'कोई संबंध नहीं',
|
||||||
description: 'इस अनुभाग में कोई निर्भरता उपलब्ध नहीं है।',
|
description: 'शुरू करने के लिए एक संबंध बनाएँ',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -240,6 +233,35 @@ export const hi: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visuals',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'नोट्स',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'फ़िल्टर',
|
||||||
|
add_note: 'नोट जोड़ें',
|
||||||
|
no_results: 'कोई नोट नहीं मिला',
|
||||||
|
clear: 'फ़िल्टर साफ़ करें',
|
||||||
|
empty_state: {
|
||||||
|
title: 'कोई नोट नहीं',
|
||||||
|
description:
|
||||||
|
'कैनवास पर टेक्स्ट एनोटेशन जोड़ने के लिए एक नोट बनाएं',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'खाली नोट',
|
||||||
|
note_actions: {
|
||||||
|
title: 'नोट क्रियाएं',
|
||||||
|
edit_content: 'सामग्री संपादित करें',
|
||||||
|
delete_note: 'नोट हटाएं',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -256,6 +278,7 @@ export const hi: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'कोई enum मान परिभाषित नहीं',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -278,7 +301,7 @@ export const hi: LanguageTranslation = {
|
|||||||
show_all: 'सभी दिखाएँ',
|
show_all: 'सभी दिखाएँ',
|
||||||
undo: 'पूर्ववत करें',
|
undo: 'पूर्ववत करें',
|
||||||
redo: 'पुनः करें',
|
redo: 'पुनः करें',
|
||||||
reorder_diagram: 'आरेख पुनः व्यवस्थित करें',
|
reorder_diagram: 'आरेख स्वचालित व्यवस्थित करें',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -318,13 +341,13 @@ export const hi: LanguageTranslation = {
|
|||||||
back: 'वापस',
|
back: 'वापस',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: 'खाली आरेख',
|
empty_diagram: 'खाली डेटाबेस',
|
||||||
continue: 'जारी रखें',
|
continue: 'जारी रखें',
|
||||||
import: 'आयात करें',
|
import: 'आयात करें',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'आरेख खोलें',
|
title: 'डेटाबेस खोलें',
|
||||||
description: 'नीचे दी गई सूची से एक आरेख चुनें।',
|
description: 'नीचे दी गई सूची से एक आरेख चुनें।',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'नाम',
|
name: 'नाम',
|
||||||
@@ -334,6 +357,12 @@ export const hi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'रद्द करें',
|
cancel: 'रद्द करें',
|
||||||
open: 'खोलें',
|
open: 'खोलें',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'खोलें',
|
||||||
|
duplicate: 'डुप्लिकेट',
|
||||||
|
delete: 'हटाएं',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -483,9 +512,11 @@ export const hi: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'नई तालिका',
|
new_table: 'नई तालिका',
|
||||||
|
new_view: 'नया व्यू',
|
||||||
new_relationship: 'नया संबंध',
|
new_relationship: 'नया संबंध',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'नया नोट',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -505,6 +536,9 @@ export const hi: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'भाषा बदलें',
|
change_language: 'भाषा बदलें',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'चालू',
|
||||||
|
off: 'बंद',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const hr: LanguageTranslation = {
|
export const hr: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Novi',
|
||||||
|
browse: 'Pregledaj',
|
||||||
|
tables: 'Tablice',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'Ovisnosti',
|
||||||
|
custom_types: 'Prilagođeni Tipovi',
|
||||||
|
visuals: 'Vizuali',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Datoteka',
|
actions: 'Akcije',
|
||||||
new: 'Nova',
|
new: 'Novi...',
|
||||||
open: 'Otvori',
|
browse: 'Pregledaj...',
|
||||||
save: 'Spremi',
|
save: 'Spremi',
|
||||||
import: 'Uvezi',
|
import: 'Uvezi',
|
||||||
export_sql: 'Izvezi SQL',
|
export_sql: 'Izvezi SQL',
|
||||||
export_as: 'Izvezi kao',
|
export_as: 'Izvezi kao',
|
||||||
delete_diagram: 'Izbriši dijagram',
|
delete_diagram: 'Izbriši',
|
||||||
exit: 'Izađi',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Uredi',
|
edit: 'Uredi',
|
||||||
@@ -29,6 +37,7 @@ export const hr: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Sakrij atribute polja',
|
hide_field_attributes: 'Sakrij atribute polja',
|
||||||
show_field_attributes: 'Prikaži atribute polja',
|
show_field_attributes: 'Prikaži atribute polja',
|
||||||
zoom_on_scroll: 'Zumiranje pri skrolanju',
|
zoom_on_scroll: 'Zumiranje pri skrolanju',
|
||||||
|
show_views: 'Pogledi Baze Podataka',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Prikaži ovisnosti',
|
show_dependencies: 'Prikaži ovisnosti',
|
||||||
hide_dependencies: 'Sakrij ovisnosti',
|
hide_dependencies: 'Sakrij ovisnosti',
|
||||||
@@ -64,21 +73,13 @@ export const hr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Preuredi dijagram',
|
title: 'Automatski preuredi dijagram',
|
||||||
description:
|
description:
|
||||||
'Ova radnja će preurediti sve tablice u dijagramu. Želite li nastaviti?',
|
'Ova radnja će preurediti sve tablice u dijagramu. Želite li nastaviti?',
|
||||||
reorder: 'Preuredi',
|
reorder: 'Automatski preuredi',
|
||||||
cancel: 'Odustani',
|
cancel: 'Odustani',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Više shema',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} shema u ovom dijagramu. Trenutno prikazano: {{formattedSchemas}}.',
|
|
||||||
show_me: 'Prikaži mi',
|
|
||||||
none: 'nijedna',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Kopiranje neuspješno',
|
title: 'Kopiranje neuspješno',
|
||||||
@@ -113,14 +114,11 @@ export const hr: LanguageTranslation = {
|
|||||||
copied: 'Kopirano!',
|
copied: 'Kopirano!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Shema:',
|
|
||||||
filter_by_schema: 'Filtriraj po shemi',
|
|
||||||
search_schema: 'Pretraži shemu...',
|
|
||||||
no_schemas_found: 'Nema pronađenih shema.',
|
|
||||||
view_all_options: 'Prikaži sve opcije...',
|
view_all_options: 'Prikaži sve opcije...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tablice',
|
tables: 'Tablice',
|
||||||
add_table: 'Dodaj tablicu',
|
add_table: 'Dodaj tablicu',
|
||||||
|
add_view: 'Dodaj Pogled',
|
||||||
filter: 'Filtriraj',
|
filter: 'Filtriraj',
|
||||||
collapse: 'Sažmi sve',
|
collapse: 'Sažmi sve',
|
||||||
clear: 'Očisti filter',
|
clear: 'Očisti filter',
|
||||||
@@ -145,6 +143,7 @@ export const hr: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Atributi polja',
|
title: 'Atributi polja',
|
||||||
unique: 'Jedinstven',
|
unique: 'Jedinstven',
|
||||||
|
auto_increment: 'Automatsko povećavanje',
|
||||||
character_length: 'Maksimalna dužina',
|
character_length: 'Maksimalna dužina',
|
||||||
precision: 'Preciznost',
|
precision: 'Preciznost',
|
||||||
scale: 'Skala',
|
scale: 'Skala',
|
||||||
@@ -158,6 +157,7 @@ export const hr: LanguageTranslation = {
|
|||||||
title: 'Atributi indeksa',
|
title: 'Atributi indeksa',
|
||||||
name: 'Naziv',
|
name: 'Naziv',
|
||||||
unique: 'Jedinstven',
|
unique: 'Jedinstven',
|
||||||
|
index_type: 'Vrsta indeksa',
|
||||||
delete_index: 'Izbriši indeks',
|
delete_index: 'Izbriši indeks',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -174,12 +174,15 @@ export const hr: LanguageTranslation = {
|
|||||||
description: 'Stvorite tablicu za početak',
|
description: 'Stvorite tablicu za početak',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Veze',
|
refs: 'Refs',
|
||||||
filter: 'Filtriraj',
|
filter: 'Filtriraj',
|
||||||
add_relationship: 'Dodaj vezu',
|
|
||||||
collapse: 'Sažmi sve',
|
collapse: 'Sažmi sve',
|
||||||
|
add_relationship: 'Dodaj vezu',
|
||||||
|
relationships: 'Veze',
|
||||||
|
dependencies: 'Ovisnosti',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Veza',
|
||||||
primary: 'Primarna tablica',
|
primary: 'Primarna tablica',
|
||||||
foreign: 'Referentna tablica',
|
foreign: 'Referentna tablica',
|
||||||
cardinality: 'Kardinalnost',
|
cardinality: 'Kardinalnost',
|
||||||
@@ -189,16 +192,8 @@ export const hr: LanguageTranslation = {
|
|||||||
delete_relationship: 'Izbriši',
|
delete_relationship: 'Izbriši',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Nema veza',
|
|
||||||
description: 'Stvorite vezu za povezivanje tablica',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Ovisnosti',
|
|
||||||
filter: 'Filtriraj',
|
|
||||||
collapse: 'Sažmi sve',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Ovisnost',
|
||||||
table: 'Tablica',
|
table: 'Tablica',
|
||||||
dependent_table: 'Ovisni pogled',
|
dependent_table: 'Ovisni pogled',
|
||||||
delete_dependency: 'Izbriši',
|
delete_dependency: 'Izbriši',
|
||||||
@@ -208,8 +203,8 @@ export const hr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Nema ovisnosti',
|
title: 'Nema veze',
|
||||||
description: 'Stvorite pogled za početak',
|
description: 'Stvorite vezu za početak',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -234,6 +229,34 @@ export const hr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Vizuali',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Područja',
|
||||||
|
notes: 'Bilješke',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Filtriraj',
|
||||||
|
add_note: 'Dodaj Bilješku',
|
||||||
|
no_results: 'Nije pronađena nijedna bilješka',
|
||||||
|
clear: 'Očisti Filter',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Nema Bilješki',
|
||||||
|
description:
|
||||||
|
'Kreirajte bilješku za dodavanje tekstualnih napomena na platnu',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Prazna bilješka',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Akcije Bilješke',
|
||||||
|
edit_content: 'Uredi Sadržaj',
|
||||||
|
delete_note: 'Obriši Bilješku',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Prilagođeni tipovi',
|
custom_types: 'Prilagođeni tipovi',
|
||||||
filter: 'Filtriraj',
|
filter: 'Filtriraj',
|
||||||
@@ -250,6 +273,7 @@ export const hr: LanguageTranslation = {
|
|||||||
enum_values: 'Enum vrijednosti',
|
enum_values: 'Enum vrijednosti',
|
||||||
composite_fields: 'Polja',
|
composite_fields: 'Polja',
|
||||||
no_fields: 'Nema definiranih polja',
|
no_fields: 'Nema definiranih polja',
|
||||||
|
no_values: 'Nema definiranih enum vrijednosti',
|
||||||
field_name_placeholder: 'Naziv polja',
|
field_name_placeholder: 'Naziv polja',
|
||||||
field_type_placeholder: 'Odaberi tip',
|
field_type_placeholder: 'Odaberi tip',
|
||||||
add_field: 'Dodaj polje',
|
add_field: 'Dodaj polje',
|
||||||
@@ -273,7 +297,7 @@ export const hr: LanguageTranslation = {
|
|||||||
show_all: 'Prikaži sve',
|
show_all: 'Prikaži sve',
|
||||||
undo: 'Poništi',
|
undo: 'Poništi',
|
||||||
redo: 'Ponovi',
|
redo: 'Ponovi',
|
||||||
reorder_diagram: 'Preuredi dijagram',
|
reorder_diagram: 'Automatski preuredi dijagram',
|
||||||
highlight_overlapping_tables: 'Istakni preklapajuće tablice',
|
highlight_overlapping_tables: 'Istakni preklapajuće tablice',
|
||||||
clear_custom_type_highlight: 'Ukloni isticanje za "{{typeName}}"',
|
clear_custom_type_highlight: 'Ukloni isticanje za "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -309,13 +333,13 @@ export const hr: LanguageTranslation = {
|
|||||||
cancel: 'Odustani',
|
cancel: 'Odustani',
|
||||||
import_from_file: 'Uvezi iz datoteke',
|
import_from_file: 'Uvezi iz datoteke',
|
||||||
back: 'Natrag',
|
back: 'Natrag',
|
||||||
empty_diagram: 'Prazan dijagram',
|
empty_diagram: 'Prazna baza podataka',
|
||||||
continue: 'Nastavi',
|
continue: 'Nastavi',
|
||||||
import: 'Uvezi',
|
import: 'Uvezi',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Otvori dijagram',
|
title: 'Otvori bazu podataka',
|
||||||
description: 'Odaberite dijagram za otvaranje iz popisa ispod.',
|
description: 'Odaberite dijagram za otvaranje iz popisa ispod.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Naziv',
|
name: 'Naziv',
|
||||||
@@ -325,6 +349,12 @@ export const hr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Odustani',
|
cancel: 'Odustani',
|
||||||
open: 'Otvori',
|
open: 'Otvori',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Otvori',
|
||||||
|
duplicate: 'Dupliciraj',
|
||||||
|
delete: 'Obriši',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -473,8 +503,10 @@ export const hr: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Nova tablica',
|
new_table: 'Nova tablica',
|
||||||
|
new_view: 'Novi Pogled',
|
||||||
new_relationship: 'Nova veza',
|
new_relationship: 'Nova veza',
|
||||||
new_area: 'Novo područje',
|
new_area: 'Novo područje',
|
||||||
|
new_note: 'Nova Bilješka',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -493,6 +525,9 @@ export const hr: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Jezik',
|
change_language: 'Jezik',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Uključeno',
|
||||||
|
off: 'Isključeno',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const id_ID: LanguageTranslation = {
|
export const id_ID: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Baru',
|
||||||
|
browse: 'Jelajahi',
|
||||||
|
tables: 'Tabel',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'Ketergantungan',
|
||||||
|
custom_types: 'Tipe Kustom',
|
||||||
|
visuals: 'Visual',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Berkas',
|
actions: 'Aksi',
|
||||||
new: 'Buat Baru',
|
new: 'Baru...',
|
||||||
open: 'Buka',
|
browse: 'Jelajahi...',
|
||||||
save: 'Simpan',
|
save: 'Simpan',
|
||||||
import: 'Impor Database',
|
import: 'Impor Database',
|
||||||
export_sql: 'Ekspor SQL',
|
export_sql: 'Ekspor SQL',
|
||||||
export_as: 'Ekspor Sebagai',
|
export_as: 'Ekspor Sebagai',
|
||||||
delete_diagram: 'Hapus Diagram',
|
delete_diagram: 'Hapus',
|
||||||
exit: 'Keluar',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Ubah',
|
edit: 'Ubah',
|
||||||
@@ -29,6 +37,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Sembunyikan Atribut Kolom',
|
hide_field_attributes: 'Sembunyikan Atribut Kolom',
|
||||||
show_field_attributes: 'Tampilkan Atribut Kolom',
|
show_field_attributes: 'Tampilkan Atribut Kolom',
|
||||||
zoom_on_scroll: 'Perbesar saat Scroll',
|
zoom_on_scroll: 'Perbesar saat Scroll',
|
||||||
|
show_views: 'Tampilan Database',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Tampilkan Dependensi',
|
show_dependencies: 'Tampilkan Dependensi',
|
||||||
hide_dependencies: 'Sembunyikan Dependensi',
|
hide_dependencies: 'Sembunyikan Dependensi',
|
||||||
@@ -65,22 +74,13 @@ export const id_ID: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Atur Ulang Diagram',
|
title: 'Atur Otomatis Diagram',
|
||||||
description:
|
description:
|
||||||
'Tindakan ini akan mengatur ulang semua tabel di diagram. Apakah Anda ingin melanjutkan?',
|
'Tindakan ini akan mengatur ulang semua tabel di diagram. Apakah Anda ingin melanjutkan?',
|
||||||
reorder: 'Atur Ulang',
|
reorder: 'Atur Otomatis',
|
||||||
cancel: 'Batal',
|
cancel: 'Batal',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Schema Lebih dari satu',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} schema di diagram ini. Sedang ditampilkan: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'Tidak ada',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Gagal menyalin',
|
title: 'Gagal menyalin',
|
||||||
@@ -115,14 +115,11 @@ export const id_ID: LanguageTranslation = {
|
|||||||
copied: 'Tersalin!',
|
copied: 'Tersalin!',
|
||||||
|
|
||||||
side_panel: {
|
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...',
|
view_all_options: 'Tampilkan Semua Pilihan...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tabel',
|
tables: 'Tabel',
|
||||||
add_table: 'Tambah Tabel',
|
add_table: 'Tambah Tabel',
|
||||||
|
add_view: 'Tambah Tampilan',
|
||||||
filter: 'Saring',
|
filter: 'Saring',
|
||||||
collapse: 'Lipat Semua',
|
collapse: 'Lipat Semua',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Atribut Kolom',
|
title: 'Atribut Kolom',
|
||||||
unique: 'Unik',
|
unique: 'Unik',
|
||||||
|
auto_increment: 'Kenaikan Otomatis',
|
||||||
comments: 'Komentar',
|
comments: 'Komentar',
|
||||||
no_comments: 'Tidak ada komentar',
|
no_comments: 'Tidak ada komentar',
|
||||||
delete_field: 'Hapus Kolom',
|
delete_field: 'Hapus Kolom',
|
||||||
@@ -163,6 +161,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
title: 'Atribut Indeks',
|
title: 'Atribut Indeks',
|
||||||
name: 'Nama',
|
name: 'Nama',
|
||||||
unique: 'Unik',
|
unique: 'Unik',
|
||||||
|
index_type: 'Tipe Indeks',
|
||||||
delete_index: 'Hapus Indeks',
|
delete_index: 'Hapus Indeks',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -179,12 +178,15 @@ export const id_ID: LanguageTranslation = {
|
|||||||
description: 'Buat tabel untuk memulai',
|
description: 'Buat tabel untuk memulai',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Hubungan',
|
refs: 'Refs',
|
||||||
filter: 'Saring',
|
filter: 'Saring',
|
||||||
add_relationship: 'Tambah Hubungan',
|
|
||||||
collapse: 'Lipat Semua',
|
collapse: 'Lipat Semua',
|
||||||
|
add_relationship: 'Tambah Hubungan',
|
||||||
|
relationships: 'Hubungan',
|
||||||
|
dependencies: 'Dependensi',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Hubungan',
|
||||||
primary: 'Tabel Primer',
|
primary: 'Tabel Primer',
|
||||||
foreign: 'Tabel Referensi',
|
foreign: 'Tabel Referensi',
|
||||||
cardinality: 'Kardinalitas',
|
cardinality: 'Kardinalitas',
|
||||||
@@ -194,16 +196,8 @@ export const id_ID: LanguageTranslation = {
|
|||||||
delete_relationship: 'Hapus',
|
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: {
|
||||||
|
dependency: 'Dependensi',
|
||||||
table: 'Tabel',
|
table: 'Tabel',
|
||||||
dependent_table: 'Tampilan Dependen',
|
dependent_table: 'Tampilan Dependen',
|
||||||
delete_dependency: 'Hapus',
|
delete_dependency: 'Hapus',
|
||||||
@@ -213,8 +207,8 @@ export const id_ID: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Tidak ada dependensi',
|
title: 'Tidak ada hubungan',
|
||||||
description: 'Buat tampilan untuk memulai',
|
description: 'Buat hubungan untuk memulai',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -238,6 +232,35 @@ export const id_ID: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visual',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'Catatan',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Filter',
|
||||||
|
add_note: 'Tambah Catatan',
|
||||||
|
no_results: 'Tidak ada catatan ditemukan',
|
||||||
|
clear: 'Hapus Filter',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Tidak Ada Catatan',
|
||||||
|
description:
|
||||||
|
'Buat catatan untuk menambahkan anotasi teks di kanvas',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Catatan kosong',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Aksi Catatan',
|
||||||
|
edit_content: 'Edit Konten',
|
||||||
|
delete_note: 'Hapus Catatan',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -254,6 +277,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Tidak ada nilai enum yang ditentukan',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +300,7 @@ export const id_ID: LanguageTranslation = {
|
|||||||
show_all: 'Tampilkan Semua',
|
show_all: 'Tampilkan Semua',
|
||||||
undo: 'Undo',
|
undo: 'Undo',
|
||||||
redo: 'Redo',
|
redo: 'Redo',
|
||||||
reorder_diagram: 'Atur Ulang Diagram',
|
reorder_diagram: 'Atur Otomatis Diagram',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -314,13 +338,13 @@ export const id_ID: LanguageTranslation = {
|
|||||||
cancel: 'Batal',
|
cancel: 'Batal',
|
||||||
import_from_file: 'Impor dari file',
|
import_from_file: 'Impor dari file',
|
||||||
back: 'Kembali',
|
back: 'Kembali',
|
||||||
empty_diagram: 'Diagram Kosong',
|
empty_diagram: 'Database Kosong',
|
||||||
continue: 'Lanjutkan',
|
continue: 'Lanjutkan',
|
||||||
import: 'Impor',
|
import: 'Impor',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Buka Diagram',
|
title: 'Buka Database',
|
||||||
description: 'Pilih diagram untuk dibuka dari daftar di bawah.',
|
description: 'Pilih diagram untuk dibuka dari daftar di bawah.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
@@ -330,6 +354,12 @@ export const id_ID: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Batal',
|
cancel: 'Batal',
|
||||||
open: 'Buka',
|
open: 'Buka',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Buka',
|
||||||
|
duplicate: 'Duplikat',
|
||||||
|
delete: 'Hapus',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -479,9 +509,11 @@ export const id_ID: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Tabel Baru',
|
new_table: 'Tabel Baru',
|
||||||
|
new_view: 'Tampilan Baru',
|
||||||
new_relationship: 'Hubungan Baru',
|
new_relationship: 'Hubungan Baru',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'Catatan Baru',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -500,6 +532,9 @@ export const id_ID: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Bahasa',
|
change_language: 'Bahasa',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Aktif',
|
||||||
|
off: 'Nonaktif',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ja: LanguageTranslation = {
|
export const ja: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: '新規',
|
||||||
|
browse: '参照',
|
||||||
|
tables: 'テーブル',
|
||||||
|
refs: '参照',
|
||||||
|
dependencies: '依存関係',
|
||||||
|
custom_types: 'カスタムタイプ',
|
||||||
|
visuals: 'ビジュアル',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ファイル',
|
actions: 'アクション',
|
||||||
new: '新規',
|
new: '新規...',
|
||||||
open: '開く',
|
browse: '参照...',
|
||||||
save: '保存',
|
save: '保存',
|
||||||
import: 'データベースをインポート',
|
import: 'データベースをインポート',
|
||||||
export_sql: 'SQLをエクスポート',
|
export_sql: 'SQLをエクスポート',
|
||||||
export_as: '形式を指定してエクスポート',
|
export_as: '形式を指定してエクスポート',
|
||||||
delete_diagram: 'ダイアグラムを削除',
|
delete_diagram: '削除',
|
||||||
exit: '終了',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: '編集',
|
edit: '編集',
|
||||||
@@ -29,6 +37,7 @@ export const ja: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'フィールド属性を非表示',
|
hide_field_attributes: 'フィールド属性を非表示',
|
||||||
show_field_attributes: 'フィールド属性を表示',
|
show_field_attributes: 'フィールド属性を表示',
|
||||||
zoom_on_scroll: 'スクロールでズーム',
|
zoom_on_scroll: 'スクロールでズーム',
|
||||||
|
show_views: 'データベースビュー',
|
||||||
theme: 'テーマ',
|
theme: 'テーマ',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
show_dependencies: 'Show Dependencies',
|
show_dependencies: 'Show Dependencies',
|
||||||
@@ -67,22 +76,13 @@ export const ja: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'ダイアグラムを並べ替え',
|
title: 'ダイアグラムを自動配置',
|
||||||
description:
|
description:
|
||||||
'この操作によりダイアグラム内のすべてのテーブルが再配置されます。続行しますか?',
|
'この操作によりダイアグラム内のすべてのテーブルが再配置されます。続行しますか?',
|
||||||
reorder: '並べ替え',
|
reorder: '自動配置',
|
||||||
cancel: 'キャンセル',
|
cancel: 'キャンセル',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: '複数のスキーマ',
|
|
||||||
description:
|
|
||||||
'このダイアグラムには{{schemasCount}}個のスキーマがあります。現在表示中: {{formattedSchemas}}。',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'なし',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'コピー失敗',
|
title: 'コピー失敗',
|
||||||
@@ -119,14 +119,11 @@ export const ja: LanguageTranslation = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'スキーマ:',
|
|
||||||
filter_by_schema: 'スキーマでフィルタ',
|
|
||||||
search_schema: 'スキーマを検索...',
|
|
||||||
no_schemas_found: 'スキーマが見つかりません。',
|
|
||||||
view_all_options: 'すべてのオプションを表示...',
|
view_all_options: 'すべてのオプションを表示...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'テーブル',
|
tables: 'テーブル',
|
||||||
add_table: 'テーブルを追加',
|
add_table: 'テーブルを追加',
|
||||||
|
add_view: 'ビューを追加',
|
||||||
filter: 'フィルタ',
|
filter: 'フィルタ',
|
||||||
collapse: 'すべて折りたたむ',
|
collapse: 'すべて折りたたむ',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -152,6 +149,7 @@ export const ja: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'フィールド属性',
|
title: 'フィールド属性',
|
||||||
unique: 'ユニーク',
|
unique: 'ユニーク',
|
||||||
|
auto_increment: 'オートインクリメント',
|
||||||
comments: 'コメント',
|
comments: 'コメント',
|
||||||
no_comments: 'コメントがありません',
|
no_comments: 'コメントがありません',
|
||||||
delete_field: 'フィールドを削除',
|
delete_field: 'フィールドを削除',
|
||||||
@@ -167,6 +165,7 @@ export const ja: LanguageTranslation = {
|
|||||||
title: 'インデックス属性',
|
title: 'インデックス属性',
|
||||||
name: '名前',
|
name: '名前',
|
||||||
unique: 'ユニーク',
|
unique: 'ユニーク',
|
||||||
|
index_type: 'インデックスタイプ',
|
||||||
delete_index: 'インデックスを削除',
|
delete_index: 'インデックスを削除',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -183,12 +182,15 @@ export const ja: LanguageTranslation = {
|
|||||||
description: 'テーブルを作成して開始してください',
|
description: 'テーブルを作成して開始してください',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'リレーションシップ',
|
refs: '参照',
|
||||||
filter: 'フィルタ',
|
filter: 'フィルタ',
|
||||||
add_relationship: 'リレーションシップを追加',
|
|
||||||
collapse: 'すべて折りたたむ',
|
collapse: 'すべて折りたたむ',
|
||||||
|
add_relationship: 'リレーションシップを追加',
|
||||||
|
relationships: 'リレーションシップ',
|
||||||
|
dependencies: '依存関係',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'リレーションシップ',
|
||||||
primary: '主テーブル',
|
primary: '主テーブル',
|
||||||
foreign: '参照テーブル',
|
foreign: '参照テーブル',
|
||||||
cardinality: 'カーディナリティ',
|
cardinality: 'カーディナリティ',
|
||||||
@@ -198,29 +200,20 @@ export const ja: LanguageTranslation = {
|
|||||||
delete_relationship: '削除',
|
delete_relationship: '削除',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'リレーションシップがありません',
|
|
||||||
description:
|
|
||||||
'テーブルを接続するためにリレーションシップを作成してください',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// TODO: Translate
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Dependencies',
|
|
||||||
filter: 'Filter',
|
|
||||||
collapse: 'Collapse All',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
table: 'Table',
|
dependency: '依存関係',
|
||||||
dependent_table: 'Dependent View',
|
table: 'テーブル',
|
||||||
delete_dependency: 'Delete',
|
dependent_table: '依存ビュー',
|
||||||
|
delete_dependency: '削除',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'Actions',
|
title: '操作',
|
||||||
delete_dependency: 'Delete',
|
delete_dependency: '削除',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'No dependencies',
|
title: 'リレーションシップがありません',
|
||||||
description: 'Create a view to get started',
|
description:
|
||||||
|
'開始するためにリレーションシップを作成してください',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -244,6 +237,35 @@ export const ja: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'ビジュアル',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'ノート',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'フィルター',
|
||||||
|
add_note: 'ノートを追加',
|
||||||
|
no_results: 'ノートが見つかりません',
|
||||||
|
clear: 'フィルターをクリア',
|
||||||
|
empty_state: {
|
||||||
|
title: 'ノートがありません',
|
||||||
|
description:
|
||||||
|
'キャンバス上にテキスト注釈を追加するためのノートを作成',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: '空のノート',
|
||||||
|
note_actions: {
|
||||||
|
title: 'ノートアクション',
|
||||||
|
edit_content: 'コンテンツを編集',
|
||||||
|
delete_note: 'ノートを削除',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -260,6 +282,7 @@ export const ja: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: '列挙値が定義されていません',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -282,7 +305,7 @@ export const ja: LanguageTranslation = {
|
|||||||
show_all: 'すべて表示',
|
show_all: 'すべて表示',
|
||||||
undo: '元に戻す',
|
undo: '元に戻す',
|
||||||
redo: 'やり直し',
|
redo: 'やり直し',
|
||||||
reorder_diagram: 'ダイアグラムを並べ替え',
|
reorder_diagram: 'ダイアグラムを自動配置',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
highlight_overlapping_tables: 'Highlight Overlapping Tables',
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
@@ -320,13 +343,13 @@ export const ja: LanguageTranslation = {
|
|||||||
back: '戻る',
|
back: '戻る',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: '空のダイアグラム',
|
empty_diagram: '空のデータベース',
|
||||||
continue: '続行',
|
continue: '続行',
|
||||||
import: 'インポート',
|
import: 'インポート',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'ダイアグラムを開く',
|
title: 'データベースを開く',
|
||||||
description: '以下のリストからダイアグラムを選択してください。',
|
description: '以下のリストからダイアグラムを選択してください。',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: '名前',
|
name: '名前',
|
||||||
@@ -336,6 +359,12 @@ export const ja: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'キャンセル',
|
cancel: 'キャンセル',
|
||||||
open: '開く',
|
open: '開く',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: '開く',
|
||||||
|
duplicate: '複製',
|
||||||
|
delete: '削除',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -485,9 +514,11 @@ export const ja: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: '新しいテーブル',
|
new_table: '新しいテーブル',
|
||||||
|
new_view: '新しいビュー',
|
||||||
new_relationship: '新しいリレーションシップ',
|
new_relationship: '新しいリレーションシップ',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: '新しいメモ',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -507,6 +538,9 @@ export const ja: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: '言語',
|
change_language: '言語',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'オン',
|
||||||
|
off: 'オフ',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ko_KR: LanguageTranslation = {
|
export const ko_KR: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: '새로 만들기',
|
||||||
|
browse: '찾아보기',
|
||||||
|
tables: '테이블',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: '종속성',
|
||||||
|
custom_types: '사용자 지정 타입',
|
||||||
|
visuals: '시각화',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: '파일',
|
actions: '작업',
|
||||||
new: '새 다이어그램',
|
new: '새로 만들기...',
|
||||||
open: '열기',
|
browse: '찾아보기...',
|
||||||
save: '저장',
|
save: '저장',
|
||||||
import: '데이터베이스 가져오기',
|
import: '데이터베이스 가져오기',
|
||||||
export_sql: 'SQL로 저장',
|
export_sql: 'SQL로 저장',
|
||||||
export_as: '다른 형식으로 저장',
|
export_as: '다른 형식으로 저장',
|
||||||
delete_diagram: '다이어그램 삭제',
|
delete_diagram: '삭제',
|
||||||
exit: '종료',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: '편집',
|
edit: '편집',
|
||||||
@@ -29,6 +37,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
hide_field_attributes: '필드 속성 숨기기',
|
hide_field_attributes: '필드 속성 숨기기',
|
||||||
show_field_attributes: '필드 속성 보이기',
|
show_field_attributes: '필드 속성 보이기',
|
||||||
zoom_on_scroll: '스크롤 시 확대',
|
zoom_on_scroll: '스크롤 시 확대',
|
||||||
|
show_views: '데이터베이스 뷰',
|
||||||
theme: '테마',
|
theme: '테마',
|
||||||
show_dependencies: '종속성 보이기',
|
show_dependencies: '종속성 보이기',
|
||||||
hide_dependencies: '종속성 숨기기',
|
hide_dependencies: '종속성 숨기기',
|
||||||
@@ -65,22 +74,13 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: '다이어그램 재정렬',
|
title: '다이어그램 자동 정렬',
|
||||||
description:
|
description:
|
||||||
'이 작업은 모든 다이어그램이 재정렬됩니다. 계속하시겠습니까?',
|
'이 작업은 모든 다이어그램이 재정렬됩니다. 계속하시겠습니까?',
|
||||||
reorder: '재정렬',
|
reorder: '자동 정렬',
|
||||||
cancel: '취소',
|
cancel: '취소',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: '다중 스키마',
|
|
||||||
description:
|
|
||||||
'현재 다이어그램에 {{schemasCount}}개의 스키마가 있습니다. Currently displaying: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: '없음',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: '복사 실패',
|
title: '복사 실패',
|
||||||
@@ -115,14 +115,11 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
copied: '복사됨!',
|
copied: '복사됨!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: '스키마:',
|
|
||||||
filter_by_schema: '스키마로 필터링',
|
|
||||||
search_schema: '스키마 검색...',
|
|
||||||
no_schemas_found: '스키마를 찾을 수 없습니다.',
|
|
||||||
view_all_options: '전체 옵션 보기...',
|
view_all_options: '전체 옵션 보기...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: '테이블',
|
tables: '테이블',
|
||||||
add_table: '테이블 추가',
|
add_table: '테이블 추가',
|
||||||
|
add_view: '뷰 추가',
|
||||||
filter: '필터',
|
filter: '필터',
|
||||||
collapse: '모두 접기',
|
collapse: '모두 접기',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: '필드 속성',
|
title: '필드 속성',
|
||||||
unique: '유니크 여부',
|
unique: '유니크 여부',
|
||||||
|
auto_increment: '자동 증가',
|
||||||
comments: '주석',
|
comments: '주석',
|
||||||
no_comments: '주석 없음',
|
no_comments: '주석 없음',
|
||||||
delete_field: '필드 삭제',
|
delete_field: '필드 삭제',
|
||||||
@@ -163,6 +161,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
title: '인덱스 속성',
|
title: '인덱스 속성',
|
||||||
name: '인덱스 명',
|
name: '인덱스 명',
|
||||||
unique: '유니크 여부',
|
unique: '유니크 여부',
|
||||||
|
index_type: '인덱스 타입',
|
||||||
delete_index: '인덱스 삭제',
|
delete_index: '인덱스 삭제',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -179,12 +178,15 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
description: '테이블을 만들어 시작하세요.',
|
description: '테이블을 만들어 시작하세요.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: '연관 관계',
|
refs: 'Refs',
|
||||||
filter: '필터',
|
filter: '필터',
|
||||||
add_relationship: '연관 관계 추가',
|
|
||||||
collapse: '모두 접기',
|
collapse: '모두 접기',
|
||||||
|
add_relationship: '연관 관계 추가',
|
||||||
|
relationships: '연관 관계',
|
||||||
|
dependencies: '종속성',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: '연관 관계',
|
||||||
primary: '주 테이블',
|
primary: '주 테이블',
|
||||||
foreign: '참조 테이블',
|
foreign: '참조 테이블',
|
||||||
cardinality: '카디널리티',
|
cardinality: '카디널리티',
|
||||||
@@ -194,16 +196,8 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
delete_relationship: '연관 관계 삭제',
|
delete_relationship: '연관 관계 삭제',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: '연관 관계',
|
|
||||||
description: '테이블 연결을 위해 연관 관계를 생성하세요',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: '종속성',
|
|
||||||
filter: '필터',
|
|
||||||
collapse: '모두 접기',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: '종속성',
|
||||||
table: '테이블',
|
table: '테이블',
|
||||||
dependent_table: '뷰 테이블',
|
dependent_table: '뷰 테이블',
|
||||||
delete_dependency: '삭제',
|
delete_dependency: '삭제',
|
||||||
@@ -213,8 +207,8 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: '뷰 테이블 없음',
|
title: '연관 관계 없음',
|
||||||
description: '뷰 테이블을 만들어 시작하세요.',
|
description: '연관 관계를 만들어 시작하세요.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -238,6 +232,35 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: '시각화',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: '메모',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: '필터',
|
||||||
|
add_note: '메모 추가',
|
||||||
|
no_results: '메모를 찾을 수 없습니다',
|
||||||
|
clear: '필터 지우기',
|
||||||
|
empty_state: {
|
||||||
|
title: '메모 없음',
|
||||||
|
description:
|
||||||
|
'캔버스에 텍스트 주석을 추가하려면 메모를 만드세요',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: '빈 메모',
|
||||||
|
note_actions: {
|
||||||
|
title: '메모 작업',
|
||||||
|
edit_content: '내용 편집',
|
||||||
|
delete_note: '메모 삭제',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -254,6 +277,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: '정의된 열거형 값이 없습니다',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +300,7 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
show_all: '전체 저장',
|
show_all: '전체 저장',
|
||||||
undo: '실행 취소',
|
undo: '실행 취소',
|
||||||
redo: '다시 실행',
|
redo: '다시 실행',
|
||||||
reorder_diagram: '다이어그램 재정렬',
|
reorder_diagram: '다이어그램 자동 정렬',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -314,13 +338,13 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
cancel: '취소',
|
cancel: '취소',
|
||||||
back: '뒤로가기',
|
back: '뒤로가기',
|
||||||
import_from_file: '파일에서 가져오기',
|
import_from_file: '파일에서 가져오기',
|
||||||
empty_diagram: '빈 다이어그램으로 시작',
|
empty_diagram: '빈 데이터베이스',
|
||||||
continue: '계속',
|
continue: '계속',
|
||||||
import: '가져오기',
|
import: '가져오기',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: '다이어그램 열기',
|
title: '데이터베이스 열기',
|
||||||
description: '아래의 목록에서 다이어그램을 선택하세요.',
|
description: '아래의 목록에서 다이어그램을 선택하세요.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: '이름',
|
name: '이름',
|
||||||
@@ -330,6 +354,12 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: '취소',
|
cancel: '취소',
|
||||||
open: '열기',
|
open: '열기',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: '열기',
|
||||||
|
duplicate: '복제',
|
||||||
|
delete: '삭제',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -476,9 +506,11 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: '새 테이블',
|
new_table: '새 테이블',
|
||||||
|
new_view: '새 뷰',
|
||||||
new_relationship: '새 연관관계',
|
new_relationship: '새 연관관계',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: '새 메모',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -497,6 +529,9 @@ export const ko_KR: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: '언어',
|
change_language: '언어',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: '켜기',
|
||||||
|
off: '끄기',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const mr: LanguageTranslation = {
|
export const mr: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'नवीन',
|
||||||
|
browse: 'ब्राउज',
|
||||||
|
tables: 'टेबल',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'अवलंबने',
|
||||||
|
custom_types: 'कस्टम प्रकार',
|
||||||
|
visuals: 'Visuals',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'फाइल',
|
actions: 'क्रिया',
|
||||||
new: 'नवीन',
|
new: 'नवीन...',
|
||||||
open: 'उघडा',
|
browse: 'ब्राउज करा...',
|
||||||
save: 'जतन करा',
|
save: 'जतन करा',
|
||||||
import: 'डेटाबेस इम्पोर्ट करा',
|
import: 'डेटाबेस इम्पोर्ट करा',
|
||||||
export_sql: 'SQL एक्स्पोर्ट करा',
|
export_sql: 'SQL एक्स्पोर्ट करा',
|
||||||
export_as: 'म्हणून एक्स्पोर्ट करा',
|
export_as: 'म्हणून एक्स्पोर्ट करा',
|
||||||
delete_diagram: 'आरेख हटवा',
|
delete_diagram: 'हटवा',
|
||||||
exit: 'बाहेर पडा',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'संपादन करा',
|
edit: 'संपादन करा',
|
||||||
@@ -29,6 +37,7 @@ export const mr: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'फील्ड गुणधर्म लपवा',
|
hide_field_attributes: 'फील्ड गुणधर्म लपवा',
|
||||||
show_field_attributes: 'फील्ड गुणधर्म दाखवा',
|
show_field_attributes: 'फील्ड गुणधर्म दाखवा',
|
||||||
zoom_on_scroll: 'स्क्रोलवर झूम करा',
|
zoom_on_scroll: 'स्क्रोलवर झूम करा',
|
||||||
|
show_views: 'डेटाबेस व्ह्यूज',
|
||||||
theme: 'थीम',
|
theme: 'थीम',
|
||||||
show_dependencies: 'डिपेंडेन्सि दाखवा',
|
show_dependencies: 'डिपेंडेन्सि दाखवा',
|
||||||
hide_dependencies: 'डिपेंडेन्सि लपवा',
|
hide_dependencies: 'डिपेंडेन्सि लपवा',
|
||||||
@@ -66,22 +75,13 @@ export const mr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'आरेख पुनःक्रमित करा',
|
title: 'आरेख स्वयंचलित व्यवस्थित करा',
|
||||||
description:
|
description:
|
||||||
'ही क्रिया आरेखातील सर्व टेबल्सची पुनर्रचना करेल. तुम्हाला पुढे जायचे आहे का?',
|
'ही क्रिया आरेखातील सर्व टेबल्सची पुनर्रचना करेल. तुम्हाला पुढे जायचे आहे का?',
|
||||||
reorder: 'पुनःक्रमित करा',
|
reorder: 'स्वयंचलित व्यवस्थित करा',
|
||||||
cancel: 'रद्द करा',
|
cancel: 'रद्द करा',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'एकाधिक स्कीमा',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} स्कीमा या आरेखात आहेत. सध्या दाखवत आहोत: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'काहीही नाही',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'कॉपी अयशस्वी',
|
title: 'कॉपी अयशस्वी',
|
||||||
@@ -118,14 +118,11 @@ export const mr: LanguageTranslation = {
|
|||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'स्कीमा:',
|
|
||||||
filter_by_schema: 'स्कीमा द्वारे फिल्टर करा',
|
|
||||||
search_schema: 'स्कीमा शोधा...',
|
|
||||||
no_schemas_found: 'कोणतेही स्कीमा सापडले नाहीत.',
|
|
||||||
view_all_options: 'सर्व पर्याय पहा...',
|
view_all_options: 'सर्व पर्याय पहा...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'टेबल्स',
|
tables: 'टेबल्स',
|
||||||
add_table: 'टेबल जोडा',
|
add_table: 'टेबल जोडा',
|
||||||
|
add_view: 'व्ह्यू जोडा',
|
||||||
filter: 'फिल्टर',
|
filter: 'फिल्टर',
|
||||||
collapse: 'सर्व संकुचित करा',
|
collapse: 'सर्व संकुचित करा',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -151,6 +148,7 @@ export const mr: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'फील्ड गुणधर्म',
|
title: 'फील्ड गुणधर्म',
|
||||||
unique: 'युनिक',
|
unique: 'युनिक',
|
||||||
|
auto_increment: 'ऑटो इंक्रिमेंट',
|
||||||
comments: 'टिप्पण्या',
|
comments: 'टिप्पण्या',
|
||||||
no_comments: 'कोणत्याही टिप्पणी नाहीत',
|
no_comments: 'कोणत्याही टिप्पणी नाहीत',
|
||||||
delete_field: 'फील्ड हटवा',
|
delete_field: 'फील्ड हटवा',
|
||||||
@@ -166,6 +164,7 @@ export const mr: LanguageTranslation = {
|
|||||||
title: 'इंडेक्स गुणधर्म',
|
title: 'इंडेक्स गुणधर्म',
|
||||||
name: 'नाव',
|
name: 'नाव',
|
||||||
unique: 'युनिक',
|
unique: 'युनिक',
|
||||||
|
index_type: 'इंडेक्स प्रकार',
|
||||||
delete_index: 'इंडेक्स हटवा',
|
delete_index: 'इंडेक्स हटवा',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -183,12 +182,15 @@ export const mr: LanguageTranslation = {
|
|||||||
description: 'सुरू करण्यासाठी एक टेबल तयार करा',
|
description: 'सुरू करण्यासाठी एक टेबल तयार करा',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'रिलेशनशिप',
|
refs: 'Refs',
|
||||||
filter: 'फिल्टर',
|
filter: 'फिल्टर',
|
||||||
add_relationship: 'रिलेशनशिप जोडा',
|
|
||||||
collapse: 'सर्व संकुचित करा',
|
collapse: 'सर्व संकुचित करा',
|
||||||
|
add_relationship: 'रिलेशनशिप जोडा',
|
||||||
|
relationships: 'रिलेशनशिप',
|
||||||
|
dependencies: 'डिपेंडेन्सि',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'रिलेशनशिप',
|
||||||
primary: 'प्राथमिक टेबल',
|
primary: 'प्राथमिक टेबल',
|
||||||
foreign: 'रेफरंस टेबल',
|
foreign: 'रेफरंस टेबल',
|
||||||
cardinality: 'कार्डिनॅलिटी',
|
cardinality: 'कार्डिनॅलिटी',
|
||||||
@@ -198,17 +200,8 @@ export const mr: LanguageTranslation = {
|
|||||||
delete_relationship: 'हटवा',
|
delete_relationship: 'हटवा',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'कोणतेही रिलेशनशिप नाहीत',
|
|
||||||
description:
|
|
||||||
'टेबल्स कनेक्ट करण्यासाठी एक रिलेशनशिप तयार करा',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'डिपेंडेन्सि',
|
|
||||||
filter: 'फिल्टर',
|
|
||||||
collapse: 'सर्व संकुचित करा',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'डिपेंडेन्सि',
|
||||||
table: 'टेबल',
|
table: 'टेबल',
|
||||||
dependent_table: 'डिपेंडेन्सि दृश्य',
|
dependent_table: 'डिपेंडेन्सि दृश्य',
|
||||||
delete_dependency: 'हटवा',
|
delete_dependency: 'हटवा',
|
||||||
@@ -218,8 +211,8 @@ export const mr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'कोणत्याही डिपेंडेन्सि नाहीत',
|
title: 'कोणतेही रिलेशनशिप नाहीत',
|
||||||
description: 'सुरू करण्यासाठी एक दृश्य तयार करा',
|
description: 'सुरू करण्यासाठी एक रिलेशनशिप तयार करा',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -243,6 +236,35 @@ export const mr: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visuals',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'नोट्स',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'फिल्टर',
|
||||||
|
add_note: 'नोट जोडा',
|
||||||
|
no_results: 'कोणत्याही नोट्स सापडल्या नाहीत',
|
||||||
|
clear: 'फिल्टर साफ करा',
|
||||||
|
empty_state: {
|
||||||
|
title: 'नोट्स नाहीत',
|
||||||
|
description:
|
||||||
|
'कॅनव्हासवर मजकूर भाष्य जोडण्यासाठी एक नोट तयार करा',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'रिकामी नोट',
|
||||||
|
note_actions: {
|
||||||
|
title: 'नोट क्रिया',
|
||||||
|
edit_content: 'सामग्री संपादित करा',
|
||||||
|
delete_note: 'नोट हटवा',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -259,6 +281,7 @@ export const mr: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'कोणतीही enum मूल्ये परिभाषित नाहीत',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -281,7 +304,7 @@ export const mr: LanguageTranslation = {
|
|||||||
show_all: 'सर्व दाखवा',
|
show_all: 'सर्व दाखवा',
|
||||||
undo: 'पूर्ववत करा',
|
undo: 'पूर्ववत करा',
|
||||||
redo: 'पुन्हा करा',
|
redo: 'पुन्हा करा',
|
||||||
reorder_diagram: 'आरेख पुनःक्रमित करा',
|
reorder_diagram: 'आरेख स्वयंचलित व्यवस्थित करा',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -321,13 +344,13 @@ export const mr: LanguageTranslation = {
|
|||||||
// TODO: Add translations
|
// TODO: Add translations
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
back: 'मागे',
|
back: 'मागे',
|
||||||
empty_diagram: 'रिक्त आरेख',
|
empty_diagram: 'रिक्त डेटाबेस',
|
||||||
continue: 'सुरू ठेवा',
|
continue: 'सुरू ठेवा',
|
||||||
import: 'आयात करा',
|
import: 'आयात करा',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'आरेख उघडा',
|
title: 'डेटाबेस उघडा',
|
||||||
description: 'खालील यादीतून उघडण्यासाठी एक आरेख निवडा.',
|
description: 'खालील यादीतून उघडण्यासाठी एक आरेख निवडा.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'नाव',
|
name: 'नाव',
|
||||||
@@ -337,6 +360,12 @@ export const mr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'रद्द करा',
|
cancel: 'रद्द करा',
|
||||||
open: 'उघडा',
|
open: 'उघडा',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'उघडा',
|
||||||
|
duplicate: 'डुप्लिकेट',
|
||||||
|
delete: 'हटवा',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -489,9 +518,11 @@ export const mr: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'नवीन टेबल',
|
new_table: 'नवीन टेबल',
|
||||||
|
new_view: 'नवीन व्ह्यू',
|
||||||
new_relationship: 'नवीन रिलेशनशिप',
|
new_relationship: 'नवीन रिलेशनशिप',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'नवीन टीप',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -512,6 +543,9 @@ export const mr: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'भाषा बदला',
|
change_language: 'भाषा बदला',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'चालू',
|
||||||
|
off: 'बंद',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ne: LanguageTranslation = {
|
export const ne: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'नयाँ',
|
||||||
|
browse: 'ब्राउज',
|
||||||
|
tables: 'टेबलहरू',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'निर्भरताहरू',
|
||||||
|
custom_types: 'कस्टम प्रकारहरू',
|
||||||
|
visuals: 'Visuals',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'फाइल',
|
actions: 'कार्यहरू',
|
||||||
new: 'नयाँ',
|
new: 'नयाँ...',
|
||||||
open: 'खोल्नुहोस्',
|
browse: 'ब्राउज गर्नुहोस्...',
|
||||||
save: 'सुरक्षित गर्नुहोस्',
|
save: 'सुरक्षित गर्नुहोस्',
|
||||||
import: 'डाटाबेस आयात गर्नुहोस्',
|
import: 'डाटाबेस आयात गर्नुहोस्',
|
||||||
export_sql: 'SQL निर्यात गर्नुहोस्',
|
export_sql: 'SQL निर्यात गर्नुहोस्',
|
||||||
export_as: 'निर्यात गर्नुहोस्',
|
export_as: 'निर्यात गर्नुहोस्',
|
||||||
delete_diagram: 'डायाग्राम हटाउनुहोस्',
|
delete_diagram: 'हटाउनुहोस्',
|
||||||
exit: 'बाहिर निस्कनुहोस्',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'सम्पादन',
|
edit: 'सम्पादन',
|
||||||
@@ -29,6 +37,7 @@ export const ne: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'फिल्ड विशेषताहरू लुकाउनुहोस्',
|
hide_field_attributes: 'फिल्ड विशेषताहरू लुकाउनुहोस्',
|
||||||
show_field_attributes: 'फिल्ड विशेषताहरू देखाउनुहोस्',
|
show_field_attributes: 'फिल्ड विशेषताहरू देखाउनुहोस्',
|
||||||
zoom_on_scroll: 'स्क्रोलमा जुम गर्नुहोस्',
|
zoom_on_scroll: 'स्क्रोलमा जुम गर्नुहोस्',
|
||||||
|
show_views: 'डाटाबेस भ्यूहरू',
|
||||||
theme: 'थिम',
|
theme: 'थिम',
|
||||||
show_dependencies: 'डिपेन्डेन्सीहरू देखाउनुहोस्',
|
show_dependencies: 'डिपेन्डेन्सीहरू देखाउनुहोस्',
|
||||||
hide_dependencies: 'डिपेन्डेन्सीहरू लुकाउनुहोस्',
|
hide_dependencies: 'डिपेन्डेन्सीहरू लुकाउनुहोस्',
|
||||||
@@ -66,22 +75,13 @@ export const ne: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'डायाग्राम पुनः क्रमबद्ध गर्नुहोस्',
|
title: 'डायाग्राम स्वचालित मिलाउनुहोस्',
|
||||||
description:
|
description:
|
||||||
'यो कार्य पूर्ववत गर्न सकिँदैन। यो डायाग्राम स्थायी रूपमा हटाउनेछ।',
|
'यो कार्य पूर्ववत गर्न सकिँदैन। यो डायाग्राम स्थायी रूपमा हटाउनेछ।',
|
||||||
reorder: 'पुनः क्रमबद्ध गर्नुहोस्',
|
reorder: 'स्वचालित मिलाउनुहोस्',
|
||||||
cancel: 'रद्द गर्नुहोस्',
|
cancel: 'रद्द गर्नुहोस्',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'विविध स्कीमहरू',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} डायाग्राममा स्कीमहरू। हालको रूपमा देखाइएको छ: {{formattedSchemas}}।',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'कुनै पनि छैन',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'प्रतिलिपि असफल',
|
title: 'प्रतिलिपि असफल',
|
||||||
@@ -116,14 +116,11 @@ export const ne: LanguageTranslation = {
|
|||||||
copied: 'प्रतिलिपि गरियो!',
|
copied: 'प्रतिलिपि गरियो!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'स्कीम:',
|
|
||||||
filter_by_schema: 'स्कीम अनुसार फिल्टर गर्नुहोस्',
|
|
||||||
search_schema: 'स्कीम खोज्नुहोस्...',
|
|
||||||
no_schemas_found: 'कुनै स्कीमहरू फेला परेनन्',
|
|
||||||
view_all_options: 'सबै विकल्पहरू हेर्नुहोस्',
|
view_all_options: 'सबै विकल्पहरू हेर्नुहोस्',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'तालिकाहरू',
|
tables: 'तालिकाहरू',
|
||||||
add_table: 'तालिका थप्नुहोस्',
|
add_table: 'तालिका थप्नुहोस्',
|
||||||
|
add_view: 'भ्यू थप्नुहोस्',
|
||||||
filter: 'फिल्टर',
|
filter: 'फिल्टर',
|
||||||
collapse: 'सबै लुकाउनुहोस्',
|
collapse: 'सबै लुकाउनुहोस्',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const ne: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'क्षेत्र विशेषताहरू',
|
title: 'क्षेत्र विशेषताहरू',
|
||||||
unique: 'अनन्य',
|
unique: 'अनन्य',
|
||||||
|
auto_increment: 'स्वचालित वृद्धि',
|
||||||
comments: 'टिप्पणीहरू',
|
comments: 'टिप्पणीहरू',
|
||||||
no_comments: 'कुनै टिप्पणीहरू छैनन्',
|
no_comments: 'कुनै टिप्पणीहरू छैनन्',
|
||||||
delete_field: 'क्षेत्र हटाउनुहोस्',
|
delete_field: 'क्षेत्र हटाउनुहोस्',
|
||||||
@@ -164,6 +162,7 @@ export const ne: LanguageTranslation = {
|
|||||||
title: 'सूचक विशेषताहरू',
|
title: 'सूचक विशेषताहरू',
|
||||||
name: 'नाम',
|
name: 'नाम',
|
||||||
unique: 'अनन्य',
|
unique: 'अनन्य',
|
||||||
|
index_type: 'इन्डेक्स प्रकार',
|
||||||
delete_index: 'सूचक हटाउनुहोस्',
|
delete_index: 'सूचक हटाउनुहोस्',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,12 +179,15 @@ export const ne: LanguageTranslation = {
|
|||||||
description: 'सुरु गर्नका लागि एक तालिका बनाउनुहोस्',
|
description: 'सुरु गर्नका लागि एक तालिका बनाउनुहोस्',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'सम्बन्धहरू',
|
refs: 'Refs',
|
||||||
filter: 'फिल्टर',
|
filter: 'फिल्टर',
|
||||||
add_relationship: 'सम्बन्ध थप्नुहोस्',
|
|
||||||
collapse: 'सबै लुकाउनुहोस्',
|
collapse: 'सबै लुकाउनुहोस्',
|
||||||
|
add_relationship: 'सम्बन्ध थप्नुहोस्',
|
||||||
|
relationships: 'सम्बन्धहरू',
|
||||||
|
dependencies: 'डिपेन्डेन्सीहरू',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'सम्बन्ध',
|
||||||
primary: 'मुख्य तालिका',
|
primary: 'मुख्य तालिका',
|
||||||
foreign: 'परिचित तालिका',
|
foreign: 'परिचित तालिका',
|
||||||
cardinality: 'कार्डिन्यालिटी',
|
cardinality: 'कार्डिन्यालिटी',
|
||||||
@@ -195,16 +197,8 @@ export const ne: LanguageTranslation = {
|
|||||||
delete_relationship: 'हटाउनुहोस्',
|
delete_relationship: 'हटाउनुहोस्',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'कुनै सम्बन्धहरू छैनन्',
|
|
||||||
description: 'तालिकाहरू जोड्नका लागि एक सम्बन्ध बनाउनुहोस्',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'डिपेन्डेन्सीहरू',
|
|
||||||
filter: 'फिल्टर',
|
|
||||||
collapse: 'सबै लुकाउनुहोस्',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'डिपेन्डेन्सी',
|
||||||
table: 'तालिका',
|
table: 'तालिका',
|
||||||
dependent_table: 'विचलित तालिका',
|
dependent_table: 'विचलित तालिका',
|
||||||
delete_dependency: 'हटाउनुहोस्',
|
delete_dependency: 'हटाउनुहोस्',
|
||||||
@@ -214,9 +208,8 @@ export const ne: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'कुनै डिपेन्डेन्सीहरू छैनन्',
|
title: 'कुनै सम्बन्धहरू छैनन्',
|
||||||
description:
|
description: 'सुरु गर्नका लागि एक सम्बन्ध बनाउनुहोस्',
|
||||||
'डिपेन्डेन्सीहरू देखाउनका लागि एक व्यू बनाउनुहोस्',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -240,6 +233,35 @@ export const ne: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visuals',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'टिप्पणीहरू',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'फिल्टर',
|
||||||
|
add_note: 'टिप्पणी थप्नुहोस्',
|
||||||
|
no_results: 'कुनै टिप्पणी फेला परेन',
|
||||||
|
clear: 'फिल्टर खाली गर्नुहोस्',
|
||||||
|
empty_state: {
|
||||||
|
title: 'कुनै टिप्पणी छैन',
|
||||||
|
description:
|
||||||
|
'क्यानभासमा पाठ टिप्पणी थप्न टिप्पणी सिर्जना गर्नुहोस्',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'खाली टिप्पणी',
|
||||||
|
note_actions: {
|
||||||
|
title: 'टिप्पणी कार्यहरू',
|
||||||
|
edit_content: 'सामग्री सम्पादन गर्नुहोस्',
|
||||||
|
delete_note: 'टिप्पणी मेटाउनुहोस्',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -256,6 +278,7 @@ export const ne: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'कुनै enum मानहरू परिभाषित छैनन्',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -278,7 +301,7 @@ export const ne: LanguageTranslation = {
|
|||||||
show_all: 'सबै देखाउनुहोस्',
|
show_all: 'सबै देखाउनुहोस्',
|
||||||
undo: 'पूर्ववत',
|
undo: 'पूर्ववत',
|
||||||
redo: 'पुनः गर्नुहोस्',
|
redo: 'पुनः गर्नुहोस्',
|
||||||
reorder_diagram: 'पुनः क्रमबद्ध गर्नुहोस्',
|
reorder_diagram: 'डायाग्राम स्वचालित मिलाउनुहोस्',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -317,13 +340,13 @@ export const ne: LanguageTranslation = {
|
|||||||
cancel: 'रद्द गर्नुहोस्',
|
cancel: 'रद्द गर्नुहोस्',
|
||||||
import_from_file: 'फाइलबाट आयात गर्नुहोस्',
|
import_from_file: 'फाइलबाट आयात गर्नुहोस्',
|
||||||
back: 'फर्क',
|
back: 'फर्क',
|
||||||
empty_diagram: 'रिक्त डायाग्राम',
|
empty_diagram: 'खाली डाटाबेस',
|
||||||
continue: 'जारी राख्नुहोस्',
|
continue: 'जारी राख्नुहोस्',
|
||||||
import: 'आयात गर्नुहोस्',
|
import: 'आयात गर्नुहोस्',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'डायाग्राम खोल्नुहोस्',
|
title: 'डाटाबेस खोल्नुहोस्',
|
||||||
description:
|
description:
|
||||||
'तलको सूचीबाट खोल्नका लागि एक डायाग्राम चयन गर्नुहोस्।',
|
'तलको सूचीबाट खोल्नका लागि एक डायाग्राम चयन गर्नुहोस्।',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -334,6 +357,12 @@ export const ne: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'रद्द गर्नुहोस्',
|
cancel: 'रद्द गर्नुहोस्',
|
||||||
open: 'खोल्नुहोस्',
|
open: 'खोल्नुहोस्',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'खोल्नुहोस्',
|
||||||
|
duplicate: 'डुप्लिकेट',
|
||||||
|
delete: 'मेटाउनुहोस्',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -483,9 +512,11 @@ export const ne: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'नयाँ तालिका',
|
new_table: 'नयाँ तालिका',
|
||||||
|
new_view: 'नयाँ भ्यू',
|
||||||
new_relationship: 'नयाँ सम्बन्ध',
|
new_relationship: 'नयाँ सम्बन्ध',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'नयाँ नोट',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -504,6 +535,9 @@ export const ne: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'भाषा परिवर्तन गर्नुहोस्',
|
change_language: 'भाषा परिवर्तन गर्नुहोस्',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'सक्रिय',
|
||||||
|
off: 'निष्क्रिय',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const pt_BR: LanguageTranslation = {
|
export const pt_BR: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Novo',
|
||||||
|
browse: 'Navegar',
|
||||||
|
tables: 'Tabelas',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'Dependências',
|
||||||
|
custom_types: 'Tipos Personalizados',
|
||||||
|
visuals: 'Visuais',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Arquivo',
|
actions: 'Ações',
|
||||||
new: 'Novo',
|
new: 'Novo...',
|
||||||
open: 'Abrir',
|
browse: 'Navegar...',
|
||||||
save: 'Salvar',
|
save: 'Salvar',
|
||||||
import: 'Importar Banco de Dados',
|
import: 'Importar Banco de Dados',
|
||||||
export_sql: 'Exportar SQL',
|
export_sql: 'Exportar SQL',
|
||||||
export_as: 'Exportar como',
|
export_as: 'Exportar como',
|
||||||
delete_diagram: 'Excluir Diagrama',
|
delete_diagram: 'Excluir',
|
||||||
exit: 'Sair',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Editar',
|
edit: 'Editar',
|
||||||
@@ -29,6 +37,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
hide_field_attributes: 'Ocultar Atributos de Campo',
|
hide_field_attributes: 'Ocultar Atributos de Campo',
|
||||||
show_field_attributes: 'Mostrar Atributos de Campo',
|
show_field_attributes: 'Mostrar Atributos de Campo',
|
||||||
zoom_on_scroll: 'Zoom ao Rolar',
|
zoom_on_scroll: 'Zoom ao Rolar',
|
||||||
|
show_views: 'Visualizações do Banco de Dados',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Mostrar Dependências',
|
show_dependencies: 'Mostrar Dependências',
|
||||||
hide_dependencies: 'Ocultar Dependências',
|
hide_dependencies: 'Ocultar Dependências',
|
||||||
@@ -66,22 +75,13 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Reordenar Diagrama',
|
title: 'Organizar Diagrama Automaticamente',
|
||||||
description:
|
description:
|
||||||
'Esta ação reorganizará todas as tabelas no diagrama. Deseja continuar?',
|
'Esta ação reorganizará todas as tabelas no diagrama. Deseja continuar?',
|
||||||
reorder: 'Reordenar',
|
reorder: 'Organizar Automaticamente',
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Múltiplos Esquemas',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} esquemas neste diagrama. Atualmente exibindo: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'nenhum',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Falha na cópia',
|
title: 'Falha na cópia',
|
||||||
@@ -116,14 +116,11 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
copied: 'Copiado!',
|
copied: 'Copiado!',
|
||||||
|
|
||||||
side_panel: {
|
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...',
|
view_all_options: 'Ver todas as Opções...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tabelas',
|
tables: 'Tabelas',
|
||||||
add_table: 'Adicionar Tabela',
|
add_table: 'Adicionar Tabela',
|
||||||
|
add_view: 'Adicionar Visualização',
|
||||||
filter: 'Filtrar',
|
filter: 'Filtrar',
|
||||||
collapse: 'Colapsar Todas',
|
collapse: 'Colapsar Todas',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Atributos do Campo',
|
title: 'Atributos do Campo',
|
||||||
unique: 'Único',
|
unique: 'Único',
|
||||||
|
auto_increment: 'Incremento Automático',
|
||||||
comments: 'Comentários',
|
comments: 'Comentários',
|
||||||
no_comments: 'Sem comentários',
|
no_comments: 'Sem comentários',
|
||||||
delete_field: 'Excluir Campo',
|
delete_field: 'Excluir Campo',
|
||||||
@@ -164,6 +162,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
title: 'Atributos do Índice',
|
title: 'Atributos do Índice',
|
||||||
name: 'Nome',
|
name: 'Nome',
|
||||||
unique: 'Único',
|
unique: 'Único',
|
||||||
|
index_type: 'Tipo de Índice',
|
||||||
delete_index: 'Excluir Índice',
|
delete_index: 'Excluir Índice',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,12 +179,15 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
description: 'Crie uma tabela para começar',
|
description: 'Crie uma tabela para começar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Relacionamentos',
|
refs: 'Refs',
|
||||||
filter: 'Filtrar',
|
filter: 'Filtrar',
|
||||||
add_relationship: 'Adicionar Relacionamento',
|
|
||||||
collapse: 'Colapsar Todas',
|
collapse: 'Colapsar Todas',
|
||||||
|
add_relationship: 'Adicionar Relacionamento',
|
||||||
|
relationships: 'Relacionamentos',
|
||||||
|
dependencies: 'Dependências',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Relacionamento',
|
||||||
primary: 'Tabela Primária',
|
primary: 'Tabela Primária',
|
||||||
foreign: 'Tabela Referenciada',
|
foreign: 'Tabela Referenciada',
|
||||||
cardinality: 'Cardinalidade',
|
cardinality: 'Cardinalidade',
|
||||||
@@ -195,16 +197,8 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
delete_relationship: 'Excluir',
|
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: {
|
||||||
|
dependency: 'Dependência',
|
||||||
table: 'Tabela',
|
table: 'Tabela',
|
||||||
dependent_table: 'Visualização Dependente',
|
dependent_table: 'Visualização Dependente',
|
||||||
delete_dependency: 'Excluir',
|
delete_dependency: 'Excluir',
|
||||||
@@ -214,8 +208,8 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Sem dependências',
|
title: 'Sem relacionamentos',
|
||||||
description: 'Crie uma visualização para começar',
|
description: 'Crie um relacionamento para começar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -239,6 +233,35 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visuais',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'Notas',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Filtrar',
|
||||||
|
add_note: 'Adicionar Nota',
|
||||||
|
no_results: 'Nenhuma nota encontrada',
|
||||||
|
clear: 'Limpar Filtro',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Sem Notas',
|
||||||
|
description:
|
||||||
|
'Crie uma nota para adicionar anotações de texto na tela',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Nota vazia',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Ações de Nota',
|
||||||
|
edit_content: 'Editar Conteúdo',
|
||||||
|
delete_note: 'Excluir Nota',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -255,6 +278,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Nenhum valor de enum definido',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -277,7 +301,7 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
show_all: 'Mostrar Tudo',
|
show_all: 'Mostrar Tudo',
|
||||||
undo: 'Desfazer',
|
undo: 'Desfazer',
|
||||||
redo: 'Refazer',
|
redo: 'Refazer',
|
||||||
reorder_diagram: 'Reordenar Diagrama',
|
reorder_diagram: 'Organizar Diagrama Automaticamente',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -316,13 +340,13 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
back: 'Voltar',
|
back: 'Voltar',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
empty_diagram: 'Diagrama vazio',
|
empty_diagram: 'Banco de dados vazio',
|
||||||
continue: 'Continuar',
|
continue: 'Continuar',
|
||||||
import: 'Importar',
|
import: 'Importar',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Abrir Diagrama',
|
title: 'Abrir Banco de Dados',
|
||||||
description: 'Selecione um diagrama para abrir da lista abaixo.',
|
description: 'Selecione um diagrama para abrir da lista abaixo.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Nome',
|
name: 'Nome',
|
||||||
@@ -332,6 +356,12 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
open: 'Abrir',
|
open: 'Abrir',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Abrir',
|
||||||
|
duplicate: 'Duplicar',
|
||||||
|
delete: 'Excluir',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -481,9 +511,11 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Nova Tabela',
|
new_table: 'Nova Tabela',
|
||||||
|
new_view: 'Nova Visualização',
|
||||||
new_relationship: 'Novo Relacionamento',
|
new_relationship: 'Novo Relacionamento',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'Nova Nota',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -503,6 +535,9 @@ export const pt_BR: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Idioma',
|
change_language: 'Idioma',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Ligado',
|
||||||
|
off: 'Desligado',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const ru: LanguageTranslation = {
|
export const ru: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Новая',
|
||||||
|
browse: 'Обзор',
|
||||||
|
tables: 'Таблицы',
|
||||||
|
refs: 'Ссылки',
|
||||||
|
dependencies: 'Зависимости',
|
||||||
|
custom_types: 'Пользовательские типы',
|
||||||
|
visuals: 'Визуальные элементы',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Файл',
|
actions: 'Действия',
|
||||||
new: 'Создать',
|
new: 'Новая...',
|
||||||
open: 'Открыть',
|
browse: 'Обзор...',
|
||||||
save: 'Сохранить',
|
save: 'Сохранить',
|
||||||
import: 'Импортировать базу данных',
|
import: 'Импортировать базу данных',
|
||||||
export_sql: 'Экспорт SQL',
|
export_sql: 'Экспорт SQL',
|
||||||
export_as: 'Экспортировать как',
|
export_as: 'Экспортировать как',
|
||||||
delete_diagram: 'Удалить диаграмму',
|
delete_diagram: 'Удалить',
|
||||||
exit: 'Выход',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Изменение',
|
edit: 'Изменение',
|
||||||
@@ -29,6 +37,7 @@ export const ru: LanguageTranslation = {
|
|||||||
show_field_attributes: 'Показать атрибуты поля',
|
show_field_attributes: 'Показать атрибуты поля',
|
||||||
hide_field_attributes: 'Скрыть атрибуты поля',
|
hide_field_attributes: 'Скрыть атрибуты поля',
|
||||||
zoom_on_scroll: 'Увеличение при прокрутке',
|
zoom_on_scroll: 'Увеличение при прокрутке',
|
||||||
|
show_views: 'Представления базы данных',
|
||||||
theme: 'Тема',
|
theme: 'Тема',
|
||||||
show_dependencies: 'Показать зависимости',
|
show_dependencies: 'Показать зависимости',
|
||||||
hide_dependencies: 'Скрыть зависимости',
|
hide_dependencies: 'Скрыть зависимости',
|
||||||
@@ -64,22 +73,13 @@ export const ru: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Переупорядочить диаграмму',
|
title: 'Автоматическая расстановка диаграммы',
|
||||||
description:
|
description:
|
||||||
'Это действие переставит все таблицы на диаграмме. Хотите продолжить?',
|
'Это действие переставит все таблицы на диаграмме. Хотите продолжить?',
|
||||||
reorder: 'Изменить порядок',
|
reorder: 'Автоматическая расстановка',
|
||||||
cancel: 'Отменить',
|
cancel: 'Отменить',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Множественные схемы',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} схем в этой диаграмме. В данный момент отображается: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'никто',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Ошибка копирования',
|
title: 'Ошибка копирования',
|
||||||
@@ -113,14 +113,11 @@ export const ru: LanguageTranslation = {
|
|||||||
show_less: 'Показать меньше',
|
show_less: 'Показать меньше',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Схема:',
|
|
||||||
filter_by_schema: 'Фильтр по схеме',
|
|
||||||
search_schema: 'Схема поиска...',
|
|
||||||
no_schemas_found: 'Схемы не найдены.',
|
|
||||||
view_all_options: 'Просмотреть все варианты...',
|
view_all_options: 'Просмотреть все варианты...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Таблицы',
|
tables: 'Таблицы',
|
||||||
add_table: 'Добавить таблицу',
|
add_table: 'Добавить таблицу',
|
||||||
|
add_view: 'Добавить представление',
|
||||||
filter: 'Фильтр',
|
filter: 'Фильтр',
|
||||||
collapse: 'Свернуть все',
|
collapse: 'Свернуть все',
|
||||||
clear: 'Очистить фильтр',
|
clear: 'Очистить фильтр',
|
||||||
@@ -146,6 +143,7 @@ export const ru: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Атрибуты поля',
|
title: 'Атрибуты поля',
|
||||||
unique: 'Уникальный',
|
unique: 'Уникальный',
|
||||||
|
auto_increment: 'Автоинкремент',
|
||||||
comments: 'Комментарии',
|
comments: 'Комментарии',
|
||||||
no_comments: 'Нет комментария',
|
no_comments: 'Нет комментария',
|
||||||
delete_field: 'Удалить поле',
|
delete_field: 'Удалить поле',
|
||||||
@@ -160,6 +158,7 @@ export const ru: LanguageTranslation = {
|
|||||||
title: 'Атрибуты индекса',
|
title: 'Атрибуты индекса',
|
||||||
name: 'Имя',
|
name: 'Имя',
|
||||||
unique: 'Уникальный',
|
unique: 'Уникальный',
|
||||||
|
index_type: 'Тип индекса',
|
||||||
delete_index: 'Удалить индекс',
|
delete_index: 'Удалить индекс',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -176,12 +175,15 @@ export const ru: LanguageTranslation = {
|
|||||||
description: 'Создайте таблицу, чтобы начать',
|
description: 'Создайте таблицу, чтобы начать',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Отношения',
|
refs: 'Ссылки',
|
||||||
filter: 'Фильтр',
|
filter: 'Фильтр',
|
||||||
add_relationship: 'Добавить отношение',
|
|
||||||
collapse: 'Свернуть все',
|
collapse: 'Свернуть все',
|
||||||
|
add_relationship: 'Добавить отношение',
|
||||||
|
relationships: 'Отношения',
|
||||||
|
dependencies: 'Зависимости',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Отношение',
|
||||||
primary: 'Основная таблица',
|
primary: 'Основная таблица',
|
||||||
foreign: 'Справочная таблица',
|
foreign: 'Справочная таблица',
|
||||||
cardinality: 'Тип множественной связи',
|
cardinality: 'Тип множественной связи',
|
||||||
@@ -191,18 +193,10 @@ export const ru: LanguageTranslation = {
|
|||||||
delete_relationship: 'Удалить',
|
delete_relationship: 'Удалить',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Нет отношений',
|
|
||||||
description: 'Создайте связь для соединения таблиц',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Зависимости',
|
|
||||||
filter: 'Фильтр',
|
|
||||||
collapse: 'Свернуть все',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
table: 'Стол',
|
dependency: 'Зависимость',
|
||||||
dependent_table: 'Зависимый вид',
|
table: 'Таблица',
|
||||||
|
dependent_table: 'Зависимое представление',
|
||||||
delete_dependency: 'Удалить',
|
delete_dependency: 'Удалить',
|
||||||
dependency_actions: {
|
dependency_actions: {
|
||||||
title: 'Действия',
|
title: 'Действия',
|
||||||
@@ -210,8 +204,8 @@ export const ru: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Нет зависимостей',
|
title: 'Нет отношений',
|
||||||
description: 'Создайте представление, чтобы начать',
|
description: 'Создайте отношение, чтобы начать',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -236,6 +230,35 @@ export const ru: LanguageTranslation = {
|
|||||||
description: 'Создайте область, чтобы начать',
|
description: 'Создайте область, чтобы начать',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Визуальные элементы',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Области',
|
||||||
|
notes: 'Заметки',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Фильтр',
|
||||||
|
add_note: 'Добавить Заметку',
|
||||||
|
no_results: 'Заметки не найдены',
|
||||||
|
clear: 'Очистить Фильтр',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Нет Заметок',
|
||||||
|
description:
|
||||||
|
'Создайте заметку, чтобы добавить текстовые аннотации на холсте',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Пустая заметка',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Действия с Заметкой',
|
||||||
|
edit_content: 'Редактировать Содержимое',
|
||||||
|
delete_note: 'Удалить Заметку',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -252,6 +275,7 @@ export const ru: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Значения перечисления не определены',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -274,7 +298,7 @@ export const ru: LanguageTranslation = {
|
|||||||
show_all: 'Показать все',
|
show_all: 'Показать все',
|
||||||
undo: 'Отменить',
|
undo: 'Отменить',
|
||||||
redo: 'Вернуть',
|
redo: 'Вернуть',
|
||||||
reorder_diagram: 'Переупорядочить диаграмму',
|
reorder_diagram: 'Автоматическая расстановка диаграммы',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -312,13 +336,13 @@ export const ru: LanguageTranslation = {
|
|||||||
cancel: 'Отменить',
|
cancel: 'Отменить',
|
||||||
back: 'Назад',
|
back: 'Назад',
|
||||||
import_from_file: 'Импортировать из файла',
|
import_from_file: 'Импортировать из файла',
|
||||||
empty_diagram: 'Пустая диаграмма',
|
empty_diagram: 'Пустая база данных',
|
||||||
continue: 'Продолжить',
|
continue: 'Продолжить',
|
||||||
import: 'Импорт',
|
import: 'Импорт',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Открыть диаграмму',
|
title: 'Открыть базу данных',
|
||||||
description:
|
description:
|
||||||
'Выберите диаграмму, которую нужно открыть, из списка ниже.',
|
'Выберите диаграмму, которую нужно открыть, из списка ниже.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -329,6 +353,12 @@ export const ru: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Отмена',
|
cancel: 'Отмена',
|
||||||
open: 'Открыть',
|
open: 'Открыть',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Открыть',
|
||||||
|
duplicate: 'Дублировать',
|
||||||
|
delete: 'Удалить',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -477,8 +507,10 @@ export const ru: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Создать таблицу',
|
new_table: 'Создать таблицу',
|
||||||
|
new_view: 'Новое представление',
|
||||||
new_relationship: 'Создать отношение',
|
new_relationship: 'Создать отношение',
|
||||||
new_area: 'Новая область',
|
new_area: 'Новая область',
|
||||||
|
new_note: 'Новая Заметка',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -498,6 +530,9 @@ export const ru: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Сменить язык',
|
change_language: 'Сменить язык',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Вкл',
|
||||||
|
off: 'Выкл',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const te: LanguageTranslation = {
|
export const te: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'కొత్తది',
|
||||||
|
browse: 'బ్రాఉజ్',
|
||||||
|
tables: 'టేబల్లు',
|
||||||
|
refs: 'సంబంధాలు',
|
||||||
|
dependencies: 'ఆధారతలు',
|
||||||
|
custom_types: 'కస్టమ్ టైప్స్',
|
||||||
|
visuals: 'Visuals',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'ఫైల్',
|
actions: 'చర్యలు',
|
||||||
new: 'కొత్తది',
|
new: 'కొత్తది...',
|
||||||
open: 'తెరవు',
|
browse: 'బ్రాఉజ్ చేయండి...',
|
||||||
save: 'సేవ్',
|
save: 'సేవ్',
|
||||||
import: 'డేటాబేస్ను దిగుమతి చేసుకోండి',
|
import: 'డేటాబేస్ను దిగుమతి చేసుకోండి',
|
||||||
export_sql: 'SQL ఎగుమతి',
|
export_sql: 'SQL ఎగుమతి',
|
||||||
export_as: 'వగా ఎగుమతి చేయండి',
|
export_as: 'వగా ఎగుమతి చేయండి',
|
||||||
delete_diagram: 'చిత్రాన్ని తొలగించండి',
|
delete_diagram: 'తొలగించండి',
|
||||||
exit: 'నిష్క్రమించు',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'సవరించు',
|
edit: 'సవరించు',
|
||||||
@@ -29,6 +37,7 @@ export const te: LanguageTranslation = {
|
|||||||
show_field_attributes: 'ఫీల్డ్ గుణాలను చూపించు',
|
show_field_attributes: 'ఫీల్డ్ గుణాలను చూపించు',
|
||||||
hide_field_attributes: 'ఫీల్డ్ గుణాలను దాచండి',
|
hide_field_attributes: 'ఫీల్డ్ గుణాలను దాచండి',
|
||||||
zoom_on_scroll: 'స్క్రోల్పై జూమ్',
|
zoom_on_scroll: 'స్క్రోల్పై జూమ్',
|
||||||
|
show_views: 'డేటాబేస్ వ్యూలు',
|
||||||
theme: 'థీమ్',
|
theme: 'థీమ్',
|
||||||
show_dependencies: 'ఆధారాలు చూపించండి',
|
show_dependencies: 'ఆధారాలు చూపించండి',
|
||||||
hide_dependencies: 'ఆధారాలను దాచండి',
|
hide_dependencies: 'ఆధారాలను దాచండి',
|
||||||
@@ -66,22 +75,13 @@ export const te: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'చిత్రాన్ని పునఃసరిచేయండి',
|
title: 'చిత్రాన్ని స్వయంచాలకంగా అమర్చండి',
|
||||||
description:
|
description:
|
||||||
'ఈ చర్య చిత్రంలోని అన్ని పట్టికలను పునఃస్థాపిస్తుంది. మీరు కొనసాగించాలనుకుంటున్నారా?',
|
'ఈ చర్య చిత్రంలోని అన్ని పట్టికలను పునఃస్థాపిస్తుంది. మీరు కొనసాగించాలనుకుంటున్నారా?',
|
||||||
reorder: 'పునఃసరిచేయండి',
|
reorder: 'స్వయంచాలకంగా అమర్చండి',
|
||||||
cancel: 'రద్దు',
|
cancel: 'రద్దు',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'బహుళ స్కీమాలు',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} స్కీమాలు ఈ చిత్రంలో ఉన్నాయి. ప్రస్తుత స్కీమాలు: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'ఎదరికాదు',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'కాపీ విఫలమైంది',
|
title: 'కాపీ విఫలమైంది',
|
||||||
@@ -116,14 +116,11 @@ export const te: LanguageTranslation = {
|
|||||||
copied: 'కాపీ చేయబడింది!',
|
copied: 'కాపీ చేయబడింది!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'స్కీమా:',
|
|
||||||
filter_by_schema: 'స్కీమా ద్వారా ఫిల్టర్ చేయండి',
|
|
||||||
search_schema: 'స్కీమా కోసం శోధించండి...',
|
|
||||||
no_schemas_found: 'ఏ స్కీమాలు కూడా కనుగొనబడలేదు.',
|
|
||||||
view_all_options: 'అన్ని ఎంపికలను చూడండి...',
|
view_all_options: 'అన్ని ఎంపికలను చూడండి...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'పట్టికలు',
|
tables: 'పట్టికలు',
|
||||||
add_table: 'పట్టికను జోడించు',
|
add_table: 'పట్టికను జోడించు',
|
||||||
|
add_view: 'వ్యూ జోడించండి',
|
||||||
filter: 'ఫిల్టర్',
|
filter: 'ఫిల్టర్',
|
||||||
collapse: 'అన్ని కూల్ చేయి',
|
collapse: 'అన్ని కూల్ చేయి',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -149,6 +146,7 @@ export const te: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'ఫీల్డ్ గుణాలు',
|
title: 'ఫీల్డ్ గుణాలు',
|
||||||
unique: 'అద్వితీయ',
|
unique: 'అద్వితీయ',
|
||||||
|
auto_increment: 'ఆటో ఇంక్రిమెంట్',
|
||||||
comments: 'వ్యాఖ్యలు',
|
comments: 'వ్యాఖ్యలు',
|
||||||
no_comments: 'వ్యాఖ్యలు లేవు',
|
no_comments: 'వ్యాఖ్యలు లేవు',
|
||||||
delete_field: 'ఫీల్డ్ తొలగించు',
|
delete_field: 'ఫీల్డ్ తొలగించు',
|
||||||
@@ -164,6 +162,7 @@ export const te: LanguageTranslation = {
|
|||||||
title: 'ఇండెక్స్ గుణాలు',
|
title: 'ఇండెక్స్ గుణాలు',
|
||||||
name: 'పేరు',
|
name: 'పేరు',
|
||||||
unique: 'అద్వితీయ',
|
unique: 'అద్వితీయ',
|
||||||
|
index_type: 'ఇండెక్స్ రకం',
|
||||||
delete_index: 'ఇండెక్స్ తొలగించు',
|
delete_index: 'ఇండెక్స్ తొలగించు',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -181,12 +180,15 @@ export const te: LanguageTranslation = {
|
|||||||
description: 'ప్రారంభించడానికి ఒక పట్టిక సృష్టించండి',
|
description: 'ప్రారంభించడానికి ఒక పట్టిక సృష్టించండి',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'సంబంధాలు',
|
refs: 'Refs',
|
||||||
filter: 'ఫిల్టర్',
|
filter: 'ఫిల్టర్',
|
||||||
add_relationship: 'సంబంధం జోడించు',
|
|
||||||
collapse: 'అన్ని కూల్ చేయి',
|
collapse: 'అన్ని కూల్ చేయి',
|
||||||
|
add_relationship: 'సంబంధం జోడించు',
|
||||||
|
relationships: 'సంబంధాలు',
|
||||||
|
dependencies: 'ఆధారాలు',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'సంబంధం',
|
||||||
primary: 'ప్రాథమిక పట్టిక',
|
primary: 'ప్రాథమిక పట్టిక',
|
||||||
foreign: 'సూచించబడిన పట్టిక',
|
foreign: 'సూచించబడిన పట్టిక',
|
||||||
cardinality: 'కార్డినాలిటీ',
|
cardinality: 'కార్డినాలిటీ',
|
||||||
@@ -196,16 +198,8 @@ export const te: LanguageTranslation = {
|
|||||||
delete_relationship: 'సంబంధం తొలగించు',
|
delete_relationship: 'సంబంధం తొలగించు',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'సంబంధాలు లేవు',
|
|
||||||
description: 'పట్టికలను అనుసంధించడానికి సంబంధం సృష్టించండి',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'ఆధారాలు',
|
|
||||||
filter: 'ఫిల్టర్',
|
|
||||||
collapse: 'అన్ని కూల్ చేయి',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'ఆధారం',
|
||||||
table: 'పట్టిక',
|
table: 'పట్టిక',
|
||||||
dependent_table: 'ఆధారిత వీక్షణ',
|
dependent_table: 'ఆధారిత వీక్షణ',
|
||||||
delete_dependency: 'ఆధారాన్ని తొలగించు',
|
delete_dependency: 'ఆధారాన్ని తొలగించు',
|
||||||
@@ -215,8 +209,8 @@ export const te: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'ఆధారాలు లేవు',
|
title: 'సంబంధాలు లేవు',
|
||||||
description: 'ప్రారంభించడానికి ఒక వీక్షణ సృష్టించండి',
|
description: 'ప్రారంభించడానికి ఒక సంబంధం సృష్టించండి',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -240,6 +234,35 @@ export const te: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Visuals',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'గమనికలు',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'ఫిల్టర్',
|
||||||
|
add_note: 'గమనిక జోడించండి',
|
||||||
|
no_results: 'గమనికలు కనుగొనబడలేదు',
|
||||||
|
clear: 'ఫిల్టర్ను క్లియర్ చేయండి',
|
||||||
|
empty_state: {
|
||||||
|
title: 'గమనికలు లేవు',
|
||||||
|
description:
|
||||||
|
'కాన్వాస్పై టెక్స్ట్ ఉల్లేఖనలను జోడించడానికి ఒక గమనికను సృష్టించండి',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'ఖాళీ గమనిక',
|
||||||
|
note_actions: {
|
||||||
|
title: 'గమనిక చర్యలు',
|
||||||
|
edit_content: 'కంటెంట్ను సవరించండి',
|
||||||
|
delete_note: 'గమనికను తొలగించండి',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -256,6 +279,7 @@ export const te: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'ఏ enum విలువలు నిర్వచించబడలేదు',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -278,7 +302,7 @@ export const te: LanguageTranslation = {
|
|||||||
show_all: 'అన్ని చూపించు',
|
show_all: 'అన్ని చూపించు',
|
||||||
undo: 'తిరిగి చేయు',
|
undo: 'తిరిగి చేయు',
|
||||||
redo: 'మరలా చేయు',
|
redo: 'మరలా చేయు',
|
||||||
reorder_diagram: 'చిత్రాన్ని పునఃసరిచేయండి',
|
reorder_diagram: 'చిత్రాన్ని స్వయంచాలకంగా అమర్చండి',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -317,13 +341,13 @@ export const te: LanguageTranslation = {
|
|||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
back: 'తిరుగు',
|
back: 'తిరుగు',
|
||||||
empty_diagram: 'ఖాళీ చిత్రము',
|
empty_diagram: 'ఖాళీ డేటాబేస్',
|
||||||
continue: 'కొనసాగించు',
|
continue: 'కొనసాగించు',
|
||||||
import: 'డిగుమతి',
|
import: 'డిగుమతి',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'చిత్రం తెరవండి',
|
title: 'డేటాబేస్ తెరవండి',
|
||||||
description: 'కింద ఉన్న జాబితా నుండి చిత్రాన్ని ఎంచుకోండి.',
|
description: 'కింద ఉన్న జాబితా నుండి చిత్రాన్ని ఎంచుకోండి.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'పేరు',
|
name: 'పేరు',
|
||||||
@@ -333,6 +357,12 @@ export const te: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'రద్దు',
|
cancel: 'రద్దు',
|
||||||
open: 'తెరవు',
|
open: 'తెరవు',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'తెరవు',
|
||||||
|
duplicate: 'నకలు',
|
||||||
|
delete: 'తొలగించు',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -485,9 +515,11 @@ export const te: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'కొత్త పట్టిక',
|
new_table: 'కొత్త పట్టిక',
|
||||||
|
new_view: 'కొత్త వ్యూ',
|
||||||
new_relationship: 'కొత్త సంబంధం',
|
new_relationship: 'కొత్త సంబంధం',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'కొత్త నోట్',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -508,6 +540,9 @@ export const te: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'భాష మార్చు',
|
change_language: 'భాష మార్చు',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'ఆన్',
|
||||||
|
off: 'ఆఫ్',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const tr: LanguageTranslation = {
|
export const tr: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Yeni',
|
||||||
|
browse: 'Gözat',
|
||||||
|
tables: 'Tablolar',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'Bağımlılıklar',
|
||||||
|
custom_types: 'Özel Tipler',
|
||||||
|
visuals: 'Görseller',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Dosya',
|
actions: 'Eylemler',
|
||||||
new: 'Yeni',
|
new: 'Yeni...',
|
||||||
open: 'Aç',
|
browse: 'Gözat...',
|
||||||
save: 'Kaydet',
|
save: 'Kaydet',
|
||||||
import: 'Veritabanı İçe Aktar',
|
import: 'Veritabanı İçe Aktar',
|
||||||
export_sql: 'SQL Olarak Dışa Aktar',
|
export_sql: 'SQL Olarak Dışa Aktar',
|
||||||
export_as: 'Olarak Dışa Aktar',
|
export_as: 'Olarak Dışa Aktar',
|
||||||
delete_diagram: 'Diyagramı Sil',
|
delete_diagram: 'Sil',
|
||||||
exit: 'Çıkış',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Düzenle',
|
edit: 'Düzenle',
|
||||||
@@ -29,6 +37,7 @@ export const tr: LanguageTranslation = {
|
|||||||
show_field_attributes: 'Alan Özelliklerini Göster',
|
show_field_attributes: 'Alan Özelliklerini Göster',
|
||||||
hide_field_attributes: 'Alan Özelliklerini Gizle',
|
hide_field_attributes: 'Alan Özelliklerini Gizle',
|
||||||
zoom_on_scroll: 'Kaydırarak Yakınlaştır',
|
zoom_on_scroll: 'Kaydırarak Yakınlaştır',
|
||||||
|
show_views: 'Veritabanı Görünümleri',
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
show_dependencies: 'Bağımlılıkları Göster',
|
show_dependencies: 'Bağımlılıkları Göster',
|
||||||
hide_dependencies: 'Bağımlılıkları Gizle',
|
hide_dependencies: 'Bağımlılıkları Gizle',
|
||||||
@@ -66,22 +75,13 @@ export const tr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Diyagramı Yeniden Sırala',
|
title: 'Diyagramı Otomatik Düzenle',
|
||||||
description:
|
description:
|
||||||
'Bu işlem tüm tabloları yeniden düzenleyecektir. Devam etmek istiyor musunuz?',
|
'Bu işlem tüm tabloları yeniden düzenleyecektir. Devam etmek istiyor musunuz?',
|
||||||
reorder: 'Yeniden Sırala',
|
reorder: 'Otomatik Düzenle',
|
||||||
cancel: 'İptal',
|
cancel: 'İptal',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Birden Fazla Şema',
|
|
||||||
description:
|
|
||||||
'Bu diyagramda {{schemasCount}} şema var. Şu anda görüntülenen: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'yok',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Kopyalama başarısız',
|
title: 'Kopyalama başarısız',
|
||||||
@@ -115,14 +115,11 @@ export const tr: LanguageTranslation = {
|
|||||||
copy_to_clipboard: 'Panoya Kopyala',
|
copy_to_clipboard: 'Panoya Kopyala',
|
||||||
copied: 'Kopyalandı!',
|
copied: 'Kopyalandı!',
|
||||||
side_panel: {
|
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...',
|
view_all_options: 'Tüm Seçenekleri Gör...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Tablolar',
|
tables: 'Tablolar',
|
||||||
add_table: 'Tablo Ekle',
|
add_table: 'Tablo Ekle',
|
||||||
|
add_view: 'Görünüm Ekle',
|
||||||
filter: 'Filtrele',
|
filter: 'Filtrele',
|
||||||
collapse: 'Hepsini Daralt',
|
collapse: 'Hepsini Daralt',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const tr: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Alan Özellikleri',
|
title: 'Alan Özellikleri',
|
||||||
unique: 'Tekil',
|
unique: 'Tekil',
|
||||||
|
auto_increment: 'Otomatik Artış',
|
||||||
comments: 'Yorumlar',
|
comments: 'Yorumlar',
|
||||||
no_comments: 'Yorum yok',
|
no_comments: 'Yorum yok',
|
||||||
delete_field: 'Alanı Sil',
|
delete_field: 'Alanı Sil',
|
||||||
@@ -163,6 +161,7 @@ export const tr: LanguageTranslation = {
|
|||||||
title: 'İndeks Özellikleri',
|
title: 'İndeks Özellikleri',
|
||||||
name: 'Ad',
|
name: 'Ad',
|
||||||
unique: 'Tekil',
|
unique: 'Tekil',
|
||||||
|
index_type: 'İndeks Türü',
|
||||||
delete_index: 'İndeksi Sil',
|
delete_index: 'İndeksi Sil',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -180,12 +179,15 @@ export const tr: LanguageTranslation = {
|
|||||||
description: 'Başlamak için bir tablo oluşturun',
|
description: 'Başlamak için bir tablo oluşturun',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'İlişkiler',
|
refs: 'Refs',
|
||||||
filter: 'Filtrele',
|
filter: 'Filtrele',
|
||||||
add_relationship: 'İlişki Ekle',
|
|
||||||
collapse: 'Hepsini Daralt',
|
collapse: 'Hepsini Daralt',
|
||||||
|
add_relationship: 'İlişki Ekle',
|
||||||
|
relationships: 'İlişkiler',
|
||||||
|
dependencies: 'Bağımlılıklar',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'İlişki',
|
||||||
primary: 'Birincil Tablo',
|
primary: 'Birincil Tablo',
|
||||||
foreign: 'Referans Tablo',
|
foreign: 'Referans Tablo',
|
||||||
cardinality: 'Kardinalite',
|
cardinality: 'Kardinalite',
|
||||||
@@ -195,16 +197,8 @@ export const tr: LanguageTranslation = {
|
|||||||
delete_relationship: 'Sil',
|
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: {
|
||||||
|
dependency: 'Bağımlılık',
|
||||||
table: 'Tablo',
|
table: 'Tablo',
|
||||||
dependent_table: 'Bağımlı Görünüm',
|
dependent_table: 'Bağımlı Görünüm',
|
||||||
delete_dependency: 'Sil',
|
delete_dependency: 'Sil',
|
||||||
@@ -214,8 +208,8 @@ export const tr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Bağımlılık yok',
|
title: 'İlişki yok',
|
||||||
description: 'Başlamak için bir görünüm oluşturun',
|
description: 'Başlamak için bir ilişki oluşturun',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -239,6 +233,35 @@ export const tr: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Görseller',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'Notlar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Filtrele',
|
||||||
|
add_note: 'Not Ekle',
|
||||||
|
no_results: 'Not bulunamadı',
|
||||||
|
clear: 'Filtreyi Temizle',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Not Yok',
|
||||||
|
description:
|
||||||
|
'Tuval üzerinde metin açıklamaları eklemek için bir not oluşturun',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Boş not',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Not İşlemleri',
|
||||||
|
edit_content: 'İçeriği Düzenle',
|
||||||
|
delete_note: 'Notu Sil',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -255,6 +278,7 @@ export const tr: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Tanımlanmış enum değeri yok',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +300,7 @@ export const tr: LanguageTranslation = {
|
|||||||
show_all: 'Hepsini Gör',
|
show_all: 'Hepsini Gör',
|
||||||
undo: 'Geri Al',
|
undo: 'Geri Al',
|
||||||
redo: 'Yinele',
|
redo: 'Yinele',
|
||||||
reorder_diagram: 'Diyagramı Yeniden Sırala',
|
reorder_diagram: 'Diyagramı Otomatik Düzenle',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -313,12 +337,12 @@ export const tr: LanguageTranslation = {
|
|||||||
import_from_file: 'Import from File',
|
import_from_file: 'Import from File',
|
||||||
cancel: 'İptal',
|
cancel: 'İptal',
|
||||||
back: 'Geri',
|
back: 'Geri',
|
||||||
empty_diagram: 'Boş diyagram',
|
empty_diagram: 'Boş veritabanı',
|
||||||
continue: 'Devam',
|
continue: 'Devam',
|
||||||
import: 'İçe Aktar',
|
import: 'İçe Aktar',
|
||||||
},
|
},
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Diyagramı Aç',
|
title: 'Veritabanı Aç',
|
||||||
description: 'Aşağıdaki listeden açmak için bir diyagram seçin.',
|
description: 'Aşağıdaki listeden açmak için bir diyagram seçin.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Ad',
|
name: 'Ad',
|
||||||
@@ -328,6 +352,12 @@ export const tr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'İptal',
|
cancel: 'İptal',
|
||||||
open: 'Aç',
|
open: 'Aç',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Aç',
|
||||||
|
duplicate: 'Kopyala',
|
||||||
|
delete: 'Sil',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -470,9 +500,11 @@ export const tr: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Yeni Tablo',
|
new_table: 'Yeni Tablo',
|
||||||
|
new_view: 'Yeni Görünüm',
|
||||||
new_relationship: 'Yeni İlişki',
|
new_relationship: 'Yeni İlişki',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'Yeni Not',
|
||||||
},
|
},
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
edit_table: 'Tabloyu Düzenle',
|
edit_table: 'Tabloyu Düzenle',
|
||||||
@@ -492,6 +524,9 @@ export const tr: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Dil',
|
change_language: 'Dil',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Açık',
|
||||||
|
off: 'Kapalı',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const uk: LanguageTranslation = {
|
export const uk: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Нова',
|
||||||
|
browse: 'Огляд',
|
||||||
|
tables: 'Таблиці',
|
||||||
|
refs: 'Зв’язки',
|
||||||
|
dependencies: 'Залежності',
|
||||||
|
custom_types: 'Користувацькі типи',
|
||||||
|
visuals: 'Візуальні елементи',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Файл',
|
actions: 'Дії',
|
||||||
new: 'Новий',
|
new: 'Нова...',
|
||||||
open: 'Відкрити',
|
browse: 'Огляд...',
|
||||||
save: 'Зберегти',
|
save: 'Зберегти',
|
||||||
import: 'Імпорт бази даних',
|
import: 'Імпорт бази даних',
|
||||||
export_sql: 'Експорт SQL',
|
export_sql: 'Експорт SQL',
|
||||||
export_as: 'Експортувати як',
|
export_as: 'Експортувати як',
|
||||||
delete_diagram: 'Видалити діаграму',
|
delete_diagram: 'Видалити',
|
||||||
exit: 'Вийти',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Редагувати',
|
edit: 'Редагувати',
|
||||||
@@ -29,6 +37,7 @@ export const uk: LanguageTranslation = {
|
|||||||
show_field_attributes: 'Показати атрибути полів',
|
show_field_attributes: 'Показати атрибути полів',
|
||||||
hide_field_attributes: 'Приховати атрибути полів',
|
hide_field_attributes: 'Приховати атрибути полів',
|
||||||
zoom_on_scroll: 'Масштабувати прокручуванням',
|
zoom_on_scroll: 'Масштабувати прокручуванням',
|
||||||
|
show_views: 'Представлення бази даних',
|
||||||
theme: 'Тема',
|
theme: 'Тема',
|
||||||
show_dependencies: 'Показати залежності',
|
show_dependencies: 'Показати залежності',
|
||||||
hide_dependencies: 'Приховати залежності',
|
hide_dependencies: 'Приховати залежності',
|
||||||
@@ -64,22 +73,13 @@ export const uk: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Перевпорядкувати діаграму',
|
title: 'Автоматичне розміщення діаграми',
|
||||||
description:
|
description:
|
||||||
'Ця дія перевпорядкує всі таблиці на діаграмі. Хочете продовжити?',
|
'Ця дія перевпорядкує всі таблиці на діаграмі. Хочете продовжити?',
|
||||||
reorder: 'Перевпорядкувати',
|
reorder: 'Автоматичне розміщення',
|
||||||
cancel: 'Скасувати',
|
cancel: 'Скасувати',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: 'Кілька схем',
|
|
||||||
description:
|
|
||||||
'{{schemasCount}} схеми на цій діаграмі. Зараз відображається: {{formattedSchemas}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'немає',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Помилка копіювання',
|
title: 'Помилка копіювання',
|
||||||
@@ -114,14 +114,11 @@ export const uk: LanguageTranslation = {
|
|||||||
copied: 'Скопійовано!',
|
copied: 'Скопійовано!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Схема:',
|
|
||||||
filter_by_schema: 'Фільтрувати за схемою',
|
|
||||||
search_schema: 'Пошук схеми…',
|
|
||||||
no_schemas_found: 'Схеми не знайдено.',
|
|
||||||
view_all_options: 'Переглянути всі параметри…',
|
view_all_options: 'Переглянути всі параметри…',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Таблиці',
|
tables: 'Таблиці',
|
||||||
add_table: 'Додати таблицю',
|
add_table: 'Додати таблицю',
|
||||||
|
add_view: 'Додати представлення',
|
||||||
filter: 'Фільтр',
|
filter: 'Фільтр',
|
||||||
collapse: 'Згорнути все',
|
collapse: 'Згорнути все',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -147,6 +144,7 @@ export const uk: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Атрибути полів',
|
title: 'Атрибути полів',
|
||||||
unique: 'Унікальне',
|
unique: 'Унікальне',
|
||||||
|
auto_increment: 'Автоінкремент',
|
||||||
comments: 'Коментарі',
|
comments: 'Коментарі',
|
||||||
no_comments: 'Немає коментарів',
|
no_comments: 'Немає коментарів',
|
||||||
delete_field: 'Видалити поле',
|
delete_field: 'Видалити поле',
|
||||||
@@ -162,6 +160,7 @@ export const uk: LanguageTranslation = {
|
|||||||
title: 'Атрибути індексу',
|
title: 'Атрибути індексу',
|
||||||
name: 'Назва індекса',
|
name: 'Назва індекса',
|
||||||
unique: 'Унікальний',
|
unique: 'Унікальний',
|
||||||
|
index_type: 'Тип індексу',
|
||||||
delete_index: 'Видалити індекс',
|
delete_index: 'Видалити індекс',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -178,12 +177,15 @@ export const uk: LanguageTranslation = {
|
|||||||
description: 'Щоб почати, створіть таблицю',
|
description: 'Щоб почати, створіть таблицю',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Звʼязки',
|
refs: 'Refs',
|
||||||
filter: 'Фільтр',
|
filter: 'Фільтр',
|
||||||
add_relationship: 'Додати звʼязок',
|
|
||||||
collapse: 'Згорнути все',
|
collapse: 'Згорнути все',
|
||||||
|
add_relationship: 'Додати звʼязок',
|
||||||
|
relationships: 'Звʼязки',
|
||||||
|
dependencies: 'Залежності',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Звʼязок',
|
||||||
primary: 'Первинна таблиця',
|
primary: 'Первинна таблиця',
|
||||||
foreign: 'Посилання на таблицю',
|
foreign: 'Посилання на таблицю',
|
||||||
cardinality: 'Звʼязок',
|
cardinality: 'Звʼязок',
|
||||||
@@ -193,16 +195,8 @@ export const uk: LanguageTranslation = {
|
|||||||
delete_relationship: 'Видалити',
|
delete_relationship: 'Видалити',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: 'Звʼязків немає',
|
|
||||||
description: 'Створіть звʼязок для зʼєднання таблиць',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: 'Залежності',
|
|
||||||
filter: 'Фільтр',
|
|
||||||
collapse: 'Згорнути все',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: 'Залежність',
|
||||||
table: 'Таблиця',
|
table: 'Таблиця',
|
||||||
dependent_table: 'Залежне подання',
|
dependent_table: 'Залежне подання',
|
||||||
delete_dependency: 'Видалити',
|
delete_dependency: 'Видалити',
|
||||||
@@ -212,8 +206,8 @@ export const uk: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Жодних залежностей',
|
title: 'Жодних зв’язків',
|
||||||
description: 'Створіть подання, щоб почати',
|
description: 'Створіть зв’язок, щоб почати',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -237,6 +231,35 @@ export const uk: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Візуальні елементи',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'Нотатки',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Фільтр',
|
||||||
|
add_note: 'Додати Нотатку',
|
||||||
|
no_results: 'Нотатки не знайдено',
|
||||||
|
clear: 'Очистити Фільтр',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Немає Нотаток',
|
||||||
|
description:
|
||||||
|
'Створіть нотатку, щоб додати текстові анотації на полотні',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Порожня нотатка',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Дії з Нотаткою',
|
||||||
|
edit_content: 'Редагувати Вміст',
|
||||||
|
delete_note: 'Видалити Нотатку',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -253,6 +276,7 @@ export const uk: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Значення переліку не визначені',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -275,7 +299,7 @@ export const uk: LanguageTranslation = {
|
|||||||
show_all: 'Показати все',
|
show_all: 'Показати все',
|
||||||
undo: 'Скасувати',
|
undo: 'Скасувати',
|
||||||
redo: 'Повторити',
|
redo: 'Повторити',
|
||||||
reorder_diagram: 'Перевпорядкувати діаграму',
|
reorder_diagram: 'Автоматичне розміщення діаграми',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -313,13 +337,13 @@ export const uk: LanguageTranslation = {
|
|||||||
cancel: 'Скасувати',
|
cancel: 'Скасувати',
|
||||||
back: 'Назад',
|
back: 'Назад',
|
||||||
import_from_file: 'Імпортувати з файлу',
|
import_from_file: 'Імпортувати з файлу',
|
||||||
empty_diagram: 'Порожня діаграма',
|
empty_diagram: 'Порожня база даних',
|
||||||
continue: 'Продовжити',
|
continue: 'Продовжити',
|
||||||
import: 'Імпорт',
|
import: 'Імпорт',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: 'Відкрити діаграму',
|
title: 'Відкрити базу даних',
|
||||||
description:
|
description:
|
||||||
'Виберіть діаграму, яку потрібно відкрити, зі списку нижче.',
|
'Виберіть діаграму, яку потрібно відкрити, зі списку нижче.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
@@ -330,6 +354,12 @@ export const uk: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Скасувати',
|
cancel: 'Скасувати',
|
||||||
open: 'Відкрити',
|
open: 'Відкрити',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Відкрити',
|
||||||
|
duplicate: 'Дублювати',
|
||||||
|
delete: 'Видалити',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -476,9 +506,11 @@ export const uk: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Нова таблиця',
|
new_table: 'Нова таблиця',
|
||||||
|
new_view: 'Нове представлення',
|
||||||
new_relationship: 'Новий звʼязок',
|
new_relationship: 'Новий звʼязок',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'Нова Нотатка',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -497,6 +529,9 @@ export const uk: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Мова',
|
change_language: 'Мова',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: 'Увімк',
|
||||||
|
off: 'Вимк',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const vi: LanguageTranslation = {
|
export const vi: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: 'Mới',
|
||||||
|
browse: 'Duyệt',
|
||||||
|
tables: 'Bảng',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: 'Phụ thuộc',
|
||||||
|
custom_types: 'Kiểu tùy chỉnh',
|
||||||
|
visuals: 'Hình ảnh',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: 'Tệp',
|
actions: 'Hành động',
|
||||||
new: 'Tạo mới',
|
new: 'Mới...',
|
||||||
open: 'Mở',
|
browse: 'Duyệt...',
|
||||||
save: 'Lưu',
|
save: 'Lưu',
|
||||||
import: 'Nhập cơ sở dữ liệu',
|
import: 'Nhập cơ sở dữ liệu',
|
||||||
export_sql: 'Xuất SQL',
|
export_sql: 'Xuất SQL',
|
||||||
export_as: 'Xuất thành',
|
export_as: 'Xuất thành',
|
||||||
delete_diagram: 'Xóa sơ đồ',
|
delete_diagram: 'Xóa',
|
||||||
exit: 'Thoát',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: 'Sửa',
|
edit: 'Sửa',
|
||||||
@@ -29,6 +37,7 @@ export const vi: LanguageTranslation = {
|
|||||||
show_field_attributes: 'Hiển thị thuộc tính trường',
|
show_field_attributes: 'Hiển thị thuộc tính trường',
|
||||||
hide_field_attributes: 'Ẩn 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',
|
zoom_on_scroll: 'Thu phóng khi cuộn',
|
||||||
|
show_views: 'Chế độ xem Cơ sở dữ liệu',
|
||||||
theme: 'Chủ đề',
|
theme: 'Chủ đề',
|
||||||
show_dependencies: 'Hiển thị các phụ thuộc',
|
show_dependencies: 'Hiển thị các phụ thuộc',
|
||||||
hide_dependencies: 'Ẩn các phụ thuộc',
|
hide_dependencies: 'Ẩn các phụ thuộc',
|
||||||
@@ -65,22 +74,13 @@ export const vi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: 'Sắp xếp lại sơ đồ',
|
title: 'Tự động sắp xếp sơ đồ',
|
||||||
description:
|
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?',
|
'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',
|
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}}.',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: 'không có',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: 'Sao chép thất bại',
|
title: 'Sao chép thất bại',
|
||||||
@@ -115,14 +115,11 @@ export const vi: LanguageTranslation = {
|
|||||||
copied: 'Đã sao chép!',
|
copied: 'Đã sao chép!',
|
||||||
|
|
||||||
side_panel: {
|
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...',
|
view_all_options: 'Xem tất cả tùy chọn...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: 'Bảng',
|
tables: 'Bảng',
|
||||||
add_table: 'Thêm bảng',
|
add_table: 'Thêm bảng',
|
||||||
|
add_view: 'Thêm Chế độ xem',
|
||||||
filter: 'Lọc',
|
filter: 'Lọc',
|
||||||
collapse: 'Thu gọn tất cả',
|
collapse: 'Thu gọn tất cả',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -148,6 +145,7 @@ export const vi: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: 'Thuộc tính trường',
|
title: 'Thuộc tính trường',
|
||||||
unique: 'Giá trị duy nhất',
|
unique: 'Giá trị duy nhất',
|
||||||
|
auto_increment: 'Tự động tăng',
|
||||||
comments: 'Bình luận',
|
comments: 'Bình luận',
|
||||||
no_comments: 'Không có bình luận',
|
no_comments: 'Không có bình luận',
|
||||||
delete_field: 'Xóa trường',
|
delete_field: 'Xóa trường',
|
||||||
@@ -163,6 +161,7 @@ export const vi: LanguageTranslation = {
|
|||||||
title: 'Thuộc tính chỉ mục',
|
title: 'Thuộc tính chỉ mục',
|
||||||
name: 'Tên',
|
name: 'Tên',
|
||||||
unique: 'Giá trị duy nhất',
|
unique: 'Giá trị duy nhất',
|
||||||
|
index_type: 'Loại chỉ mục',
|
||||||
delete_index: 'Xóa chỉ mục',
|
delete_index: 'Xóa chỉ mục',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -179,12 +178,15 @@ export const vi: LanguageTranslation = {
|
|||||||
description: 'Tạo một bảng để bắt đầu',
|
description: 'Tạo một bảng để bắt đầu',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: 'Quan hệ',
|
refs: 'Refs',
|
||||||
filter: 'Lọc',
|
filter: 'Lọc',
|
||||||
add_relationship: 'Thêm quan hệ',
|
|
||||||
collapse: 'Thu gọn tất cả',
|
collapse: 'Thu gọn tất cả',
|
||||||
|
add_relationship: 'Thêm quan hệ',
|
||||||
|
relationships: 'Quan hệ',
|
||||||
|
dependencies: 'Phụ thuộc',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: 'Quan hệ',
|
||||||
primary: 'Bảng khóa chính',
|
primary: 'Bảng khóa chính',
|
||||||
foreign: 'Bảng khóa ngoại',
|
foreign: 'Bảng khóa ngoại',
|
||||||
cardinality: 'Quan hệ',
|
cardinality: 'Quan hệ',
|
||||||
@@ -194,16 +196,8 @@ export const vi: LanguageTranslation = {
|
|||||||
delete_relationship: 'Xóa',
|
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: {
|
||||||
|
dependency: 'Phụ thuộc',
|
||||||
table: 'Bảng',
|
table: 'Bảng',
|
||||||
dependent_table: 'Bảng xem phụ thuộc',
|
dependent_table: 'Bảng xem phụ thuộc',
|
||||||
delete_dependency: 'Xóa',
|
delete_dependency: 'Xóa',
|
||||||
@@ -213,8 +207,8 @@ export const vi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: 'Không có phụ thuộc',
|
title: 'Không có quan hệ',
|
||||||
description: 'Tạo bảng xem phụ thuộc để bắt đầu',
|
description: 'Tạo một quan hệ để bắt đầu',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -238,6 +232,35 @@ export const vi: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: 'Hình ảnh',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: 'Ghi chú',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: 'Lọc',
|
||||||
|
add_note: 'Thêm Ghi Chú',
|
||||||
|
no_results: 'Không tìm thấy ghi chú',
|
||||||
|
clear: 'Xóa Bộ Lọc',
|
||||||
|
empty_state: {
|
||||||
|
title: 'Không Có Ghi Chú',
|
||||||
|
description:
|
||||||
|
'Tạo ghi chú để thêm chú thích văn bản trên canvas',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: 'Ghi chú trống',
|
||||||
|
note_actions: {
|
||||||
|
title: 'Hành Động Ghi Chú',
|
||||||
|
edit_content: 'Chỉnh Sửa Nội Dung',
|
||||||
|
delete_note: 'Xóa Ghi Chú',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -254,6 +277,7 @@ export const vi: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: 'Không có giá trị enum được định nghĩa',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -276,7 +300,7 @@ export const vi: LanguageTranslation = {
|
|||||||
show_all: 'Hiển thị tất cả',
|
show_all: 'Hiển thị tất cả',
|
||||||
undo: 'Hoàn tác',
|
undo: 'Hoàn tác',
|
||||||
redo: 'Làm lại',
|
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
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -314,13 +338,13 @@ export const vi: LanguageTranslation = {
|
|||||||
cancel: 'Hủy',
|
cancel: 'Hủy',
|
||||||
import_from_file: 'Nhập từ tệp',
|
import_from_file: 'Nhập từ tệp',
|
||||||
back: 'Trở lại',
|
back: 'Trở lại',
|
||||||
empty_diagram: 'Sơ đồ trống',
|
empty_diagram: 'Cơ sở dữ liệu trống',
|
||||||
continue: 'Tiếp tục',
|
continue: 'Tiếp tục',
|
||||||
import: 'Nhập',
|
import: 'Nhập',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
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.',
|
description: 'Chọn sơ đồ để mở từ danh sách bên dưới.',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: 'Tên',
|
name: 'Tên',
|
||||||
@@ -330,6 +354,12 @@ export const vi: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: 'Hủy',
|
cancel: 'Hủy',
|
||||||
open: 'Mở',
|
open: 'Mở',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: 'Mở',
|
||||||
|
duplicate: 'Nhân bản',
|
||||||
|
delete: 'Xóa',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -477,9 +507,11 @@ export const vi: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: 'Tạo bảng mới',
|
new_table: 'Tạo bảng mới',
|
||||||
|
new_view: 'Chế độ xem Mới',
|
||||||
new_relationship: 'Tạo quan hệ mới',
|
new_relationship: 'Tạo quan hệ mới',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: 'Ghi Chú Mới',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -498,6 +530,9 @@ export const vi: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: 'Ngôn ngữ',
|
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 = {
|
export const zh_CN: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: '新建',
|
||||||
|
browse: '浏览',
|
||||||
|
tables: '表',
|
||||||
|
refs: '引用',
|
||||||
|
dependencies: '依赖关系',
|
||||||
|
custom_types: '自定义类型',
|
||||||
|
visuals: '视觉效果',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: '文件',
|
actions: '操作',
|
||||||
new: '新建',
|
new: '新建...',
|
||||||
open: '打开',
|
browse: '浏览...',
|
||||||
save: '保存',
|
save: '保存',
|
||||||
import: '导入数据库',
|
import: '导入数据库',
|
||||||
export_sql: '导出 SQL 语句',
|
export_sql: '导出 SQL 语句',
|
||||||
export_as: '导出为',
|
export_as: '导出为',
|
||||||
delete_diagram: '删除关系图',
|
delete_diagram: '删除',
|
||||||
exit: '退出',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: '编辑',
|
edit: '编辑',
|
||||||
@@ -29,6 +37,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
show_field_attributes: '展示字段属性',
|
show_field_attributes: '展示字段属性',
|
||||||
hide_field_attributes: '隐藏字段属性',
|
hide_field_attributes: '隐藏字段属性',
|
||||||
zoom_on_scroll: '滚动缩放',
|
zoom_on_scroll: '滚动缩放',
|
||||||
|
show_views: '数据库视图',
|
||||||
theme: '主题',
|
theme: '主题',
|
||||||
show_dependencies: '展示依赖',
|
show_dependencies: '展示依赖',
|
||||||
hide_dependencies: '隐藏依赖',
|
hide_dependencies: '隐藏依赖',
|
||||||
@@ -63,21 +72,12 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: '重新排列关系图',
|
title: '自动排列关系图',
|
||||||
description: '此操作将重新排列关系图中的所有表。是否要继续?',
|
description: '此操作将重新排列关系图中的所有表。是否要继续?',
|
||||||
reorder: '重新排列',
|
reorder: '自动排列',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: '多个模式',
|
|
||||||
description:
|
|
||||||
'此关系图中有 {{schemasCount}} 个模式,当前显示:{{formattedSchemas}}。',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: '无',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: '复制失败',
|
title: '复制失败',
|
||||||
@@ -112,14 +112,11 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
copied: '复制了!',
|
copied: '复制了!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: '模式:',
|
|
||||||
filter_by_schema: '按模式筛选',
|
|
||||||
search_schema: '搜索模式...',
|
|
||||||
no_schemas_found: '未找到模式。',
|
|
||||||
view_all_options: '查看所有选项...',
|
view_all_options: '查看所有选项...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: '表',
|
tables: '表',
|
||||||
add_table: '添加表',
|
add_table: '添加表',
|
||||||
|
add_view: '添加视图',
|
||||||
filter: '筛选',
|
filter: '筛选',
|
||||||
collapse: '全部折叠',
|
collapse: '全部折叠',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -145,6 +142,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: '字段属性',
|
title: '字段属性',
|
||||||
unique: '唯一',
|
unique: '唯一',
|
||||||
|
auto_increment: '自动递增',
|
||||||
comments: '注释',
|
comments: '注释',
|
||||||
no_comments: '空',
|
no_comments: '空',
|
||||||
delete_field: '删除字段',
|
delete_field: '删除字段',
|
||||||
@@ -160,6 +158,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
title: '索引属性',
|
title: '索引属性',
|
||||||
name: '名称',
|
name: '名称',
|
||||||
unique: '唯一',
|
unique: '唯一',
|
||||||
|
index_type: '索引类型',
|
||||||
delete_index: '删除索引',
|
delete_index: '删除索引',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -176,12 +175,15 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
description: '新建表以开始',
|
description: '新建表以开始',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: '关系',
|
refs: '引用',
|
||||||
filter: '筛选',
|
filter: '筛选',
|
||||||
add_relationship: '添加关系',
|
|
||||||
collapse: '全部折叠',
|
collapse: '全部折叠',
|
||||||
|
add_relationship: '添加关系',
|
||||||
|
relationships: '关系',
|
||||||
|
dependencies: '依赖关系',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: '关系',
|
||||||
primary: '主表',
|
primary: '主表',
|
||||||
foreign: '被引用表',
|
foreign: '被引用表',
|
||||||
cardinality: '基数',
|
cardinality: '基数',
|
||||||
@@ -191,16 +193,8 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
delete_relationship: '删除',
|
delete_relationship: '删除',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: '无关系',
|
|
||||||
description: '创建关系以连接表',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: '依赖关系',
|
|
||||||
filter: '筛选',
|
|
||||||
collapse: '全部折叠',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: '依赖',
|
||||||
table: '表',
|
table: '表',
|
||||||
dependent_table: '依赖视图',
|
dependent_table: '依赖视图',
|
||||||
delete_dependency: '删除',
|
delete_dependency: '删除',
|
||||||
@@ -210,8 +204,8 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: '无依赖',
|
title: '无关系',
|
||||||
description: '创建视图以开始',
|
description: '创建关系以开始',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -235,6 +229,34 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: '视觉效果',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: '笔记',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: '筛选',
|
||||||
|
add_note: '添加笔记',
|
||||||
|
no_results: '未找到笔记',
|
||||||
|
clear: '清除筛选',
|
||||||
|
empty_state: {
|
||||||
|
title: '没有笔记',
|
||||||
|
description: '创建笔记以在画布上添加文本注释',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: '空笔记',
|
||||||
|
note_actions: {
|
||||||
|
title: '笔记操作',
|
||||||
|
edit_content: '编辑内容',
|
||||||
|
delete_note: '删除笔记',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -251,6 +273,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: '没有定义枚举值',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -273,7 +296,7 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
show_all: '展示全部',
|
show_all: '展示全部',
|
||||||
undo: '撤销',
|
undo: '撤销',
|
||||||
redo: '重做',
|
redo: '重做',
|
||||||
reorder_diagram: '重新排列关系图',
|
reorder_diagram: '自动排列关系图',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -311,13 +334,13 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
import_from_file: '从文件导入',
|
import_from_file: '从文件导入',
|
||||||
back: '上一步',
|
back: '上一步',
|
||||||
empty_diagram: '新建空关系图',
|
empty_diagram: '空数据库',
|
||||||
continue: '下一步',
|
continue: '下一步',
|
||||||
import: '导入',
|
import: '导入',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: '打开关系图',
|
title: '打开数据库',
|
||||||
description: '从下面的列表中选择一个图表打开。',
|
description: '从下面的列表中选择一个图表打开。',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: '名称',
|
name: '名称',
|
||||||
@@ -327,6 +350,12 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
open: '打开',
|
open: '打开',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: '打开',
|
||||||
|
duplicate: '复制',
|
||||||
|
delete: '删除',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -472,9 +501,11 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: '新建表',
|
new_table: '新建表',
|
||||||
|
new_view: '新建视图',
|
||||||
new_relationship: '新建关系',
|
new_relationship: '新建关系',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: '新笔记',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -493,6 +524,9 @@ export const zh_CN: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: '语言',
|
change_language: '语言',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: '开启',
|
||||||
|
off: '关闭',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,25 @@ import type { LanguageMetadata, LanguageTranslation } from '../types';
|
|||||||
|
|
||||||
export const zh_TW: LanguageTranslation = {
|
export const zh_TW: LanguageTranslation = {
|
||||||
translation: {
|
translation: {
|
||||||
|
editor_sidebar: {
|
||||||
|
new_diagram: '新建',
|
||||||
|
browse: '瀏覽',
|
||||||
|
tables: '表格',
|
||||||
|
refs: 'Refs',
|
||||||
|
dependencies: '相依性',
|
||||||
|
custom_types: '自定義類型',
|
||||||
|
visuals: '視覺效果',
|
||||||
|
},
|
||||||
menu: {
|
menu: {
|
||||||
file: {
|
actions: {
|
||||||
file: '檔案',
|
actions: '操作',
|
||||||
new: '新增',
|
new: '新增...',
|
||||||
open: '開啟',
|
browse: '瀏覽...',
|
||||||
save: '儲存',
|
save: '儲存',
|
||||||
import: '匯入資料庫',
|
import: '匯入資料庫',
|
||||||
export_sql: '匯出 SQL',
|
export_sql: '匯出 SQL',
|
||||||
export_as: '匯出為特定格式',
|
export_as: '匯出為特定格式',
|
||||||
delete_diagram: '刪除圖表',
|
delete_diagram: '刪除',
|
||||||
exit: '退出',
|
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
edit: '編輯',
|
edit: '編輯',
|
||||||
@@ -29,6 +37,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
hide_field_attributes: '隱藏欄位屬性',
|
hide_field_attributes: '隱藏欄位屬性',
|
||||||
show_field_attributes: '顯示欄位屬性',
|
show_field_attributes: '顯示欄位屬性',
|
||||||
zoom_on_scroll: '滾動縮放',
|
zoom_on_scroll: '滾動縮放',
|
||||||
|
show_views: '資料庫檢視',
|
||||||
theme: '主題',
|
theme: '主題',
|
||||||
show_dependencies: '顯示相依性',
|
show_dependencies: '顯示相依性',
|
||||||
hide_dependencies: '隱藏相依性',
|
hide_dependencies: '隱藏相依性',
|
||||||
@@ -63,21 +72,12 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reorder_diagram_alert: {
|
reorder_diagram_alert: {
|
||||||
title: '重新排列圖表',
|
title: '自動排列圖表',
|
||||||
description: '此操作將重新排列圖表中的所有表格。是否繼續?',
|
description: '此操作將重新排列圖表中的所有表格。是否繼續?',
|
||||||
reorder: '重新排列',
|
reorder: '自動排列',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
},
|
},
|
||||||
|
|
||||||
multiple_schemas_alert: {
|
|
||||||
title: '多重 Schema',
|
|
||||||
description:
|
|
||||||
'此圖表中包含 {{schemasCount}} 個 Schema,目前顯示:{{formattedSchemas}}。',
|
|
||||||
// TODO: Translate
|
|
||||||
show_me: 'Show me',
|
|
||||||
none: '無',
|
|
||||||
},
|
|
||||||
|
|
||||||
copy_to_clipboard_toast: {
|
copy_to_clipboard_toast: {
|
||||||
unsupported: {
|
unsupported: {
|
||||||
title: '複製失敗',
|
title: '複製失敗',
|
||||||
@@ -112,14 +112,11 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
copied: '已複製!',
|
copied: '已複製!',
|
||||||
|
|
||||||
side_panel: {
|
side_panel: {
|
||||||
schema: 'Schema:',
|
|
||||||
filter_by_schema: '依 Schema 篩選',
|
|
||||||
search_schema: '搜尋 Schema...',
|
|
||||||
no_schemas_found: '未找到 Schema。',
|
|
||||||
view_all_options: '顯示所有選項...',
|
view_all_options: '顯示所有選項...',
|
||||||
tables_section: {
|
tables_section: {
|
||||||
tables: '表格',
|
tables: '表格',
|
||||||
add_table: '新增表格',
|
add_table: '新增表格',
|
||||||
|
add_view: '新增檢視',
|
||||||
filter: '篩選',
|
filter: '篩選',
|
||||||
collapse: '全部摺疊',
|
collapse: '全部摺疊',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
@@ -145,6 +142,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
field_actions: {
|
field_actions: {
|
||||||
title: '欄位屬性',
|
title: '欄位屬性',
|
||||||
unique: '唯一',
|
unique: '唯一',
|
||||||
|
auto_increment: '自動遞增',
|
||||||
comments: '註解',
|
comments: '註解',
|
||||||
no_comments: '無註解',
|
no_comments: '無註解',
|
||||||
delete_field: '刪除欄位',
|
delete_field: '刪除欄位',
|
||||||
@@ -160,6 +158,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
title: '索引屬性',
|
title: '索引屬性',
|
||||||
name: '名稱',
|
name: '名稱',
|
||||||
unique: '唯一',
|
unique: '唯一',
|
||||||
|
index_type: '索引類型',
|
||||||
delete_index: '刪除索引',
|
delete_index: '刪除索引',
|
||||||
},
|
},
|
||||||
table_actions: {
|
table_actions: {
|
||||||
@@ -176,12 +175,15 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
description: '請新增表格以開始',
|
description: '請新增表格以開始',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships_section: {
|
refs_section: {
|
||||||
relationships: '關聯',
|
refs: 'Refs',
|
||||||
filter: '篩選',
|
filter: '篩選',
|
||||||
add_relationship: '新增關聯',
|
|
||||||
collapse: '全部摺疊',
|
collapse: '全部摺疊',
|
||||||
|
add_relationship: '新增關聯',
|
||||||
|
relationships: '關聯',
|
||||||
|
dependencies: '相依性',
|
||||||
relationship: {
|
relationship: {
|
||||||
|
relationship: '關聯',
|
||||||
primary: '主表格',
|
primary: '主表格',
|
||||||
foreign: '參照表格',
|
foreign: '參照表格',
|
||||||
cardinality: '基數',
|
cardinality: '基數',
|
||||||
@@ -191,16 +193,8 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
delete_relationship: '刪除',
|
delete_relationship: '刪除',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
|
||||||
title: '尚無關聯',
|
|
||||||
description: '請新增關聯以連接表格',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dependencies_section: {
|
|
||||||
dependencies: '相依性',
|
|
||||||
filter: '篩選',
|
|
||||||
collapse: '全部摺疊',
|
|
||||||
dependency: {
|
dependency: {
|
||||||
|
dependency: '相依性',
|
||||||
table: '表格',
|
table: '表格',
|
||||||
dependent_table: '相依檢視',
|
dependent_table: '相依檢視',
|
||||||
delete_dependency: '刪除',
|
delete_dependency: '刪除',
|
||||||
@@ -210,8 +204,8 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
empty_state: {
|
empty_state: {
|
||||||
title: '尚無相依性',
|
title: '尚無關聯',
|
||||||
description: '請建立檢視以開始',
|
description: '請建立關聯以開始',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -235,6 +229,34 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
description: 'Create an area to get started',
|
description: 'Create an area to get started',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
visuals_section: {
|
||||||
|
visuals: '視覺效果',
|
||||||
|
tabs: {
|
||||||
|
areas: 'Areas',
|
||||||
|
notes: '筆記',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
notes_section: {
|
||||||
|
filter: '篩選',
|
||||||
|
add_note: '新增筆記',
|
||||||
|
no_results: '未找到筆記',
|
||||||
|
clear: '清除篩選',
|
||||||
|
empty_state: {
|
||||||
|
title: '沒有筆記',
|
||||||
|
description: '建立筆記以在畫布上新增文字註解',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
empty_note: '空白筆記',
|
||||||
|
note_actions: {
|
||||||
|
title: '筆記操作',
|
||||||
|
edit_content: '編輯內容',
|
||||||
|
delete_note: '刪除筆記',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
custom_types_section: {
|
custom_types_section: {
|
||||||
custom_types: 'Custom Types',
|
custom_types: 'Custom Types',
|
||||||
@@ -251,6 +273,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
enum_values: 'Enum Values',
|
enum_values: 'Enum Values',
|
||||||
composite_fields: 'Fields',
|
composite_fields: 'Fields',
|
||||||
no_fields: 'No fields defined',
|
no_fields: 'No fields defined',
|
||||||
|
no_values: '沒有定義列舉值',
|
||||||
field_name_placeholder: 'Field name',
|
field_name_placeholder: 'Field name',
|
||||||
field_type_placeholder: 'Select type',
|
field_type_placeholder: 'Select type',
|
||||||
add_field: 'Add Field',
|
add_field: 'Add Field',
|
||||||
@@ -273,7 +296,7 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
show_all: '顯示全部',
|
show_all: '顯示全部',
|
||||||
undo: '復原',
|
undo: '復原',
|
||||||
redo: '重做',
|
redo: '重做',
|
||||||
reorder_diagram: '重新排列圖表',
|
reorder_diagram: '自動排列圖表',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
clear_custom_type_highlight: 'Clear highlight for "{{typeName}}"',
|
||||||
custom_type_highlight_tooltip:
|
custom_type_highlight_tooltip:
|
||||||
@@ -310,13 +333,13 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
import_from_file: '從檔案匯入',
|
import_from_file: '從檔案匯入',
|
||||||
back: '返回',
|
back: '返回',
|
||||||
empty_diagram: '空白圖表',
|
empty_diagram: '空資料庫',
|
||||||
continue: '繼續',
|
continue: '繼續',
|
||||||
import: '匯入',
|
import: '匯入',
|
||||||
},
|
},
|
||||||
|
|
||||||
open_diagram_dialog: {
|
open_diagram_dialog: {
|
||||||
title: '開啟圖表',
|
title: '開啟資料庫',
|
||||||
description: '請從以下列表中選擇一個圖表。',
|
description: '請從以下列表中選擇一個圖表。',
|
||||||
table_columns: {
|
table_columns: {
|
||||||
name: '名稱',
|
name: '名稱',
|
||||||
@@ -326,6 +349,12 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
},
|
},
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
open: '開啟',
|
open: '開啟',
|
||||||
|
|
||||||
|
diagram_actions: {
|
||||||
|
open: '開啟',
|
||||||
|
duplicate: '複製',
|
||||||
|
delete: '刪除',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
export_sql_dialog: {
|
export_sql_dialog: {
|
||||||
@@ -472,9 +501,11 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
|
|
||||||
canvas_context_menu: {
|
canvas_context_menu: {
|
||||||
new_table: '新建表格',
|
new_table: '新建表格',
|
||||||
|
new_view: '新檢視',
|
||||||
new_relationship: '新建關聯',
|
new_relationship: '新建關聯',
|
||||||
// TODO: Translate
|
// TODO: Translate
|
||||||
new_area: 'New Area',
|
new_area: 'New Area',
|
||||||
|
new_note: '新筆記',
|
||||||
},
|
},
|
||||||
|
|
||||||
table_node_context_menu: {
|
table_node_context_menu: {
|
||||||
@@ -493,6 +524,9 @@ export const zh_TW: LanguageTranslation = {
|
|||||||
language_select: {
|
language_select: {
|
||||||
change_language: '變更語言',
|
change_language: '變更語言',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: '開啟',
|
||||||
|
off: '關閉',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,4 +18,7 @@
|
|||||||
|
|
||||||
.marker-definitions {
|
.marker-definitions {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nodrag {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { DBIndex } from './domain/db-index';
|
|||||||
import type { DBRelationship } from './domain/db-relationship';
|
import type { DBRelationship } from './domain/db-relationship';
|
||||||
import type { DBTable } from './domain/db-table';
|
import type { DBTable } from './domain/db-table';
|
||||||
import type { Diagram } from './domain/diagram';
|
import type { Diagram } from './domain/diagram';
|
||||||
|
import type { Note } from './domain/note';
|
||||||
import { generateId as defaultGenerateId } from './utils';
|
import { generateId as defaultGenerateId } from './utils';
|
||||||
|
|
||||||
const generateIdsMapFromTable = (
|
const generateIdsMapFromTable = (
|
||||||
@@ -49,6 +50,10 @@ const generateIdsMapFromDiagram = (
|
|||||||
idsMap.set(area.id, generateId());
|
idsMap.set(area.id, generateId());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
diagram.notes?.forEach((note) => {
|
||||||
|
idsMap.set(note.id, generateId());
|
||||||
|
});
|
||||||
|
|
||||||
diagram.customTypes?.forEach((customType) => {
|
diagram.customTypes?.forEach((customType) => {
|
||||||
idsMap.set(customType.id, generateId());
|
idsMap.set(customType.id, generateId());
|
||||||
});
|
});
|
||||||
@@ -218,6 +223,21 @@ export const cloneDiagram = (
|
|||||||
})
|
})
|
||||||
.filter((area): area is Area => area !== null) ?? [];
|
.filter((area): area is Area => area !== null) ?? [];
|
||||||
|
|
||||||
|
const notes: Note[] =
|
||||||
|
diagram.notes
|
||||||
|
?.map((note) => {
|
||||||
|
const id = getNewId(note.id);
|
||||||
|
if (!id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...note,
|
||||||
|
id,
|
||||||
|
} satisfies Note;
|
||||||
|
})
|
||||||
|
.filter((note): note is Note => note !== null) ?? [];
|
||||||
|
|
||||||
const customTypes: DBCustomType[] =
|
const customTypes: DBCustomType[] =
|
||||||
diagram.customTypes
|
diagram.customTypes
|
||||||
?.map((customType) => {
|
?.map((customType) => {
|
||||||
@@ -242,6 +262,7 @@ export const cloneDiagram = (
|
|||||||
relationships,
|
relationships,
|
||||||
tables,
|
tables,
|
||||||
areas,
|
areas,
|
||||||
|
notes,
|
||||||
customTypes,
|
customTypes,
|
||||||
createdAt: diagram.createdAt
|
createdAt: diagram.createdAt
|
||||||
? new Date(diagram.createdAt)
|
? new Date(diagram.createdAt)
|
||||||
|
|||||||
@@ -19,3 +19,5 @@ export const randomColor = () => {
|
|||||||
|
|
||||||
export const viewColor = '#b0b0b0';
|
export const viewColor = '#b0b0b0';
|
||||||
export const materializedViewColor = '#7d7d7d';
|
export const materializedViewColor = '#7d7d7d';
|
||||||
|
export const defaultTableColor = '#8eb7ff';
|
||||||
|
export const defaultAreaColor = '#b067e9';
|
||||||
|
|||||||
@@ -129,9 +129,6 @@ export const clickhouseDataTypes: readonly DataTypeData[] = [
|
|||||||
{ name: 'enum', id: 'enum' },
|
{ name: 'enum', id: 'enum' },
|
||||||
{ name: 'lowcardinality', id: 'lowcardinality' },
|
{ name: 'lowcardinality', id: 'lowcardinality' },
|
||||||
|
|
||||||
// Array Type
|
|
||||||
{ name: 'array', id: 'array' },
|
|
||||||
|
|
||||||
// Tuple Type
|
// Tuple Type
|
||||||
{ name: 'tuple', id: 'tuple' },
|
{ name: 'tuple', id: 'tuple' },
|
||||||
{ name: 'map', id: 'map' },
|
{ name: 'map', id: 'map' },
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { DatabaseType } from '../../domain/database-type';
|
import { DatabaseType } from '../../domain/database-type';
|
||||||
|
import { databaseSupportsArrays } from '../../domain/database-capabilities';
|
||||||
import { clickhouseDataTypes } from './clickhouse-data-types';
|
import { clickhouseDataTypes } from './clickhouse-data-types';
|
||||||
import { genericDataTypes } from './generic-data-types';
|
import { genericDataTypes } from './generic-data-types';
|
||||||
import { mariadbDataTypes } from './mariadb-data-types';
|
import { mariadbDataTypes } from './mariadb-data-types';
|
||||||
@@ -146,3 +147,53 @@ export const findDataTypeDataById = (
|
|||||||
|
|
||||||
return dataTypesOptions.find((dataType) => dataType.id === id);
|
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());
|
||||||
|
};
|
||||||
|
|
||||||
|
export const autoIncrementAlwaysOn = (dataTypeName: string): boolean => {
|
||||||
|
return ['serial', 'bigserial', 'smallserial'].includes(
|
||||||
|
dataTypeName.toLowerCase()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const requiresNotNull = (dataTypeName: string): boolean => {
|
||||||
|
return ['serial', 'bigserial', 'smallserial'].includes(
|
||||||
|
dataTypeName.toLowerCase()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ARRAY_INCOMPATIBLE_TYPES = [
|
||||||
|
'serial',
|
||||||
|
'bigserial',
|
||||||
|
'smallserial',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const supportsArrayDataType = (
|
||||||
|
dataTypeName: string,
|
||||||
|
databaseType: DatabaseType
|
||||||
|
): boolean => {
|
||||||
|
if (!databaseSupportsArrays(databaseType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !ARRAY_INCOMPATIBLE_TYPES.includes(
|
||||||
|
dataTypeName.toLowerCase() as (typeof ARRAY_INCOMPATIBLE_TYPES)[number]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const postgresDataTypes: readonly DataTypeData[] = [
|
|||||||
{ name: 'text', id: 'text', usageLevel: 1 },
|
{ name: 'text', id: 'text', usageLevel: 1 },
|
||||||
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
|
{ name: 'boolean', id: 'boolean', usageLevel: 1 },
|
||||||
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
|
{ name: 'timestamp', id: 'timestamp', usageLevel: 1 },
|
||||||
|
{ name: 'timestamptz', id: 'timestamptz', usageLevel: 1 },
|
||||||
{ name: 'date', id: 'date', usageLevel: 1 },
|
{ name: 'date', id: 'date', usageLevel: 1 },
|
||||||
|
|
||||||
// Level 2 - Second most common types
|
// Level 2 - Second most common types
|
||||||
@@ -42,6 +43,7 @@ export const postgresDataTypes: readonly DataTypeData[] = [
|
|||||||
id: 'timestamp_with_time_zone',
|
id: 'timestamp_with_time_zone',
|
||||||
usageLevel: 2,
|
usageLevel: 2,
|
||||||
},
|
},
|
||||||
|
{ name: 'int', id: 'int', usageLevel: 2 },
|
||||||
|
|
||||||
// Less common types
|
// Less common types
|
||||||
{
|
{
|
||||||
@@ -95,7 +97,6 @@ export const postgresDataTypes: readonly DataTypeData[] = [
|
|||||||
{ name: 'tsvector', id: 'tsvector' },
|
{ name: 'tsvector', id: 'tsvector' },
|
||||||
{ name: 'tsquery', id: 'tsquery' },
|
{ name: 'tsquery', id: 'tsquery' },
|
||||||
{ name: 'xml', id: 'xml' },
|
{ name: 'xml', id: 'xml' },
|
||||||
{ name: 'array', id: 'array' },
|
|
||||||
{ name: 'int4range', id: 'int4range' },
|
{ name: 'int4range', id: 'int4range' },
|
||||||
{ name: 'int8range', id: 'int8range' },
|
{ name: 'int8range', id: 'int8range' },
|
||||||
{ name: 'numrange', id: 'numrange' },
|
{ name: 'numrange', id: 'numrange' },
|
||||||
|
|||||||
@@ -50,5 +50,8 @@ export const sqliteDataTypes: readonly DataTypeData[] = [
|
|||||||
{ name: 'smallint', id: 'smallint' },
|
{ name: 'smallint', id: 'smallint' },
|
||||||
{ name: 'bigint', id: 'bigint' },
|
{ name: 'bigint', id: 'bigint' },
|
||||||
{ name: 'bool', id: 'bool' },
|
{ name: 'bool', id: 'bool' },
|
||||||
|
{ name: 'boolean', id: 'boolean' }, // Added for smartquery compatibility
|
||||||
{ name: 'time', id: 'time' },
|
{ name: 'time', id: 'time' },
|
||||||
|
{ name: 'date', id: 'date' }, // Added for smartquery compatibility
|
||||||
|
{ name: 'datetime', id: 'datetime' }, // Added for smartquery compatibility
|
||||||
] as const;
|
] as const;
|
||||||
|
|||||||
21
src/lib/data/import-metadata/import/custom-types.ts
Normal file
21
src/lib/data/import-metadata/import/custom-types.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import type { DBCustomType, DBCustomTypeKind } from '@/lib/domain';
|
||||||
|
import { schemaNameToDomainSchemaName } from '@/lib/domain';
|
||||||
|
import type { DBCustomTypeInfo } from '../metadata-types/custom-type-info';
|
||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
|
||||||
|
export const createCustomTypesFromMetadata = ({
|
||||||
|
customTypes,
|
||||||
|
}: {
|
||||||
|
customTypes: DBCustomTypeInfo[];
|
||||||
|
}): DBCustomType[] => {
|
||||||
|
return customTypes.map((customType) => {
|
||||||
|
return {
|
||||||
|
id: generateId(),
|
||||||
|
schema: schemaNameToDomainSchemaName(customType.schema),
|
||||||
|
name: customType.type,
|
||||||
|
kind: customType.kind as DBCustomTypeKind,
|
||||||
|
values: customType.values,
|
||||||
|
fields: customType.fields,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
351
src/lib/data/import-metadata/import/dependencies.ts
Normal file
351
src/lib/data/import-metadata/import/dependencies.ts
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
import type { AST } from 'node-sql-parser';
|
||||||
|
import type { DBDependency, DBTable } from '@/lib/domain';
|
||||||
|
import { DatabaseType, schemaNameToDomainSchemaName } from '@/lib/domain';
|
||||||
|
import type { ViewInfo } from '../metadata-types/view-info';
|
||||||
|
import { decodeViewDefinition } from './tables';
|
||||||
|
|
||||||
|
const astDatabaseTypes: Record<DatabaseType, string> = {
|
||||||
|
[DatabaseType.POSTGRESQL]: 'postgresql',
|
||||||
|
[DatabaseType.MYSQL]: 'postgresql',
|
||||||
|
[DatabaseType.MARIADB]: 'postgresql',
|
||||||
|
[DatabaseType.GENERIC]: 'postgresql',
|
||||||
|
[DatabaseType.SQLITE]: 'postgresql',
|
||||||
|
[DatabaseType.SQL_SERVER]: 'postgresql',
|
||||||
|
[DatabaseType.CLICKHOUSE]: 'postgresql',
|
||||||
|
[DatabaseType.COCKROACHDB]: 'postgresql',
|
||||||
|
[DatabaseType.ORACLE]: 'postgresql',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createDependenciesFromMetadata = async ({
|
||||||
|
views,
|
||||||
|
tables,
|
||||||
|
databaseType,
|
||||||
|
}: {
|
||||||
|
views: ViewInfo[];
|
||||||
|
tables: DBTable[];
|
||||||
|
databaseType: DatabaseType;
|
||||||
|
}): Promise<DBDependency[]> => {
|
||||||
|
if (!views || views.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { Parser } = await import('node-sql-parser');
|
||||||
|
const parser = new Parser();
|
||||||
|
|
||||||
|
const dependencies = views
|
||||||
|
.flatMap((view) => {
|
||||||
|
const viewSchema = schemaNameToDomainSchemaName(view.schema);
|
||||||
|
const viewTable = tables.find(
|
||||||
|
(table) =>
|
||||||
|
table.name === view.view_name && viewSchema === table.schema
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!viewTable) {
|
||||||
|
console.warn(
|
||||||
|
`Source table for view ${view.view_name} not found (schema: ${viewSchema})`
|
||||||
|
);
|
||||||
|
return []; // Skip this view and proceed to the next
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view.view_definition) {
|
||||||
|
try {
|
||||||
|
const decodedViewDefinition = decodeViewDefinition(
|
||||||
|
databaseType,
|
||||||
|
view.view_definition
|
||||||
|
);
|
||||||
|
|
||||||
|
let modifiedViewDefinition = '';
|
||||||
|
if (
|
||||||
|
databaseType === DatabaseType.MYSQL ||
|
||||||
|
databaseType === DatabaseType.MARIADB
|
||||||
|
) {
|
||||||
|
modifiedViewDefinition = preprocessViewDefinitionMySQL(
|
||||||
|
decodedViewDefinition
|
||||||
|
);
|
||||||
|
} else if (databaseType === DatabaseType.SQL_SERVER) {
|
||||||
|
modifiedViewDefinition =
|
||||||
|
preprocessViewDefinitionSQLServer(
|
||||||
|
decodedViewDefinition
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
modifiedViewDefinition = preprocessViewDefinition(
|
||||||
|
decodedViewDefinition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse using the appropriate dialect
|
||||||
|
const ast = parser.astify(modifiedViewDefinition, {
|
||||||
|
database: astDatabaseTypes[databaseType],
|
||||||
|
type: 'select', // Parsing a SELECT statement
|
||||||
|
});
|
||||||
|
|
||||||
|
let relatedTables = extractTablesFromAST(ast);
|
||||||
|
|
||||||
|
// Filter out duplicate tables without schema
|
||||||
|
relatedTables = filterDuplicateTables(relatedTables);
|
||||||
|
|
||||||
|
return relatedTables.map((relTable) => {
|
||||||
|
const relSchema = relTable.schema || view.schema; // Use view's schema if relSchema is undefined
|
||||||
|
const relTableName = relTable.tableName;
|
||||||
|
|
||||||
|
const table = tables.find(
|
||||||
|
(table) =>
|
||||||
|
table.name === relTableName &&
|
||||||
|
(table.schema || '') === relSchema
|
||||||
|
);
|
||||||
|
|
||||||
|
if (table) {
|
||||||
|
const dependency: DBDependency = {
|
||||||
|
id: generateId(),
|
||||||
|
schema: view.schema,
|
||||||
|
tableId: table.id, // related table
|
||||||
|
dependentSchema: table.schema,
|
||||||
|
dependentTableId: viewTable.id, // dependent view
|
||||||
|
createdAt: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return dependency;
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Dependent table ${relSchema}.${relTableName} not found for view ${view.schema}.${view.view_name}`
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Error parsing view ${view.schema}.${view.view_name}:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`View definition missing for ${view.schema}.${view.view_name}`
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((dependency) => dependency !== null);
|
||||||
|
|
||||||
|
return dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add this new function to filter out duplicate tables
|
||||||
|
function filterDuplicateTables(
|
||||||
|
tables: { schema?: string; tableName: string }[]
|
||||||
|
): { schema?: string; tableName: string }[] {
|
||||||
|
const tableMap = new Map<string, { schema?: string; tableName: string }>();
|
||||||
|
|
||||||
|
for (const table of tables) {
|
||||||
|
const key = table.tableName;
|
||||||
|
const existingTable = tableMap.get(key);
|
||||||
|
|
||||||
|
if (!existingTable || (table.schema && !existingTable.schema)) {
|
||||||
|
tableMap.set(key, table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(tableMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preprocess the view_definition to remove schema from CREATE VIEW
|
||||||
|
function preprocessViewDefinition(viewDefinition: string): string {
|
||||||
|
if (!viewDefinition) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove leading and trailing whitespace
|
||||||
|
viewDefinition = viewDefinition.replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
|
// Replace escaped double quotes with regular ones
|
||||||
|
viewDefinition = viewDefinition.replace(/\\"/g, '"');
|
||||||
|
|
||||||
|
// Replace 'CREATE MATERIALIZED VIEW' with 'CREATE VIEW'
|
||||||
|
viewDefinition = viewDefinition.replace(
|
||||||
|
/CREATE\s+MATERIALIZED\s+VIEW/i,
|
||||||
|
'CREATE VIEW'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Regular expression to match 'CREATE VIEW [schema.]view_name [ (column definitions) ] AS'
|
||||||
|
// This regex captures the view name and skips any content between the view name and 'AS'
|
||||||
|
const regex =
|
||||||
|
/CREATE\s+VIEW\s+(?:(?:`[^`]+`|"[^"]+"|\w+)\.)?(?:`([^`]+)`|"([^"]+)"|(\w+))[\s\S]*?\bAS\b\s+/i;
|
||||||
|
|
||||||
|
const match = viewDefinition.match(regex);
|
||||||
|
let modifiedDefinition: string;
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const viewName = match[1] || match[2] || match[3];
|
||||||
|
// Extract the SQL after the 'AS' keyword
|
||||||
|
const restOfDefinition = viewDefinition.substring(
|
||||||
|
match.index! + match[0].length
|
||||||
|
);
|
||||||
|
|
||||||
|
// Replace double-quoted identifiers with unquoted ones
|
||||||
|
let modifiedSQL = restOfDefinition.replace(/"(\w+)"/g, '$1');
|
||||||
|
|
||||||
|
// Replace '::' type casts with 'CAST' expressions
|
||||||
|
modifiedSQL = modifiedSQL.replace(
|
||||||
|
/\(([^()]+)\)::(\w+)/g,
|
||||||
|
'CAST($1 AS $2)'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove ClickHouse-specific syntax that may still be present
|
||||||
|
// For example, remove SETTINGS clauses inside the SELECT statement
|
||||||
|
modifiedSQL = modifiedSQL.replace(/\bSETTINGS\b[\s\S]*$/i, '');
|
||||||
|
|
||||||
|
modifiedDefinition = `CREATE VIEW ${viewName} AS ${modifiedSQL}`;
|
||||||
|
} else {
|
||||||
|
console.warn('Could not preprocess view definition:', viewDefinition);
|
||||||
|
modifiedDefinition = viewDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preprocess the view_definition for SQL Server
|
||||||
|
function preprocessViewDefinitionSQLServer(viewDefinition: string): string {
|
||||||
|
if (!viewDefinition) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove BOM if present
|
||||||
|
viewDefinition = viewDefinition.replace(/^\uFEFF/, '');
|
||||||
|
|
||||||
|
// Normalize whitespace
|
||||||
|
viewDefinition = viewDefinition.replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
|
// Remove square brackets and replace with double quotes
|
||||||
|
viewDefinition = viewDefinition.replace(/\[([^\]]+)\]/g, '"$1"');
|
||||||
|
|
||||||
|
// Remove database names from fully qualified identifiers
|
||||||
|
viewDefinition = viewDefinition.replace(
|
||||||
|
/"([a-zA-Z0-9_]+)"\."([a-zA-Z0-9_]+)"\."([a-zA-Z0-9_]+)"/g,
|
||||||
|
'"$2"."$3"'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Replace SQL Server functions with PostgreSQL equivalents
|
||||||
|
viewDefinition = viewDefinition.replace(/\bGETDATE\(\)/gi, 'NOW()');
|
||||||
|
viewDefinition = viewDefinition.replace(/\bISNULL\(/gi, 'COALESCE(');
|
||||||
|
|
||||||
|
// Replace 'TOP N' with 'LIMIT N' at the end of the query
|
||||||
|
const topMatch = viewDefinition.match(/SELECT\s+TOP\s+(\d+)/i);
|
||||||
|
if (topMatch) {
|
||||||
|
const topN = topMatch[1];
|
||||||
|
viewDefinition = viewDefinition.replace(
|
||||||
|
/SELECT\s+TOP\s+\d+/i,
|
||||||
|
'SELECT'
|
||||||
|
);
|
||||||
|
viewDefinition = viewDefinition.replace(/;+\s*$/, ''); // Remove semicolons at the end
|
||||||
|
viewDefinition += ` LIMIT ${topN}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewDefinition = viewDefinition.replace(/\n/g, ''); // Remove newlines
|
||||||
|
|
||||||
|
// Adjust CREATE VIEW syntax
|
||||||
|
const regex =
|
||||||
|
/CREATE\s+VIEW\s+(?:"?([^".\s]+)"?\.)?"?([^".\s]+)"?\s+AS\s+/i;
|
||||||
|
const match = viewDefinition.match(regex);
|
||||||
|
let modifiedDefinition: string;
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const viewName = match[2];
|
||||||
|
const modifiedSQL = viewDefinition.substring(
|
||||||
|
match.index! + match[0].length
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove semicolons at the end
|
||||||
|
const finalSQL = modifiedSQL.replace(/;+\s*$/, '');
|
||||||
|
|
||||||
|
modifiedDefinition = `CREATE VIEW "${viewName}" AS ${finalSQL}`;
|
||||||
|
} else {
|
||||||
|
console.warn('Could not preprocess view definition:', viewDefinition);
|
||||||
|
modifiedDefinition = viewDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preprocess the view_definition to remove schema from CREATE VIEW
|
||||||
|
function preprocessViewDefinitionMySQL(viewDefinition: string): string {
|
||||||
|
if (!viewDefinition) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any trailing semicolons
|
||||||
|
viewDefinition = viewDefinition.replace(/;\s*$/, '');
|
||||||
|
|
||||||
|
// Remove backticks from identifiers
|
||||||
|
viewDefinition = viewDefinition.replace(/`/g, '');
|
||||||
|
|
||||||
|
// Remove unnecessary parentheses around joins and ON clauses
|
||||||
|
viewDefinition = removeRedundantParentheses(viewDefinition);
|
||||||
|
|
||||||
|
return viewDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRedundantParentheses(sql: string): string {
|
||||||
|
// Regular expressions to match unnecessary parentheses
|
||||||
|
const patterns = [
|
||||||
|
/\(\s*(JOIN\s+[^()]+?)\s*\)/gi,
|
||||||
|
/\(\s*(ON\s+[^()]+?)\s*\)/gi,
|
||||||
|
// Additional patterns if necessary
|
||||||
|
];
|
||||||
|
|
||||||
|
let prevSql;
|
||||||
|
do {
|
||||||
|
prevSql = sql;
|
||||||
|
patterns.forEach((pattern) => {
|
||||||
|
sql = sql.replace(pattern, '$1');
|
||||||
|
});
|
||||||
|
} while (sql !== prevSql);
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractTablesFromAST(
|
||||||
|
ast: AST | AST[]
|
||||||
|
): { schema?: string; tableName: string }[] {
|
||||||
|
const tablesMap = new Map<string, { schema: string; tableName: string }>();
|
||||||
|
const visitedNodes = new Set();
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
function traverse(node: any) {
|
||||||
|
if (!node || visitedNodes.has(node)) return;
|
||||||
|
visitedNodes.add(node);
|
||||||
|
|
||||||
|
if (Array.isArray(node)) {
|
||||||
|
node.forEach(traverse);
|
||||||
|
} else if (typeof node === 'object') {
|
||||||
|
// Check if node represents a table
|
||||||
|
if (
|
||||||
|
Object.hasOwnProperty.call(node, 'table') &&
|
||||||
|
typeof node.table === 'string'
|
||||||
|
) {
|
||||||
|
let schema = node.db || node.schema;
|
||||||
|
const tableName = node.table;
|
||||||
|
if (tableName) {
|
||||||
|
// Assign default schema if undefined
|
||||||
|
schema = schemaNameToDomainSchemaName(schema) || '';
|
||||||
|
const key = `${schema}.${tableName}`;
|
||||||
|
if (!tablesMap.has(key)) {
|
||||||
|
tablesMap.set(key, { schema, tableName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively traverse all properties
|
||||||
|
for (const key in node) {
|
||||||
|
if (Object.hasOwnProperty.call(node, key)) {
|
||||||
|
traverse(node[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(ast);
|
||||||
|
|
||||||
|
return Array.from(tablesMap.values());
|
||||||
|
}
|
||||||
68
src/lib/data/import-metadata/import/fields.ts
Normal file
68
src/lib/data/import-metadata/import/fields.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import type { DBField } from '@/lib/domain';
|
||||||
|
import type { ColumnInfo } from '../metadata-types/column-info';
|
||||||
|
import type { AggregatedIndexInfo } from '../metadata-types/index-info';
|
||||||
|
import type { PrimaryKeyInfo } from '../metadata-types/primary-key-info';
|
||||||
|
import type { TableInfo } from '../metadata-types/table-info';
|
||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
|
||||||
|
export const createFieldsFromMetadata = ({
|
||||||
|
tableColumns,
|
||||||
|
tablePrimaryKeys,
|
||||||
|
aggregatedIndexes,
|
||||||
|
}: {
|
||||||
|
tableColumns: ColumnInfo[];
|
||||||
|
tableSchema?: string;
|
||||||
|
tableInfo: TableInfo;
|
||||||
|
tablePrimaryKeys: PrimaryKeyInfo[];
|
||||||
|
aggregatedIndexes: AggregatedIndexInfo[];
|
||||||
|
}) => {
|
||||||
|
const uniqueColumns = tableColumns.reduce((acc, col) => {
|
||||||
|
if (!acc.has(col.name)) {
|
||||||
|
acc.set(col.name, col);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, new Map<string, ColumnInfo>());
|
||||||
|
|
||||||
|
const sortedColumns = Array.from(uniqueColumns.values()).sort(
|
||||||
|
(a, b) => a.ordinal_position - b.ordinal_position
|
||||||
|
);
|
||||||
|
|
||||||
|
const tablePrimaryKeysColumns = tablePrimaryKeys.map((pk) =>
|
||||||
|
pk.column.trim()
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortedColumns.map(
|
||||||
|
(col: ColumnInfo): DBField => ({
|
||||||
|
id: generateId(),
|
||||||
|
name: col.name,
|
||||||
|
type: {
|
||||||
|
id: col.type.split(' ').join('_').toLowerCase(),
|
||||||
|
name: col.type.toLowerCase(),
|
||||||
|
},
|
||||||
|
primaryKey: tablePrimaryKeysColumns.includes(col.name),
|
||||||
|
unique: Object.values(aggregatedIndexes).some(
|
||||||
|
(idx) =>
|
||||||
|
idx.unique &&
|
||||||
|
idx.columns.length === 1 &&
|
||||||
|
idx.columns[0].name === col.name
|
||||||
|
),
|
||||||
|
nullable: Boolean(col.nullable),
|
||||||
|
...(col.character_maximum_length &&
|
||||||
|
col.character_maximum_length !== 'null'
|
||||||
|
? { characterMaximumLength: col.character_maximum_length }
|
||||||
|
: {}),
|
||||||
|
...(col.precision?.precision
|
||||||
|
? { precision: col.precision.precision }
|
||||||
|
: {}),
|
||||||
|
...(col.precision?.scale ? { scale: col.precision.scale } : {}),
|
||||||
|
...(col.default ? { default: col.default } : {}),
|
||||||
|
...(col.collation ? { collation: col.collation } : {}),
|
||||||
|
...(col.is_identity !== undefined
|
||||||
|
? { increment: col.is_identity }
|
||||||
|
: {}),
|
||||||
|
...(col.is_array !== undefined ? { isArray: col.is_array } : {}),
|
||||||
|
createdAt: Date.now(),
|
||||||
|
comments: col.comment ? col.comment : undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
82
src/lib/data/import-metadata/import/index.ts
Normal file
82
src/lib/data/import-metadata/import/index.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import type { DatabaseEdition, Diagram } from '@/lib/domain';
|
||||||
|
import { adjustTablePositions, DatabaseType } from '@/lib/domain';
|
||||||
|
import { generateDiagramId } from '@/lib/utils';
|
||||||
|
import type { DatabaseMetadata } from '../metadata-types/database-metadata';
|
||||||
|
import { createCustomTypesFromMetadata } from './custom-types';
|
||||||
|
import { createRelationshipsFromMetadata } from './relationships';
|
||||||
|
import { createTablesFromMetadata } from './tables';
|
||||||
|
import { createDependenciesFromMetadata } from './dependencies';
|
||||||
|
|
||||||
|
export const loadFromDatabaseMetadata = async ({
|
||||||
|
databaseType,
|
||||||
|
databaseMetadata,
|
||||||
|
diagramNumber,
|
||||||
|
databaseEdition,
|
||||||
|
}: {
|
||||||
|
databaseType: DatabaseType;
|
||||||
|
databaseMetadata: DatabaseMetadata;
|
||||||
|
diagramNumber?: number;
|
||||||
|
databaseEdition?: DatabaseEdition;
|
||||||
|
}): Promise<Diagram> => {
|
||||||
|
const {
|
||||||
|
fk_info: foreignKeys,
|
||||||
|
views: views,
|
||||||
|
custom_types: customTypes,
|
||||||
|
} = databaseMetadata;
|
||||||
|
|
||||||
|
const tables = createTablesFromMetadata({
|
||||||
|
databaseMetadata,
|
||||||
|
databaseType,
|
||||||
|
});
|
||||||
|
|
||||||
|
const relationships = createRelationshipsFromMetadata({
|
||||||
|
foreignKeys,
|
||||||
|
tables,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dependencies = await createDependenciesFromMetadata({
|
||||||
|
views,
|
||||||
|
tables,
|
||||||
|
databaseType,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dbCustomTypes = customTypes
|
||||||
|
? createCustomTypesFromMetadata({
|
||||||
|
customTypes,
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const adjustedTables = adjustTablePositions({
|
||||||
|
tables,
|
||||||
|
relationships,
|
||||||
|
mode: 'perSchema',
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortedTables = adjustedTables.sort((a, b) => {
|
||||||
|
if (a.isView === b.isView) {
|
||||||
|
// Both are either tables or views, so sort alphabetically by name
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
}
|
||||||
|
// If one is a view and the other is not, put tables first
|
||||||
|
return a.isView ? 1 : -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const diagram: Diagram = {
|
||||||
|
id: generateDiagramId(),
|
||||||
|
name: databaseMetadata.database_name
|
||||||
|
? `${databaseMetadata.database_name}`
|
||||||
|
: diagramNumber
|
||||||
|
? `Diagram ${diagramNumber}`
|
||||||
|
: 'New Diagram',
|
||||||
|
databaseType: databaseType ?? DatabaseType.GENERIC,
|
||||||
|
databaseEdition,
|
||||||
|
tables: sortedTables,
|
||||||
|
relationships,
|
||||||
|
dependencies,
|
||||||
|
customTypes: dbCustomTypes,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return diagram;
|
||||||
|
};
|
||||||
24
src/lib/data/import-metadata/import/indexes.ts
Normal file
24
src/lib/data/import-metadata/import/indexes.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import type { DBField, DBIndex, IndexType } from '@/lib/domain';
|
||||||
|
import type { AggregatedIndexInfo } from '../metadata-types/index-info';
|
||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
|
||||||
|
export const createIndexesFromMetadata = ({
|
||||||
|
aggregatedIndexes,
|
||||||
|
fields,
|
||||||
|
}: {
|
||||||
|
aggregatedIndexes: AggregatedIndexInfo[];
|
||||||
|
fields: DBField[];
|
||||||
|
}): DBIndex[] =>
|
||||||
|
aggregatedIndexes.map(
|
||||||
|
(idx): DBIndex => ({
|
||||||
|
id: generateId(),
|
||||||
|
name: idx.name,
|
||||||
|
unique: Boolean(idx.unique),
|
||||||
|
fieldIds: idx.columns
|
||||||
|
.sort((a, b) => a.position - b.position)
|
||||||
|
.map((c) => fields.find((f) => f.name === c.name)?.id)
|
||||||
|
.filter((id): id is string => id !== undefined),
|
||||||
|
createdAt: Date.now(),
|
||||||
|
type: idx.index_type?.toLowerCase() as IndexType,
|
||||||
|
})
|
||||||
|
);
|
||||||
85
src/lib/data/import-metadata/import/relationships.ts
Normal file
85
src/lib/data/import-metadata/import/relationships.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import type {
|
||||||
|
Cardinality,
|
||||||
|
DBField,
|
||||||
|
DBRelationship,
|
||||||
|
DBTable,
|
||||||
|
} from '@/lib/domain';
|
||||||
|
import { schemaNameToDomainSchemaName } from '@/lib/domain';
|
||||||
|
import type { ForeignKeyInfo } from '../metadata-types/foreign-key-info';
|
||||||
|
import { generateId } from '@/lib/utils';
|
||||||
|
|
||||||
|
const determineCardinality = (
|
||||||
|
field: DBField,
|
||||||
|
isTablePKComplex: boolean
|
||||||
|
): Cardinality => {
|
||||||
|
return field.unique || (field.primaryKey && !isTablePKComplex)
|
||||||
|
? 'one'
|
||||||
|
: 'many';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createRelationshipsFromMetadata = ({
|
||||||
|
foreignKeys,
|
||||||
|
tables,
|
||||||
|
}: {
|
||||||
|
foreignKeys: ForeignKeyInfo[];
|
||||||
|
tables: DBTable[];
|
||||||
|
}): DBRelationship[] => {
|
||||||
|
return foreignKeys
|
||||||
|
.map((fk: ForeignKeyInfo): DBRelationship | null => {
|
||||||
|
const schema = schemaNameToDomainSchemaName(fk.schema);
|
||||||
|
const sourceTable = tables.find(
|
||||||
|
(table) => table.name === fk.table && table.schema === schema
|
||||||
|
);
|
||||||
|
|
||||||
|
const targetSchema = schemaNameToDomainSchemaName(
|
||||||
|
fk.reference_schema
|
||||||
|
);
|
||||||
|
|
||||||
|
const targetTable = tables.find(
|
||||||
|
(table) =>
|
||||||
|
table.name === fk.reference_table &&
|
||||||
|
table.schema === targetSchema
|
||||||
|
);
|
||||||
|
const sourceField = sourceTable?.fields.find(
|
||||||
|
(field) => field.name === fk.column
|
||||||
|
);
|
||||||
|
const targetField = targetTable?.fields.find(
|
||||||
|
(field) => field.name === fk.reference_column
|
||||||
|
);
|
||||||
|
|
||||||
|
const isSourceTablePKComplex =
|
||||||
|
(sourceTable?.fields.filter((field) => field.primaryKey) ?? [])
|
||||||
|
.length > 1;
|
||||||
|
const isTargetTablePKComplex =
|
||||||
|
(targetTable?.fields.filter((field) => field.primaryKey) ?? [])
|
||||||
|
.length > 1;
|
||||||
|
|
||||||
|
if (sourceTable && targetTable && sourceField && targetField) {
|
||||||
|
const sourceCardinality = determineCardinality(
|
||||||
|
sourceField,
|
||||||
|
isSourceTablePKComplex
|
||||||
|
);
|
||||||
|
const targetCardinality = determineCardinality(
|
||||||
|
targetField,
|
||||||
|
isTargetTablePKComplex
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: generateId(),
|
||||||
|
name: fk.foreign_key_name,
|
||||||
|
sourceSchema: schema,
|
||||||
|
targetSchema: targetSchema,
|
||||||
|
sourceTableId: sourceTable.id,
|
||||||
|
targetTableId: targetTable.id,
|
||||||
|
sourceFieldId: sourceField.id,
|
||||||
|
targetFieldId: targetField.id,
|
||||||
|
sourceCardinality,
|
||||||
|
targetCardinality,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((rel) => rel !== null) as DBRelationship[];
|
||||||
|
};
|
||||||
228
src/lib/data/import-metadata/import/tables.ts
Normal file
228
src/lib/data/import-metadata/import/tables.ts
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import type { DBIndex, DBTable } from '@/lib/domain';
|
||||||
|
import {
|
||||||
|
DatabaseType,
|
||||||
|
generateTableKey,
|
||||||
|
schemaNameToDomainSchemaName,
|
||||||
|
} from '@/lib/domain';
|
||||||
|
import type { DatabaseMetadata } from '../metadata-types/database-metadata';
|
||||||
|
import type { TableInfo } from '../metadata-types/table-info';
|
||||||
|
import { createAggregatedIndexes } from '../metadata-types/index-info';
|
||||||
|
import {
|
||||||
|
decodeBase64ToUtf16LE,
|
||||||
|
decodeBase64ToUtf8,
|
||||||
|
generateId,
|
||||||
|
} from '@/lib/utils';
|
||||||
|
import {
|
||||||
|
defaultTableColor,
|
||||||
|
materializedViewColor,
|
||||||
|
viewColor,
|
||||||
|
} from '@/lib/colors';
|
||||||
|
import { createFieldsFromMetadata } from './fields';
|
||||||
|
import { createIndexesFromMetadata } from './indexes';
|
||||||
|
|
||||||
|
export const decodeViewDefinition = (
|
||||||
|
databaseType: DatabaseType,
|
||||||
|
viewDefinition?: string
|
||||||
|
): string => {
|
||||||
|
if (!viewDefinition) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let decodedViewDefinition: string;
|
||||||
|
if (databaseType === DatabaseType.SQL_SERVER) {
|
||||||
|
decodedViewDefinition = decodeBase64ToUtf16LE(viewDefinition);
|
||||||
|
} else {
|
||||||
|
decodedViewDefinition = decodeBase64ToUtf8(viewDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodedViewDefinition;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTablesFromMetadata = ({
|
||||||
|
databaseMetadata,
|
||||||
|
databaseType,
|
||||||
|
}: {
|
||||||
|
databaseMetadata: DatabaseMetadata;
|
||||||
|
databaseType: DatabaseType;
|
||||||
|
}): DBTable[] => {
|
||||||
|
const {
|
||||||
|
tables: tableInfos,
|
||||||
|
pk_info: primaryKeys,
|
||||||
|
columns,
|
||||||
|
indexes,
|
||||||
|
views: views,
|
||||||
|
} = databaseMetadata;
|
||||||
|
|
||||||
|
// Pre-compute view names for faster lookup if there are views
|
||||||
|
const viewNamesSet = new Set<string>();
|
||||||
|
const materializedViewNamesSet = new Set<string>();
|
||||||
|
|
||||||
|
if (views && views.length > 0) {
|
||||||
|
views.forEach((view) => {
|
||||||
|
const key = generateTableKey({
|
||||||
|
schemaName: view.schema,
|
||||||
|
tableName: view.view_name,
|
||||||
|
});
|
||||||
|
viewNamesSet.add(key);
|
||||||
|
|
||||||
|
if (
|
||||||
|
view.view_definition &&
|
||||||
|
decodeViewDefinition(databaseType, view.view_definition)
|
||||||
|
.toLowerCase()
|
||||||
|
.includes('materialized')
|
||||||
|
) {
|
||||||
|
materializedViewNamesSet.add(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-compute lookup maps for better performance
|
||||||
|
const columnsByTable = new Map<string, (typeof columns)[0][]>();
|
||||||
|
const indexesByTable = new Map<string, (typeof indexes)[0][]>();
|
||||||
|
const primaryKeysByTable = new Map<string, (typeof primaryKeys)[0][]>();
|
||||||
|
|
||||||
|
// Group columns by table
|
||||||
|
columns.forEach((col) => {
|
||||||
|
const key = generateTableKey({
|
||||||
|
schemaName: col.schema,
|
||||||
|
tableName: col.table,
|
||||||
|
});
|
||||||
|
if (!columnsByTable.has(key)) {
|
||||||
|
columnsByTable.set(key, []);
|
||||||
|
}
|
||||||
|
columnsByTable.get(key)!.push(col);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group indexes by table
|
||||||
|
indexes.forEach((idx) => {
|
||||||
|
const key = generateTableKey({
|
||||||
|
schemaName: idx.schema,
|
||||||
|
tableName: idx.table,
|
||||||
|
});
|
||||||
|
if (!indexesByTable.has(key)) {
|
||||||
|
indexesByTable.set(key, []);
|
||||||
|
}
|
||||||
|
indexesByTable.get(key)!.push(idx);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group primary keys by table
|
||||||
|
primaryKeys.forEach((pk) => {
|
||||||
|
const key = generateTableKey({
|
||||||
|
schemaName: pk.schema,
|
||||||
|
tableName: pk.table,
|
||||||
|
});
|
||||||
|
if (!primaryKeysByTable.has(key)) {
|
||||||
|
primaryKeysByTable.set(key, []);
|
||||||
|
}
|
||||||
|
primaryKeysByTable.get(key)!.push(pk);
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = tableInfos.map((tableInfo: TableInfo) => {
|
||||||
|
const tableSchema = schemaNameToDomainSchemaName(tableInfo.schema);
|
||||||
|
const tableKey = generateTableKey({
|
||||||
|
schemaName: tableInfo.schema,
|
||||||
|
tableName: tableInfo.table,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use pre-computed lookups instead of filtering entire arrays
|
||||||
|
const tableIndexes = indexesByTable.get(tableKey) || [];
|
||||||
|
const tablePrimaryKeys = primaryKeysByTable.get(tableKey) || [];
|
||||||
|
const tableColumns = columnsByTable.get(tableKey) || [];
|
||||||
|
|
||||||
|
// Aggregate indexes with multiple columns
|
||||||
|
const aggregatedIndexes = createAggregatedIndexes({
|
||||||
|
tableInfo,
|
||||||
|
tableSchema,
|
||||||
|
tableIndexes,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fields = createFieldsFromMetadata({
|
||||||
|
aggregatedIndexes,
|
||||||
|
tableColumns,
|
||||||
|
tablePrimaryKeys,
|
||||||
|
tableInfo,
|
||||||
|
tableSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for composite primary key and find matching index name
|
||||||
|
const primaryKeyFields = fields.filter((f) => f.primaryKey);
|
||||||
|
let pkMatchingIndexName: string | undefined;
|
||||||
|
let pkIndex: DBIndex | undefined;
|
||||||
|
|
||||||
|
if (primaryKeyFields.length >= 1) {
|
||||||
|
// We have a composite primary key, look for an index that matches all PK columns
|
||||||
|
const pkFieldNames = primaryKeyFields.map((f) => f.name).sort();
|
||||||
|
|
||||||
|
// Find an index that matches the primary key columns exactly
|
||||||
|
const matchingIndex = aggregatedIndexes.find((index) => {
|
||||||
|
const indexColumnNames = index.columns
|
||||||
|
.map((c) => c.name)
|
||||||
|
.sort();
|
||||||
|
return (
|
||||||
|
indexColumnNames.length === pkFieldNames.length &&
|
||||||
|
indexColumnNames.every((col, i) => col === pkFieldNames[i])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchingIndex) {
|
||||||
|
pkMatchingIndexName = matchingIndex.name;
|
||||||
|
// Create a special PK index
|
||||||
|
pkIndex = {
|
||||||
|
id: generateId(),
|
||||||
|
name: matchingIndex.name,
|
||||||
|
unique: true,
|
||||||
|
fieldIds: primaryKeyFields.map((f) => f.id),
|
||||||
|
createdAt: Date.now(),
|
||||||
|
isPrimaryKey: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out the index that matches the composite PK (to avoid duplication)
|
||||||
|
const filteredAggregatedIndexes = pkMatchingIndexName
|
||||||
|
? aggregatedIndexes.filter(
|
||||||
|
(idx) => idx.name !== pkMatchingIndexName
|
||||||
|
)
|
||||||
|
: aggregatedIndexes;
|
||||||
|
|
||||||
|
const dbIndexes = createIndexesFromMetadata({
|
||||||
|
aggregatedIndexes: filteredAggregatedIndexes,
|
||||||
|
fields,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the PK index if it exists
|
||||||
|
if (pkIndex) {
|
||||||
|
dbIndexes.push(pkIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the current table is a view by checking against pre-computed sets
|
||||||
|
const viewKey = generateTableKey({
|
||||||
|
schemaName: tableSchema,
|
||||||
|
tableName: tableInfo.table,
|
||||||
|
});
|
||||||
|
const isView = viewNamesSet.has(viewKey);
|
||||||
|
const isMaterializedView = materializedViewNamesSet.has(viewKey);
|
||||||
|
|
||||||
|
// Initial random positions; these will be adjusted later
|
||||||
|
return {
|
||||||
|
id: generateId(),
|
||||||
|
name: tableInfo.table,
|
||||||
|
schema: tableSchema,
|
||||||
|
x: Math.random() * 1000, // Placeholder X
|
||||||
|
y: Math.random() * 800, // Placeholder Y
|
||||||
|
fields,
|
||||||
|
indexes: dbIndexes,
|
||||||
|
color: isMaterializedView
|
||||||
|
? materializedViewColor
|
||||||
|
: isView
|
||||||
|
? viewColor
|
||||||
|
: defaultTableColor,
|
||||||
|
isView: isView,
|
||||||
|
isMaterializedView: isMaterializedView,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
comments: tableInfo.comment ? tableInfo.comment : undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
@@ -15,6 +15,8 @@ export interface ColumnInfo {
|
|||||||
default?: string | null; // Default value for the column, nullable
|
default?: string | null; // Default value for the column, nullable
|
||||||
collation?: string | null;
|
collation?: string | null;
|
||||||
comment?: string | null;
|
comment?: string | null;
|
||||||
|
is_identity?: boolean | null; // Indicates if the column is auto-increment/identity
|
||||||
|
is_array?: boolean | null; // Indicates if the column is an array type
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ColumnInfoSchema: z.ZodType<ColumnInfo> = z.object({
|
export const ColumnInfoSchema: z.ZodType<ColumnInfo> = z.object({
|
||||||
@@ -35,4 +37,6 @@ export const ColumnInfoSchema: z.ZodType<ColumnInfo> = z.object({
|
|||||||
default: z.string().nullable().optional(),
|
default: z.string().nullable().optional(),
|
||||||
collation: z.string().nullable().optional(),
|
collation: z.string().nullable().optional(),
|
||||||
comment: z.string().nullable().optional(),
|
comment: z.string().nullable().optional(),
|
||||||
|
is_identity: z.boolean().nullable().optional(),
|
||||||
|
is_array: z.boolean().nullable().optional(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -127,7 +127,13 @@ cols AS (
|
|||||||
',"default":"', null,
|
',"default":"', null,
|
||||||
'","collation":"', COALESCE(cols.COLLATION_NAME::TEXT, ''),
|
'","collation":"', COALESCE(cols.COLLATION_NAME::TEXT, ''),
|
||||||
'","comment":"', COALESCE(replace(replace(dsc.description::TEXT, '"', '\\"'), '\\x', '\\\\x'), ''),
|
'","comment":"', COALESCE(replace(replace(dsc.description::TEXT, '"', '\\"'), '\\x', '\\\\x'), ''),
|
||||||
'"}')), ',') AS cols_metadata
|
'","is_identity":', CASE
|
||||||
|
WHEN cols.is_identity = 'YES' THEN 'true'
|
||||||
|
WHEN cols.column_default IS NOT NULL AND cols.column_default LIKE 'nextval(%' THEN 'true'
|
||||||
|
WHEN cols.column_default LIKE 'unique_rowid()%' THEN 'true'
|
||||||
|
ELSE 'false'
|
||||||
|
END,
|
||||||
|
'}')), ',') AS cols_metadata
|
||||||
FROM information_schema.columns cols
|
FROM information_schema.columns cols
|
||||||
LEFT JOIN pg_catalog.pg_class c
|
LEFT JOIN pg_catalog.pg_class c
|
||||||
ON c.relname = cols.table_name
|
ON c.relname = cols.table_name
|
||||||
|
|||||||
@@ -69,7 +69,9 @@ SELECT CAST(CONCAT(
|
|||||||
',"ordinal_position":', cols.ordinal_position,
|
',"ordinal_position":', cols.ordinal_position,
|
||||||
',"nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
|
',"nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
|
||||||
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
||||||
'","collation":"', IFNULL(cols.collation_name, ''), '"}')
|
'","collation":"', IFNULL(cols.collation_name, ''),
|
||||||
|
'","is_identity":', IF(cols.extra LIKE '%auto_increment%', 'true', 'false'),
|
||||||
|
'"}')
|
||||||
) FROM (
|
) FROM (
|
||||||
SELECT cols.table_schema,
|
SELECT cols.table_schema,
|
||||||
cols.table_name,
|
cols.table_name,
|
||||||
@@ -81,7 +83,8 @@ SELECT CAST(CONCAT(
|
|||||||
cols.ordinal_position,
|
cols.ordinal_position,
|
||||||
cols.is_nullable,
|
cols.is_nullable,
|
||||||
cols.column_default,
|
cols.column_default,
|
||||||
cols.collation_name
|
cols.collation_name,
|
||||||
|
cols.extra
|
||||||
FROM information_schema.columns cols
|
FROM information_schema.columns cols
|
||||||
WHERE cols.table_schema = DATABASE()
|
WHERE cols.table_schema = DATABASE()
|
||||||
) AS cols), ''),
|
) AS cols), ''),
|
||||||
|
|||||||
@@ -92,7 +92,9 @@ export const getMySQLQuery = (
|
|||||||
',"ordinal_position":', cols.ordinal_position,
|
',"ordinal_position":', cols.ordinal_position,
|
||||||
',"nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
|
',"nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
|
||||||
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
',"default":"', ${withExtras ? withDefault : withoutDefault},
|
||||||
'","collation":"', IFNULL(cols.collation_name, ''), '"}'
|
'","collation":"', IFNULL(cols.collation_name, ''),
|
||||||
|
'","is_identity":', IF(cols.extra LIKE '%auto_increment%', 'true', 'false'),
|
||||||
|
'}'
|
||||||
)))))
|
)))))
|
||||||
), indexes as (
|
), indexes as (
|
||||||
(SELECT (@indexes:=NULL),
|
(SELECT (@indexes:=NULL),
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user