databasium 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +32 -0
- data/MIT-LICENSE +20 -0
- data/README.md +109 -0
- data/Rakefile +6 -0
- data/app/assets/builds/application.js +9045 -0
- data/app/assets/builds/application.js.map +7 -0
- data/app/assets/builds/databasium.css +2 -0
- data/app/assets/config/databasium_manifest.js +1 -0
- data/app/assets/javascript/databasium/application.js +2 -0
- data/app/assets/javascript/databasium/controllers/attribute_controller.js +27 -0
- data/app/assets/javascript/databasium/controllers/collapse_controller.js +18 -0
- data/app/assets/javascript/databasium/controllers/error_controller.js +15 -0
- data/app/assets/javascript/databasium/controllers/filter_controller.js +224 -0
- data/app/assets/javascript/databasium/controllers/flash_controller.js +18 -0
- data/app/assets/javascript/databasium/controllers/graph_controller.js +193 -0
- data/app/assets/javascript/databasium/controllers/index.js +7 -0
- data/app/assets/javascript/databasium/controllers/layout_controller.js +13 -0
- data/app/assets/javascript/databasium/controllers/model_controller.js +32 -0
- data/app/assets/javascript/databasium/controllers/new_migration_controller.js +107 -0
- data/app/assets/javascript/databasium/controllers/relation_controller.js +10 -0
- data/app/assets/javascript/databasium/controllers/search_controller.js +23 -0
- data/app/assets/javascript/databasium/controllers/table_controller.js +283 -0
- data/app/assets/javascript/databasium/controllers/table_select_controller.js +19 -0
- data/app/assets/javascript/databasium/controllers/toggle_controller.js +28 -0
- data/app/assets/javascript/databasium/controllers/validation_controller.js +78 -0
- data/app/assets/javascript/databasium/shapes/erd_table_shape.js +54 -0
- data/app/assets/stylesheets/databasium/application.css +15 -0
- data/app/assets/stylesheets/databasium/colors.css +55 -0
- data/app/assets/stylesheets/databasium/custom.css +36 -0
- data/app/assets/stylesheets/databasium/databasium_engine.css +6 -0
- data/app/assets/stylesheets/databasium/pagy-tailwind.css +66 -0
- data/app/components/base.rb +50 -0
- data/app/components/databasium/collapsable.rb +62 -0
- data/app/components/databasium/forms/model.rb +147 -0
- data/app/components/databasium/forms/search.rb +31 -0
- data/app/components/databasium/global/error.rb +60 -0
- data/app/components/databasium/global/flash.rb +73 -0
- data/app/components/databasium/global/header_actions.rb +36 -0
- data/app/components/databasium/global/sidebar.rb +45 -0
- data/app/components/databasium/global/suggestion.rb +25 -0
- data/app/components/databasium/migrations/action.rb +39 -0
- data/app/components/databasium/migrations/file.rb +58 -0
- data/app/components/databasium/migrations/form.rb +222 -0
- data/app/components/databasium/migrations/header_actions.rb +87 -0
- data/app/components/databasium/migrations/migration_status.rb +22 -0
- data/app/components/databasium/migrations/preview.rb +29 -0
- data/app/components/databasium/migrations/show_turbo_stream.rb +19 -0
- data/app/components/databasium/migrations/sidebar.rb +28 -0
- data/app/components/databasium/models/attributes.rb +49 -0
- data/app/components/databasium/models/form.rb +100 -0
- data/app/components/databasium/models/header_actions.rb +51 -0
- data/app/components/databasium/models/model_preview.rb +31 -0
- data/app/components/databasium/models/sidebar.rb +25 -0
- data/app/components/databasium/models/templates/attribute.rb +99 -0
- data/app/components/databasium/models/templates/base.rb +6 -0
- data/app/components/databasium/models/templates/relation.rb +56 -0
- data/app/components/databasium/models/templates/validation.rb +285 -0
- data/app/components/databasium/navigation/base_icon.rb +32 -0
- data/app/components/databasium/navigation/frontend_icon.rb +17 -0
- data/app/components/databasium/navigation/get_icon.rb +26 -0
- data/app/components/databasium/navigation/icon.rb +28 -0
- data/app/components/databasium/navigation/icon_panel.rb +26 -0
- data/app/components/databasium/navigation/post_icon.rb +25 -0
- data/app/components/databasium/navigation/put_icon.rb +18 -0
- data/app/components/databasium/records/filter.rb +73 -0
- data/app/components/databasium/records/foreign_records.rb +84 -0
- data/app/components/databasium/records/header_actions.rb +110 -0
- data/app/components/databasium/records/show_turbo_stream.rb +75 -0
- data/app/components/databasium/records/sidebar.rb +23 -0
- data/app/components/databasium/records/table/record_panel.rb +60 -0
- data/app/components/databasium/records/table/row.rb +104 -0
- data/app/components/databasium/records/table.rb +125 -0
- data/app/components/databasium/records/table_turbo_frame.rb +37 -0
- data/app/components/databasium/records/utilities.rb +25 -0
- data/app/components/databasium/schemas/header_actions.rb +99 -0
- data/app/components/databasium/schemas/sidebar.rb +25 -0
- data/app/components/databasium/search_results/migrations.rb +37 -0
- data/app/components/databasium/search_results/models.rb +36 -0
- data/app/components/databasium/search_results/schema_models.rb +37 -0
- data/app/components/databasium/search_results/tables.rb +31 -0
- data/app/components/databasium/type_select.rb +35 -0
- data/app/controllers/databasium/application_controller.rb +68 -0
- data/app/controllers/databasium/homepage_controller.rb +5 -0
- data/app/controllers/databasium/migrations_controller.rb +186 -0
- data/app/controllers/databasium/models_controller.rb +105 -0
- data/app/controllers/databasium/records_controller.rb +156 -0
- data/app/controllers/databasium/schemas_controller.rb +52 -0
- data/app/helpers/databasium/application_helper.rb +4 -0
- data/app/helpers/databasium/heroicon_helper.rb +21 -0
- data/app/helpers/databasium/models_helper.rb +4 -0
- data/app/jobs/databasium/application_job.rb +4 -0
- data/app/mailers/databasium/application_mailer.rb +6 -0
- data/app/models/databasium/application_record.rb +5 -0
- data/app/models/model.json +0 -0
- data/app/services/databasium/migration.rb +176 -0
- data/app/services/databasium/model.rb +182 -0
- data/app/services/databasium/record.rb +65 -0
- data/app/services/databasium/schema.rb +146 -0
- data/app/views/base.rb +13 -0
- data/app/views/databasium/errors/non_development.rb +21 -0
- data/app/views/databasium/homepage/index.rb +29 -0
- data/app/views/databasium/migrations/index.rb +33 -0
- data/app/views/databasium/migrations/new.rb +29 -0
- data/app/views/databasium/models/index.rb +31 -0
- data/app/views/databasium/models/new.rb +37 -0
- data/app/views/databasium/records/index.rb +24 -0
- data/app/views/databasium/schemas/index.rb +39 -0
- data/app/views/layouts/databasium/application.rb +56 -0
- data/config/importmap.rb +12 -0
- data/config/initializers/heroicon.rb +12 -0
- data/config/initializers/pagy.rb +48 -0
- data/config/initializers/phlex.rb +19 -0
- data/config/routes.rb +31 -0
- data/config/tailwind.config.js +10 -0
- data/lib/databasium/engine.rb +57 -0
- data/lib/databasium/engine_mount.rb +37 -0
- data/lib/databasium/middleware/conditional_check_pending.rb +27 -0
- data/lib/databasium/templates/create_table_migration.rb.tt +29 -0
- data/lib/databasium/templates/migration.rb.tt +48 -0
- data/lib/databasium/templates/model.rb.tt +23 -0
- data/lib/databasium/version.rb +3 -0
- data/lib/databasium.rb +11 -0
- data/lib/tasks/databasium_tasks.rake +4 -0
- metadata +272 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-yellow-400:oklch(85.2% .199 91.936);--color-green-100:oklch(96.2% .044 156.743);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-700:oklch(52.7% .154 150.069);--color-blue-500:oklch(62.3% .214 259.815);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-gray-300:oklch(87.2% .01 258.338);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height:calc(1.5/1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-light:300;--font-weight-semibold:600;--font-weight-bold:700;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-background:var(--databasium-background);--color-main-text:var(--databasium-text);--color-border:var(--databasium-border);--color-panel:var(--databasium-panel)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}input:-webkit-autofill{transition:background-color 5000s ease-in-out;-webkit-box-shadow:0 0 0px 1000px var(--color-background)inset!important;-webkit-text-fill-color:var(--color-main-text)!important}input:-webkit-autofill:hover{transition:background-color 5000s ease-in-out;-webkit-box-shadow:0 0 0px 1000px var(--color-background)inset!important;-webkit-text-fill-color:var(--color-main-text)!important}input:-webkit-autofill:focus{transition:background-color 5000s ease-in-out;-webkit-box-shadow:0 0 0px 1000px var(--color-background)inset!important;-webkit-text-fill-color:var(--color-main-text)!important}}@layer components{.pagy{--B:1;--H:360;--S:48.71;--L:73.56;--A:1;--spacing:.1875rem;--padding:.5rem;--rounding:.75rem;--border-width:.0625rem;--font-size:.875rem;--font-weight:300;--line-height:1.75;--text:hsl(var(--H)var(--S)calc(var(--L) - (30*var(--B))));--text-hover:hsl(var(--H)var(--S)calc(var(--L) - (33*var(--B))));--text-current:hsl(var(--H)var(--S)calc(100*(var(--B) + 1)));--background:hsl(var(--H)var(--S)calc(var(--L) + (30*var(--B))));--background-hover:hsl(var(--H)var(--S)calc(var(--L) + (20*var(--B))));--background-current:hsl(var(--H)var(--S)var(--L));--background-input:hsl(var(--H)var(--S)calc(var(--L) + (45*var(--B))));--opacity:1;column-gap:var(--spacing);font-size:var(--font-size);--tw-leading:var(--line-height);line-height:var(--line-height);--tw-font-weight:var(--font-weight);font-weight:var(--font-weight);color:var(--text);display:flex}.pagy a:not([role=separator]){border-radius:var(--rounding);border-style:var(--tw-border-style);border-width:var(--border-width);--tw-border-style:solid;border-style:solid;border-color:var(--background-current);background-color:var(--background);padding-inline:var(--padding);padding-block:calc(var(--padding)/3);opacity:var(--opacity);display:block}.pagy a[href]:hover{background-color:var(--background-hover);color:var(--text-hover)}.pagy a:not([href]){cursor:default}.pagy a[role=link]:not([aria-current]){opacity:calc(var(--opacity)*.6)}.pagy a[aria-current]{background-color:var(--background-current);color:var(--text-current)}.pagy label{border-radius:var(--rounding);border-style:var(--tw-border-style);border-width:var(--border-width);--tw-border-style:solid;border-style:solid;border-color:var(--background-current);background-color:var(--background);padding-inline:var(--padding);padding-block:calc(var(--padding)/3 - var(--border-width));white-space:nowrap;display:inline-block}.pagy label input{border-radius:calc(var(--rounding)/2);border-style:var(--tw-border-style);border-width:var(--border-width);border-color:var(--background-current);background-color:var(--background-input);font-size:var(--font-size);--tw-leading:var(--line-height);line-height:var(--line-height);--tw-font-weight:var(--font-weight);font-weight:var(--font-weight);color:var(--text)}.scrollbar-thin::-webkit-scrollbar{border-radius:3.40282e38px;width:6px;height:6px}.scrollbar-thin::-webkit-scrollbar-track{background-color:var(--databasium-background)}.scrollbar-thin::-webkit-scrollbar-thumb{background-color:var(--databasium-panel)}.scrollbar-thin::-webkit-scrollbar-thumb:hover{background-color:var(--databasium-hover)}.scrollbar-thin{scrollbar-width:thin;scrollbar-color:var(--databasium-panel)var(--databasium-background)}}@layer utilities{.collapse{visibility:collapse}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing)*0)}.-top-1\.5{top:calc(var(--spacing)*-1.5)}.-top-2\.5{top:calc(var(--spacing)*-2.5)}.-top-3{top:calc(var(--spacing)*-3)}.-top-4{top:calc(var(--spacing)*-4)}.top-1{top:calc(var(--spacing)*1)}.top-2{top:calc(var(--spacing)*2)}.top-20{top:calc(var(--spacing)*20)}.-right-\[0\.5rem\]{right:-.5rem}.right-0{right:calc(var(--spacing)*0)}.right-4{right:calc(var(--spacing)*4)}.-bottom-5\.5{bottom:calc(var(--spacing)*-5.5)}.bottom-full{bottom:100%}.left-0{left:calc(var(--spacing)*0)}.left-1\/2{left:50%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-\[9999\]{z-index:9999}.col-span-2{grid-column:span 2/span 2}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-2{margin:calc(var(--spacing)*2)}.m-4{margin:calc(var(--spacing)*4)}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing)*2)}.ms-1{margin-inline-start:calc(var(--spacing)*1)}.ms-2{margin-inline-start:calc(var(--spacing)*2)}.ms-auto{margin-inline-start:auto}.me-2{margin-inline-end:calc(var(--spacing)*2)}.me-auto{margin-inline-end:auto}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mr-2{margin-right:calc(var(--spacing)*2)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.ml-2{margin-left:calc(var(--spacing)*2)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-2{height:calc(var(--spacing)*2)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-8{height:calc(var(--spacing)*8)}.h-10{height:calc(var(--spacing)*10)}.h-11{height:calc(var(--spacing)*11)}.h-15{height:calc(var(--spacing)*15)}.h-\[90dvh\]{height:90dvh}.h-dvh{height:100dvh}.h-fit{height:fit-content}.h-full{height:100%}.max-h-50{max-height:calc(var(--spacing)*50)}.max-h-64{max-height:calc(var(--spacing)*64)}.max-h-100{max-height:calc(var(--spacing)*100)}.max-h-\[calc\(100dvh-135px\)\]{max-height:calc(100dvh - 135px)}.max-h-fit{max-height:fit-content}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-\[calc\(100dvh-51px\)\]{min-height:calc(100dvh - 51px)}.min-h-full{min-height:100%}.w-1\/2{width:50%}.w-1\/3{width:33.3333%}.w-3{width:calc(var(--spacing)*3)}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-10{width:calc(var(--spacing)*10)}.w-35{width:calc(var(--spacing)*35)}.w-55{width:calc(var(--spacing)*55)}.w-64{width:calc(var(--spacing)*64)}.w-80{width:calc(var(--spacing)*80)}.w-125{width:calc(var(--spacing)*125)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-15{max-width:calc(var(--spacing)*15)}.max-w-40{max-width:calc(var(--spacing)*40)}.max-w-55{max-width:calc(var(--spacing)*55)}.max-w-125{max-width:calc(var(--spacing)*125)}.max-w-\[50vw\]{max-width:50vw}.max-w-fit{max-width:fit-content}.max-w-full{max-width:100%}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-fit{min-width:fit-content}.min-w-max{min-width:max-content}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:calc(calc(1/2*100%)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.rotate-45{rotate:45deg}.rotate-180{rotate:180deg}.animate-pulse{animation:var(--animate-pulse)}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[max-content_1fr\]{grid-template-columns:max-content 1fr}.flex-col{flex-direction:column}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.justify-items-center{justify-items:center}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.gap-4{gap:calc(var(--spacing)*4)}.gap-5{gap:calc(var(--spacing)*5)}.gap-x-2{column-gap:calc(var(--spacing)*2)}:where(.divide-x>:not(:last-child)),:where(.divide-x-1>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px*var(--tw-divide-x-reverse));border-inline-end-width:calc(1px*calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--databasium-border)}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-b-xl{border-bottom-right-radius:var(--radius-xl);border-bottom-left-radius:var(--radius-xl)}.rounded-bl-xl{border-bottom-left-radius:var(--radius-xl)}.border,.border-1{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-r-2{border-right-style:var(--tw-border-style);border-right-width:2px}.border-b,.border-b-1{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l,.border-l-1{border-left-style:var(--tw-border-style);border-left-width:1px}.border-accent{border-color:var(--databasium-accent)}.border-blue-500{border-color:var(--color-blue-500)}.border-border{border-color:var(--databasium-border)}.border-gray-300{border-color:var(--color-gray-300)}.border-green-400{border-color:var(--color-green-400)}.border-red-400{border-color:var(--color-red-400)}.border-r-border{border-right-color:var(--databasium-border)}.border-b-border{border-bottom-color:var(--databasium-border)}.border-b-gray-300{border-bottom-color:var(--color-gray-300)}.bg-accent{background-color:var(--databasium-accent)}.bg-background{background-color:var(--databasium-background)}.bg-black\/40{background-color:#0006}@supports (color:color-mix(in lab, red, red)){.bg-black\/40{background-color:color-mix(in oklab,var(--color-black)40%,transparent)}}.bg-blue-500{background-color:var(--color-blue-500)}.bg-green-100{background-color:var(--color-green-100)}.bg-panel{background-color:var(--databasium-panel)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-white{background-color:var(--color-white)}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-1{padding-block:calc(var(--spacing)*1)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.ps-1{padding-inline-start:calc(var(--spacing)*1)}.ps-4{padding-inline-start:calc(var(--spacing)*4)}.pe-2{padding-inline-end:calc(var(--spacing)*2)}.pe-4{padding-inline-end:calc(var(--spacing)*4)}.pr-10{padding-right:calc(var(--spacing)*10)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.pb-6{padding-bottom:calc(var(--spacing)*6)}.pl-2{padding-left:calc(var(--spacing)*2)}.text-center{text-align:center}.text-end{text-align:end}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-5{--tw-leading:calc(var(--spacing)*5);line-height:calc(var(--spacing)*5)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-nowrap{text-wrap:nowrap}.text-wrap{text-wrap:wrap}.break-all{word-break:break-all}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--databasium-accent)}.text-blue-500{color:var(--color-blue-500)}.text-gray-300{color:var(--color-gray-300)}.text-green-500{color:var(--color-green-500)}.text-green-700{color:var(--color-green-700)}.text-main-text{color:var(--databasium-text)}.text-red-500{color:var(--color-red-500)}.text-red-700{color:var(--color-red-700)}.text-selected{color:var(--databasium-selected)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-accent{--tw-shadow:var(--databasium-shadow);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-1000{--tw-duration:1s;transition-duration:1s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:cursor-pointer:hover{cursor:pointer}.hover\:border-hover:hover{border-color:var(--databasium-hover)}.hover\:bg-background:hover{background-color:var(--databasium-background)}.hover\:bg-panel:hover{background-color:var(--databasium-panel)}.hover\:text-hover:hover{color:var(--databasium-hover)}.hover\:text-red-800:hover{color:var(--color-red-800)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}@media (min-width:64rem){.lg\:w-4xl{width:var(--container-4xl)}}}:root{--databasium-primary:var(--color-blue-500);--databasium-secondary:var(--color-blue-500);--databasium-accent:var(--color-blue-500);--databasium-background:var(--color-slate-900);--databasium-text:var(--color-slate-100);--databasium-border:var(--color-slate-600);--databasium-panel:var(--color-slate-800);--databasium-hover:var(--color-yellow-400);--databasium-selected:var(--color-green-500);--databasium-shadow:0 0 6px #60a5fa80;--databasium-active:var(--color-blue-500);--databasium-focus:var(--color-blue-500);--databasium-disabled:var(--color-blue-500);--databasium-error:var(--color-blue-500);--databasium-success:var(--color-blue-500);--databasium-warning:var(--color-blue-500)}[data-theme=white]{--app-primary:#fff;--app-secondary:yellow;--app-accent:#888;--app-background:#111;--app-text:#fff}[data-theme=ocean]{--app-primary:#0f172a;--app-secondary:#38bdf8;--app-accent:#06b6d4;--app-background:#ecfeff;--app-text:#082f49}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//= link_tree ../builds .css
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="attribute"
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = ["name", "nameInput", "nameValidationInput"];
|
|
6
|
+
|
|
7
|
+
connect() {}
|
|
8
|
+
|
|
9
|
+
updateName() {
|
|
10
|
+
const name = this.nameInputTarget.value;
|
|
11
|
+
this.nameTarget.textContent = name;
|
|
12
|
+
this.nameValidationInputTargets.forEach((target) => {
|
|
13
|
+
target.value = name;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
updateValidationName(e) {
|
|
18
|
+
const name = this.nameInputTarget.value;
|
|
19
|
+
this.nameValidationInputTargets.forEach((target) => {
|
|
20
|
+
target.value = name;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
remove() {
|
|
25
|
+
this.element.remove();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="collapse"
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = ["content", "collapseIcon"];
|
|
6
|
+
|
|
7
|
+
connect() {}
|
|
8
|
+
|
|
9
|
+
toggle(e) {
|
|
10
|
+
e.preventDefault();
|
|
11
|
+
if (this.hasContentTarget) {
|
|
12
|
+
this.contentTargets.forEach((target) => {
|
|
13
|
+
target.classList.toggle("hidden");
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
this.collapseIconTarget.classList.toggle("rotate-180");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="error"
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = [];
|
|
6
|
+
|
|
7
|
+
connect() {}
|
|
8
|
+
|
|
9
|
+
close() {
|
|
10
|
+
this.element.classList.add("opacity-0");
|
|
11
|
+
setTimeout(() => {
|
|
12
|
+
this.element.classList.add("hidden");
|
|
13
|
+
}, 1000);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
const OPERATOR_LABELS = {
|
|
4
|
+
eq: "=",
|
|
5
|
+
not_eq: "!=",
|
|
6
|
+
matches: "like",
|
|
7
|
+
does_not_match: "not like",
|
|
8
|
+
gt: ">",
|
|
9
|
+
lt: "<",
|
|
10
|
+
gteq: ">=",
|
|
11
|
+
lteq: "<=",
|
|
12
|
+
// between: "between",
|
|
13
|
+
is_true: "= true",
|
|
14
|
+
is_false: "= false",
|
|
15
|
+
is_null: "is null",
|
|
16
|
+
is_not_null: "is not null"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const TEXT_OPERATORS = ["eq", "not_eq", "matches", "does_not_match"];
|
|
20
|
+
const NUMBER_OPERATORS = ["eq", "not_eq", "gt", "lt", "gteq", "lteq"];
|
|
21
|
+
const DATE_OPERATORS = ["eq", "not_eq", "gt", "lt", "gteq", "lteq"];
|
|
22
|
+
const BOOLEAN_OPERATORS = ["eq", "not_eq", "is_true", "is_false", "is_null", "is_not_null"];
|
|
23
|
+
|
|
24
|
+
const INPUT_TYPE_BY_COLUMN = {
|
|
25
|
+
string: "text",
|
|
26
|
+
text: "text",
|
|
27
|
+
integer: "number",
|
|
28
|
+
float: "number",
|
|
29
|
+
decimal: "number",
|
|
30
|
+
datetime: "date",
|
|
31
|
+
date: "date",
|
|
32
|
+
time: "time"
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const CL = {
|
|
36
|
+
row: "flex items-center py-1 border-l-1 border-border ps-1 ms-1",
|
|
37
|
+
select: "px-4 py-2 rounded-md border-1 border-border w-fit h-10 bg-panel",
|
|
38
|
+
selectMuted: "px-4 py-2 rounded-md border-1 border-border w-fit h-10 bg-background",
|
|
39
|
+
selectWhere: "px-4 py-2 rounded-md border-1 border-border w-fit h-10",
|
|
40
|
+
input: "px-4 py-2 rounded-md border-1 border-border w-fit max-w-40 h-10",
|
|
41
|
+
separator: "bg-panel inline-block h-2 w-3",
|
|
42
|
+
removeBtn: "text-red-500 hover:text-red-800"
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function operatorsForColumnType(type) {
|
|
46
|
+
if (type === "text" || type === "string") return TEXT_OPERATORS;
|
|
47
|
+
if (type === "integer" || type === "float" || type === "decimal") return NUMBER_OPERATORS;
|
|
48
|
+
if (type === "datetime" || type === "date" || type === "time") return DATE_OPERATORS;
|
|
49
|
+
if (type === "boolean") return BOOLEAN_OPERATORS;
|
|
50
|
+
return TEXT_OPERATORS;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function fillOperatorOptions(select, operatorKeys) {
|
|
54
|
+
select.add(new Option("Operator", ""));
|
|
55
|
+
for (const key of operatorKeys) {
|
|
56
|
+
select.add(new Option(OPERATOR_LABELS[key], key));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Connects to data-controller="filter"
|
|
61
|
+
export default class extends Controller {
|
|
62
|
+
static targets = ["selectColumn", "form", "closeButton", "removeIcon"];
|
|
63
|
+
static values = { columns: Array };
|
|
64
|
+
|
|
65
|
+
connect() {
|
|
66
|
+
this.previousValues = new WeakMap();
|
|
67
|
+
this.addFilter(null, false);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
createSeparator() {
|
|
71
|
+
const el = document.createElement("div");
|
|
72
|
+
el.className = CL.separator;
|
|
73
|
+
return el;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
chooseColumn(event) {
|
|
77
|
+
const columnSelect = event.target;
|
|
78
|
+
const selected = columnSelect.value;
|
|
79
|
+
let selectedAttribute = null;
|
|
80
|
+
|
|
81
|
+
this.columnsValue = this.columnsValue.map((c) => {
|
|
82
|
+
if (c.name === selected) {
|
|
83
|
+
selectedAttribute = c;
|
|
84
|
+
return { ...c, used: true };
|
|
85
|
+
}
|
|
86
|
+
if (c.name === this.previousValues.get(columnSelect)) {
|
|
87
|
+
return { ...c, used: false };
|
|
88
|
+
}
|
|
89
|
+
return c;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.resetOptions();
|
|
93
|
+
|
|
94
|
+
if (this.previousValues.get(columnSelect)) {
|
|
95
|
+
while (
|
|
96
|
+
columnSelect.nextElementSibling &&
|
|
97
|
+
columnSelect.nextElementSibling.tagName !== "BUTTON"
|
|
98
|
+
) {
|
|
99
|
+
columnSelect.nextElementSibling.remove();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!selectedAttribute) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const sep = this.createSeparator();
|
|
108
|
+
columnSelect.after(this.createInputField(selectedAttribute));
|
|
109
|
+
columnSelect.after(sep.cloneNode(true));
|
|
110
|
+
columnSelect.after(this.createOperatorField(selectedAttribute));
|
|
111
|
+
columnSelect.after(sep.cloneNode(true));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
rememberValue(event) {
|
|
115
|
+
this.previousValues.set(event.target, event.target.value);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
addFilter(e, withOperatorType = true) {
|
|
119
|
+
e?.preventDefault();
|
|
120
|
+
|
|
121
|
+
const row = document.createElement("div");
|
|
122
|
+
row.className = CL.row;
|
|
123
|
+
|
|
124
|
+
const columnSelect = this.buildColumnSelect();
|
|
125
|
+
const removeButton = this.buildRemoveButton();
|
|
126
|
+
|
|
127
|
+
row.appendChild(removeButton);
|
|
128
|
+
row.appendChild(withOperatorType ? this.createOperatorTypeField() : this.createWhereField());
|
|
129
|
+
row.appendChild(this.createSeparator());
|
|
130
|
+
row.appendChild(columnSelect);
|
|
131
|
+
|
|
132
|
+
this.formTarget.lastElementChild.before(row);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
buildColumnSelect() {
|
|
136
|
+
const select = document.createElement("select");
|
|
137
|
+
select.add(new Option("Column", ""));
|
|
138
|
+
this.columnsValue.forEach((col) => {
|
|
139
|
+
if (!col.used) {
|
|
140
|
+
select.add(new Option(col.name, col.name));
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
select.setAttribute("data-filter-target", "selectColumn");
|
|
144
|
+
select.setAttribute(
|
|
145
|
+
"data-action",
|
|
146
|
+
"change->filter#chooseColumn mousedown->filter#rememberValue"
|
|
147
|
+
);
|
|
148
|
+
select.className = CL.select;
|
|
149
|
+
return select;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
buildRemoveButton() {
|
|
153
|
+
const button = document.createElement("button");
|
|
154
|
+
button.setAttribute("data-action", "click->filter#removeFilter");
|
|
155
|
+
button.className = CL.removeBtn;
|
|
156
|
+
const xIcon = this.removeIconTarget.cloneNode(true);
|
|
157
|
+
xIcon.classList.remove("hidden");
|
|
158
|
+
xIcon.classList.add("inline-block");
|
|
159
|
+
button.appendChild(xIcon);
|
|
160
|
+
return button;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
createInputField(selectedAttribute) {
|
|
164
|
+
const input = document.createElement("input");
|
|
165
|
+
input.type = INPUT_TYPE_BY_COLUMN[selectedAttribute.type] || "text";
|
|
166
|
+
input.className = CL.input;
|
|
167
|
+
input.name = `filter[${selectedAttribute.name}][value]`;
|
|
168
|
+
return input;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
createOperatorField(selectedAttribute) {
|
|
172
|
+
const select = document.createElement("select");
|
|
173
|
+
select.name = `filter[${selectedAttribute.name}][operator]`;
|
|
174
|
+
select.className = CL.selectMuted;
|
|
175
|
+
fillOperatorOptions(select, operatorsForColumnType(selectedAttribute.type));
|
|
176
|
+
return select;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
createOperatorTypeField() {
|
|
180
|
+
const select = document.createElement("select");
|
|
181
|
+
select.name = "filter[operator_types][]";
|
|
182
|
+
select.className = CL.selectMuted;
|
|
183
|
+
select.add(new Option("Operator Type", "and"));
|
|
184
|
+
select.add(new Option("AND", "and"));
|
|
185
|
+
select.add(new Option("OR", "or"));
|
|
186
|
+
return select;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
createWhereField() {
|
|
190
|
+
const select = document.createElement("select");
|
|
191
|
+
select.className = CL.selectWhere;
|
|
192
|
+
select.add(new Option("Where", "Where"));
|
|
193
|
+
select.disabled = true;
|
|
194
|
+
return select;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
removeFilter(event) {
|
|
198
|
+
const row = event.currentTarget.parentElement;
|
|
199
|
+
const columnName = row?.querySelector("[data-filter-target='selectColumn']")?.value;
|
|
200
|
+
|
|
201
|
+
if (columnName) {
|
|
202
|
+
this.columnsValue = this.columnsValue.map((c) =>
|
|
203
|
+
c.name === columnName ? { ...c, used: false } : c
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
row?.remove();
|
|
208
|
+
this.resetOptions();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
resetOptions() {
|
|
212
|
+
this.selectColumnTargets.forEach((select) => {
|
|
213
|
+
const value = select.value;
|
|
214
|
+
select.innerHTML = "";
|
|
215
|
+
select.add(new Option("Column", ""));
|
|
216
|
+
this.columnsValue.forEach((col) => {
|
|
217
|
+
if (!col.used || col.name === value) {
|
|
218
|
+
select.add(new Option(col.name, col.name));
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
select.value = value;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="flash"
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = [];
|
|
6
|
+
|
|
7
|
+
connect() {
|
|
8
|
+
setTimeout(() => {
|
|
9
|
+
this.element.classList.add("opacity-0");
|
|
10
|
+
setTimeout(() => this.element.remove(), 1000);
|
|
11
|
+
}, 4000);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
close() {
|
|
15
|
+
this.element.classList.add("opacity-0");
|
|
16
|
+
setTimeout(() => this.element.remove(), 1000);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
import { Graph, InternalEvent, HierarchicalLayout } from "@maxgraph/core";
|
|
3
|
+
import "databasium/shapes/erd_table_shape";
|
|
4
|
+
|
|
5
|
+
// Connects to data-controller="graph"
|
|
6
|
+
export default class extends Controller {
|
|
7
|
+
static values = { tables: String };
|
|
8
|
+
|
|
9
|
+
connect() {
|
|
10
|
+
const data = JSON.parse(this.tablesValue);
|
|
11
|
+
|
|
12
|
+
const container = this.element;
|
|
13
|
+
InternalEvent.disableContextMenu(container);
|
|
14
|
+
const graph = new Graph(container);
|
|
15
|
+
graph.setPanning(true);
|
|
16
|
+
|
|
17
|
+
const panningHandler = graph.getPlugin("PanningHandler");
|
|
18
|
+
if (panningHandler) {
|
|
19
|
+
panningHandler.useLeftButtonForPanning = true;
|
|
20
|
+
}
|
|
21
|
+
container.style.cursor = "grab";
|
|
22
|
+
|
|
23
|
+
container.addEventListener(
|
|
24
|
+
"wheel",
|
|
25
|
+
(event) => {
|
|
26
|
+
event.preventDefault();
|
|
27
|
+
|
|
28
|
+
const currentScale = graph.view.scale;
|
|
29
|
+
const zoomFactor = event.deltaY < 0 ? 1.06 : 0.94;
|
|
30
|
+
const nextScale = Math.min(Math.max(currentScale * zoomFactor, 0.25), 2.5);
|
|
31
|
+
|
|
32
|
+
graph.zoomTo(nextScale, true);
|
|
33
|
+
},
|
|
34
|
+
{ passive: false }
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
graph.getStylesheet().getDefaultEdgeStyle().edgeStyle = "orthogonalEdgeStyle";
|
|
38
|
+
|
|
39
|
+
const vertexes = this.addVertexes(graph, data);
|
|
40
|
+
const edges = this.addEdges(graph, data, vertexes);
|
|
41
|
+
|
|
42
|
+
const layout = new HierarchicalLayout(graph);
|
|
43
|
+
layout.execute(graph.getDefaultParent());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
addLabelToEdge(graph, edge, text, position = "start") {
|
|
47
|
+
const x = position === "start" ? -0.85 : 0.85;
|
|
48
|
+
|
|
49
|
+
graph.insertVertex(
|
|
50
|
+
edge,
|
|
51
|
+
null,
|
|
52
|
+
text,
|
|
53
|
+
x,
|
|
54
|
+
0,
|
|
55
|
+
1,
|
|
56
|
+
1,
|
|
57
|
+
{
|
|
58
|
+
fillColor: "none",
|
|
59
|
+
strokeColor: "none",
|
|
60
|
+
fontColor: "var(--color-main-text)",
|
|
61
|
+
fontSize: 12,
|
|
62
|
+
align: "center",
|
|
63
|
+
verticalAlign: "middle",
|
|
64
|
+
labelBackgroundColor: "var(--color-panel)",
|
|
65
|
+
labelPadding: 4
|
|
66
|
+
},
|
|
67
|
+
true // relative — without this, labels won't appear on the edge
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
addEdges(graph, data, vertexes) {
|
|
72
|
+
const edgeStyle = {
|
|
73
|
+
edgeStyle: "orthogonalEdgeStyle",
|
|
74
|
+
rounded: true,
|
|
75
|
+
startArrow: "none",
|
|
76
|
+
endArrow: "none",
|
|
77
|
+
strokeColor: "var(--color-border)",
|
|
78
|
+
strokeWidth: 2
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
graph.batchUpdate(() => {
|
|
82
|
+
const alreadySet = new Set();
|
|
83
|
+
for (let table of Object.keys(data)) {
|
|
84
|
+
for (let association of data[table]?.associations || []) {
|
|
85
|
+
const relatedTable = data[association.name];
|
|
86
|
+
if (!relatedTable || !vertexes[association.name]) continue;
|
|
87
|
+
if (alreadySet.has(`${association.name}-${table}`)) continue;
|
|
88
|
+
|
|
89
|
+
alreadySet.add(`${table}-${association.name}`);
|
|
90
|
+
|
|
91
|
+
const sourceMacro = association.macro;
|
|
92
|
+
const targetMacro = relatedTable.associations?.find(
|
|
93
|
+
(assoc) => assoc.name === table
|
|
94
|
+
)?.macro;
|
|
95
|
+
|
|
96
|
+
const edge = graph.insertEdge({
|
|
97
|
+
source: vertexes[table],
|
|
98
|
+
target: vertexes[association.name],
|
|
99
|
+
style: edgeStyle
|
|
100
|
+
});
|
|
101
|
+
// macros are declered on oposite sites, thats why we use labels for oposite macros
|
|
102
|
+
this.addLabelToEdge(
|
|
103
|
+
graph,
|
|
104
|
+
edge,
|
|
105
|
+
`${this.getLabelForEdge(sourceMacro)} (${targetMacro ?? "Missing relation"})`,
|
|
106
|
+
"end"
|
|
107
|
+
);
|
|
108
|
+
this.addLabelToEdge(
|
|
109
|
+
graph,
|
|
110
|
+
edge,
|
|
111
|
+
`${this.getLabelForEdge(targetMacro)} (${sourceMacro ?? "Missing relation"})`,
|
|
112
|
+
"start"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getLabelForEdge(macro) {
|
|
120
|
+
switch (macro) {
|
|
121
|
+
case "has_many":
|
|
122
|
+
return "0..*";
|
|
123
|
+
case "has_one":
|
|
124
|
+
return "1";
|
|
125
|
+
case "belongs_to":
|
|
126
|
+
return "1";
|
|
127
|
+
case "has_and_belongs_to_many":
|
|
128
|
+
return "0..*";
|
|
129
|
+
default:
|
|
130
|
+
return "0..*";
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
addZooming(graph) {
|
|
135
|
+
this.element.addEventListener(
|
|
136
|
+
"wheel",
|
|
137
|
+
(event) => {
|
|
138
|
+
event.preventDefault();
|
|
139
|
+
|
|
140
|
+
const currentScale = graph.view.scale;
|
|
141
|
+
const zoomFactor = event.deltaY < 0 ? 1.06 : 0.94;
|
|
142
|
+
const nextScale = Math.min(Math.max(currentScale * zoomFactor, 0.25), 2.5);
|
|
143
|
+
|
|
144
|
+
graph.zoomTo(nextScale, true);
|
|
145
|
+
},
|
|
146
|
+
{ passive: false }
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
addVertexes(graph, data) {
|
|
151
|
+
const vertexes = {};
|
|
152
|
+
const parent = graph.getDefaultParent();
|
|
153
|
+
|
|
154
|
+
graph.batchUpdate(() => {
|
|
155
|
+
for (let table of Object.keys(data)) {
|
|
156
|
+
let longest = this.detectedMaxLength(data[table], "name");
|
|
157
|
+
let longestValue = this.detectedMaxLength(data[table], "sql_type");
|
|
158
|
+
let width = (longest + longestValue + 4) * 8;
|
|
159
|
+
const vertex = graph.insertVertex(
|
|
160
|
+
parent,
|
|
161
|
+
null,
|
|
162
|
+
{
|
|
163
|
+
name: table,
|
|
164
|
+
fields: this.getFields(data[table].columns)
|
|
165
|
+
},
|
|
166
|
+
0,
|
|
167
|
+
0,
|
|
168
|
+
width,
|
|
169
|
+
data[table].columns.filter((column) => column != undefined).length * 32 + 32,
|
|
170
|
+
{ shape: "erdTable", label: "", fontSize: 0, perimeter: "rectanglePerimeter" }
|
|
171
|
+
);
|
|
172
|
+
vertexes[table] = vertex;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
return vertexes;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
getFields(columns) {
|
|
179
|
+
return columns
|
|
180
|
+
.filter((column) => column != undefined)
|
|
181
|
+
.map((column) => {
|
|
182
|
+
return { name: column.name, sql_type: column.sql_type };
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
detectedMaxLength(table, type) {
|
|
187
|
+
return (
|
|
188
|
+
table.columns.reduce((max, column) => {
|
|
189
|
+
return Math.max(max, column[type].length);
|
|
190
|
+
}, 0) || 0
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Application } from "@hotwired/stimulus";
|
|
2
|
+
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading";
|
|
3
|
+
|
|
4
|
+
const application = Application.start();
|
|
5
|
+
window.Stimulus = application;
|
|
6
|
+
|
|
7
|
+
eagerLoadControllersFrom("databasium/controllers", application);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="layout"
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = ["sidebar"];
|
|
6
|
+
|
|
7
|
+
connect() {}
|
|
8
|
+
|
|
9
|
+
toggleSidebar(event) {
|
|
10
|
+
event.preventDefault();
|
|
11
|
+
this.sidebarTarget.classList.toggle("hidden");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="model"
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = [
|
|
6
|
+
"attribute",
|
|
7
|
+
"validation",
|
|
8
|
+
"relation",
|
|
9
|
+
"attributesContainer",
|
|
10
|
+
"validationsContainer",
|
|
11
|
+
"relationsContainer"
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
connect() {}
|
|
15
|
+
|
|
16
|
+
add(event) {
|
|
17
|
+
const target = event.params["target"];
|
|
18
|
+
const container = event.params["container"];
|
|
19
|
+
|
|
20
|
+
if (!target || !container) return;
|
|
21
|
+
|
|
22
|
+
const capitalizedTarget = target[0].toUpperCase() + target.slice(1);
|
|
23
|
+
|
|
24
|
+
if (!this[`has${capitalizedTarget}Target`]) return;
|
|
25
|
+
|
|
26
|
+
const scope = event.currentTarget.closest("[data-controller~='attribute']") || this.element;
|
|
27
|
+
const destination = scope.querySelector(`[data-model-target='${container}']`);
|
|
28
|
+
if (!destination) return;
|
|
29
|
+
|
|
30
|
+
destination.insertAdjacentHTML("beforeend", this[`${target}Target`].innerHTML);
|
|
31
|
+
}
|
|
32
|
+
}
|