Browse Source

git commit -m "HUE-9381 [ui] Add limits and ellipsis to ERD component (sree)

- Limit number of columns to 10, remaining will be grouped to one dummy column
- Links to/from grouped columns if any, must be connected to the dummy column
- Add ellipsis for very long table and column names
- Primary key and foreign key columns must be shown at the top
sreenaths 5 years ago
parent
commit
f757edb26d

+ 26 - 3
desktop/core/src/desktop/js/apps/tableBrowser/propsMappers.ts

@@ -18,17 +18,39 @@
 
 import { Table, Column } from '../../components/er-diagram/lib/entities';
 
-function createTable(database: string, name: string, columns: Array<string>) {
+function createTable(database: string, name: string, columnNames: Array<string>) {
+  const tableId: string = Table.buildId(database, name);
+  const columns: Array<Column> = columnNames.map((columnName: string) => {
+    return new Column(tableId, columnName);
+  });
   return new Table(database, name, columns);
 }
 
+function reorderBasedOnKeys(
+  columnNames: Array<string>,
+  primaryKeys: Array<unknown>,
+  foreignKeys: Array<unknown>
+): Array<string> {
+  const keyWeight = {};
+
+  columnNames.forEach((key: unknown) => (keyWeight[key] = 0));
+  foreignKeys.forEach((key: unknown) => (keyWeight[key.name] = 1));
+  primaryKeys.forEach((key: unknown) => (keyWeight[key.name] = 2));
+
+  columnNames.sort((a, b) => keyWeight[b] - keyWeight[a]);
+
+  return columnNames;
+}
+
 function getTables(
   fromDB: string,
   fromTableName: string,
-  columns: Array<string>,
+  columnNames: Array<string>,
+  primaryKeys: Array<unknown>,
   foreignKeys: Array<unknown>
 ) {
-  const fromTable = createTable(fromDB, fromTableName, columns);
+  columnNames = reorderBasedOnKeys(columnNames, primaryKeys, foreignKeys);
+  const fromTable = createTable(fromDB, fromTableName, columnNames);
   const tables = [fromTable];
   const createdTableIdSet = new Set([fromTable.id]);
 
@@ -69,6 +91,7 @@ export function tableERD(catalogEntry: unknown): unknown {
       fromDB,
       fromTable,
       catalogEntry.sourceMeta.columns,
+      catalogEntry.sourceMeta.primary_keys,
       catalogEntry.sourceMeta.foreign_keys
     ),
     relations: getForeignKeyRelations(fromDB, fromTable, catalogEntry.sourceMeta.foreign_keys)

+ 26 - 4
desktop/core/src/desktop/js/components/er-diagram/comps/table-entity.scss

@@ -16,8 +16,17 @@
   limitations under the License.
 */
 
+$border-color: #d6d8db;
+$bg-color: #FFF;
+
+.ellipsis-on-overflow {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
 .table-entity {
-  border: 1px solid #d6d8db;
+  border: 1px solid $border-color;
   border-radius: 5px;
   padding: 5px;
 
@@ -31,26 +40,39 @@
     color: rgb(5, 5, 5);
     font-size: 0.8em;
     text-align: center;
+
+    @extend .ellipsis-on-overflow;
   }
   .table-name {
     color: #0a78a3;
     text-align: center;
     margin-top: -5px;
+
+    @extend .ellipsis-on-overflow;
   }
 
   .columns-container {
     padding: 0 10px;
 
-    .column-entity {
+    .column-entity, .grouped-columns {
       padding: 5px;
       text-align: center;
-      background-color: #FFF;
+      background-color: $bg-color;
 
-      border: 1px solid #d6d8db;
+      border: 1px solid $border-color;
       border-radius: 5px;
       margin: 5px 0;
 
       position: relative;
+
+      @extend .ellipsis-on-overflow;
+    }
+
+    .grouped-columns {
+      box-shadow: -2px -2px 0 rgba($bg-color, 0.95), -3px -3px 0 rgba($border-color, 0.95),
+                  -5px -5px 0 rgba($bg-color, 0.7), -6px -6px 0 rgba($border-color, 0.7);
+      margin-left: 7px;
+      margin-top: 10px;
     }
   }
 }

+ 26 - 6
desktop/core/src/desktop/js/components/er-diagram/comps/table-entity.vue

@@ -18,22 +18,33 @@
 
 <template>
   <div class="table-entity" @click="$emit('click')">
-    <div class="db-name">
+    <div :title="entity.database" class="db-name">
       {{ entity.database }}
     </div>
-    <div class="table-name">
+    <div :title="entity.name" class="table-name">
       {{ entity.name }}
     </div>
     <div class="columns-container">
       <div
-        v-for="column in entity.columns"
-        :key="column"
+        v-for="column in entity.columns.slice(0, maxColumns)"
+        :key="column.id"
+        :data-entity-id="column.id"
+        :title="column.name"
         class="column-entity"
-        :data-erd-id="entity.id + '.' + column"
       >
         <div class="left-point" />
         <div class="right-point" />
-        {{ column }}
+        {{ column.name }}
+      </div>
+      <div
+        v-if="entity.columns.length > maxColumns"
+        :data-entity-id="entity.columns.map(column => column.id).join(' ')"
+        class="grouped-columns"
+        @click.stop="expandColumns()"
+      >
+        <div class="left-point" />
+        <div class="right-point" />
+        +{{ entity.columns.length - maxColumns }} columns
       </div>
     </div>
   </div>
@@ -48,5 +59,14 @@
   @Component
   export default class TableEntity extends Vue {
     @Prop() entity: Table;
+    @Prop({ default: 10 }) maxColumns: number;
+
+    updated(): void {
+      this.$emit('updated');
+    }
+
+    expandColumns(): void {
+      this.maxColumns = Number.MAX_SAFE_INTEGER;
+    }
   }
 </script>

+ 24 - 16
desktop/core/src/desktop/js/components/er-diagram/index.vue

@@ -32,11 +32,12 @@
               v-if="entity.type === EntityTypes.Table"
               :entity="entity"
               @click="entityClicked(entity)"
+              @updated="plotRelations()"
             />
             <LiteralEntity
               v-else-if="entity.type === EntityTypes.Literal"
               :entity="entity"
-              :data-erd-id="entity.id"
+              :data-entity-id="entity.id"
             />
           </div>
         </div>
@@ -46,8 +47,8 @@
           <path
             v-for="(relation, index) in relations"
             :key="index"
-            :data-erd-left="relation.left.id"
-            :data-erd-right="relation.right.id"
+            :data-entity-id-left="relation.left.id"
+            :data-entity-id-right="relation.right.id"
             class="relation-path"
           />
         </svg>
@@ -92,16 +93,18 @@
 
     getSelectorPosition(selector: string, offset: any): any {
       const element = this.$el.querySelector(selector);
-      const rect = element.getBoundingClientRect();
-      let x = rect.left;
-      let y = rect.top;
+      if (element) {
+        const rect = element.getBoundingClientRect();
+        let x = rect.left;
+        let y = rect.top;
 
-      if (offset) {
-        x = x - offset.x;
-        y = y - offset.y + 1;
-      }
+        if (offset) {
+          x = x - offset.x;
+          y = y - offset.y + 1;
+        }
 
-      return { x, y };
+        return { x, y };
+      }
     }
 
     plotRelations(): void {
@@ -110,18 +113,23 @@
 
       relationPaths.forEach((element: any) => {
         const leftPos = this.getSelectorPosition(
-          `[data-erd-id="${element.dataset.erdLeft}"] .right-point`,
+          `[data-entity-id~="${element.dataset.entityIdLeft}"] .right-point`,
           offset
         );
         const rightPos = this.getSelectorPosition(
-          `[data-erd-id="${element.dataset.erdRight}"] .left-point`,
+          `[data-entity-id~="${element.dataset.entityIdRight}"] .left-point`,
           offset
         );
 
-        const path: string = `M ${leftPos.x},${leftPos.y} C ${leftPos.x + CURVATURE},${leftPos.y}
-            ${rightPos.x - CURVATURE},${rightPos.y} ${rightPos.x},${rightPos.y}`;
+        if (leftPos && rightPos) {
+          const path: string = `M ${leftPos.x},${leftPos.y} C ${leftPos.x + CURVATURE},${leftPos.y}
+              ${rightPos.x - CURVATURE},${rightPos.y} ${rightPos.x},${rightPos.y}`;
+          element.setAttribute('d', path);
 
-        element.setAttribute('d', path);
+          element.style.visibility = 'visible';
+        } else {
+          element.style.visibility = 'hidden';
+        }
       });
     }
     updated(): void {

+ 2 - 2
desktop/core/src/desktop/js/components/er-diagram/lib/entities.ts

@@ -25,9 +25,9 @@ export class Table implements IEntity {
 
   database: string;
   name: string;
-  columns: Array<string>;
+  columns: Array<Column>;
 
-  constructor(database: string, name: string, columns: Array<string>) {
+  constructor(database: string, name: string, columns: Array<Column>) {
     this.id = Table.buildId(database, name);
     this.database = database;
     this.name = name;