@dataif/cli 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.
- package/README.md +16 -0
- package/bin/dataif.js +623 -0
- package/package.json +26 -0
- package/scripts/build-template.mjs +72 -0
- package/templates/dataif/README.md +157 -0
- package/templates/dataif/infra/.env.example +119 -0
- package/templates/dataif/infra/.env.stg.example +119 -0
- package/templates/dataif/infra/airflow/Dockerfile +11 -0
- package/templates/dataif/infra/airflow/Dockerfile.release +17 -0
- package/templates/dataif/infra/airflow/requirements.txt +3 -0
- package/templates/dataif/infra/docker-compose.yml +306 -0
- package/templates/dataif/infra/init-db/01-init-dataif.sh +129 -0
- package/templates/dataif/infra/init-db/pnp-curated-views.sqlinc +444 -0
- package/templates/dataif/infra/init-db/pnp-raw-staging-curated.sqlinc +701 -0
- package/templates/dataif/infra/keycloak/Dockerfile +4 -0
- package/templates/dataif/infra/keycloak/realm-dataif.json +73 -0
- package/templates/dataif/infra/ollama/Dockerfile +9 -0
- package/templates/dataif/infra/ollama/bootstrap-model.sh +100 -0
- package/templates/dataif/infra/ollama/sabia-7b.Modelfile +14 -0
- package/templates/dataif/infra/postgres/Dockerfile +4 -0
- package/templates/dataif/pipelines/airflow/dags/generated/.gitkeep +1 -0
- package/templates/dataif/pipelines/airflow/dags/generated/2020_financeiro_fcc6f1f3_sync.py +9 -0
- package/templates/dataif/pipelines/dataif_pipelines/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/airflow/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/airflow/pnp_pipeline_factory.py +167 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/base/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/base/connector.py +28 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/base/types.py +14 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/config.py +19 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/connector.py +558 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/powerbi_microdados.py +728 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/transform.py +296 -0
- package/templates/dataif/pipelines/dataif_pipelines/jobs/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/jobs/nilo_pipeline.py +112 -0
- package/templates/dataif/pipelines/dataif_pipelines/orchestration/__init__.py +21 -0
- package/templates/dataif/pipelines/dataif_pipelines/orchestration/pnp_workflow.py +783 -0
- package/templates/dataif/pipelines/dataif_pipelines/repositories/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/repositories/pnp_raw_repository.py +860 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/__init__.py +19 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_curated_service.py +66 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_download_service.py +534 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_quality_service.py +9 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_raw_ingestion_service.py +124 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_staging_service.py +271 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/powerbi_catalog_service.py +159 -0
- package/templates/dataif/pipelines/sql/staging/020_pnp_matriculas.sql +112 -0
- package/templates/dataif/pipelines/sql/staging/030_pnp_eficiencia_academica.sql +83 -0
- package/templates/dataif/pipelines/sql/staging/040_pnp_servidores.sql +90 -0
- package/templates/dataif/pipelines/sql/staging/050_pnp_financeiro.sql +72 -0
- package/templates/dataif/pipelines/sql/views_curated/004_mv_pnp_dashboard_fast.sql +204 -0
- package/templates/dataif/pipelines/sql/views_curated/010_vw_pnp_admin_ingestao.sql +51 -0
- package/templates/dataif/pipelines/sql/views_curated/020_vw_pnp_qualidade_dados.sql +114 -0
- package/templates/dataif/pipelines/sql/views_curated/030_vw_pnp_matriculas.sql +67 -0
- package/templates/dataif/pipelines/sql/views_curated/040_vw_pnp_eficiencia.sql +33 -0
- package/templates/dataif/pipelines/sql/views_curated/050_vw_pnp_servidores.sql +30 -0
- package/templates/dataif/pipelines/sql/views_curated/060_vw_pnp_financeiro.sql +22 -0
- package/templates/dataif/pipelines/sql/views_curated/070_vw_pnp_vanna.sql +115 -0
- package/templates/dataif/scripts/configure-env.sh +149 -0
- package/templates/dataif/scripts/create_metabase_pnp_dashboard.py +943 -0
- package/templates/dataif/scripts/create_metabase_pnp_matriculas_dashboard.py +580 -0
- package/templates/dataif/scripts/deploy.sh +79 -0
- package/templates/dataif/scripts/fix_metabase_template_tag_ids.py +91 -0
- package/templates/dataif/scripts/pnp_powerbi_microdados_probe.py +14 -0
- package/templates/dataif/scripts/pnp_validate_raw_run.py +330 -0
- package/templates/dataif/scripts/publish-images.sh +31 -0
- package/templates/dataif/scripts/sync_metabase_dashboard_field_filters.py +241 -0
- package/templates/dataif/scripts/use-vanna-ollama.sh +139 -0
- package/templates/dataif/services/api/.dockerignore +18 -0
- package/templates/dataif/services/api/Dockerfile +12 -0
- package/templates/dataif/services/api/app/__init__.py +1 -0
- package/templates/dataif/services/api/app/auth.py +48 -0
- package/templates/dataif/services/api/app/config.py +59 -0
- package/templates/dataif/services/api/app/keycloak_admin.py +215 -0
- package/templates/dataif/services/api/app/main.py +2432 -0
- package/templates/dataif/services/api/app/metabase_admin.py +191 -0
- package/templates/dataif/services/api/app/metabase_bootstrap.py +44 -0
- package/templates/dataif/services/api/app/metabase_embed.py +15 -0
- package/templates/dataif/services/api/app/pnp_dag_provisioner.py +113 -0
- package/templates/dataif/services/api/app/pnp_instance_repository.py +951 -0
- package/templates/dataif/services/api/app/pnp_powerbi.py +438 -0
- package/templates/dataif/services/api/app/vanna_client.py +32 -0
- package/templates/dataif/services/api/requirements.txt +9 -0
- package/templates/dataif/services/vanna/.dockerignore +18 -0
- package/templates/dataif/services/vanna/Dockerfile +12 -0
- package/templates/dataif/services/vanna/app/config.py +57 -0
- package/templates/dataif/services/vanna/app/main.py +108 -0
- package/templates/dataif/services/vanna/app/runtime_config.py +114 -0
- package/templates/dataif/services/vanna/app/sql_guard.py +123 -0
- package/templates/dataif/services/vanna/app/vanna_engine.py +382 -0
- package/templates/dataif/services/vanna/requirements.txt +8 -0
- package/templates/dataif/services/web/.dockerignore +13 -0
- package/templates/dataif/services/web/Dockerfile +16 -0
- package/templates/dataif/services/web/index.html +12 -0
- package/templates/dataif/services/web/nginx.conf +74 -0
- package/templates/dataif/services/web/package-lock.json +4397 -0
- package/templates/dataif/services/web/package.json +32 -0
- package/templates/dataif/services/web/postcss.config.mjs +5 -0
- package/templates/dataif/services/web/src/App.jsx +2817 -0
- package/templates/dataif/services/web/src/adminAuth.js +245 -0
- package/templates/dataif/services/web/src/assets/avatar_placeholder.png +0 -0
- package/templates/dataif/services/web/src/assets/github_logo_icon_229278.svg +1 -0
- package/templates/dataif/services/web/src/assets/if-logo.png +0 -0
- package/templates/dataif/services/web/src/assets/if.svg +0 -0
- package/templates/dataif/services/web/src/assets/pnp-horizontal.svg +1 -0
- package/templates/dataif/services/web/src/components/AppHeader.jsx +233 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/mobile-header.tsx +56 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-account-card.tsx +209 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-item-button.tsx +67 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-item.tsx +108 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-list.tsx +83 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/config.ts +23 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/header-navigation.tsx +240 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination-base.tsx +376 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination-dot.tsx +52 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination-line.tsx +48 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination.tsx +328 -0
- package/templates/dataif/services/web/src/components/application/tabs/tabs.tsx +223 -0
- package/templates/dataif/services/web/src/components/base/avatar/avatar-label-group.tsx +28 -0
- package/templates/dataif/services/web/src/components/base/avatar/avatar.tsx +129 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/avatar-add-button.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/avatar-company-icon.tsx +24 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/avatar-online-indicator.tsx +29 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/index.tsx +4 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/verified-tick.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/badges/badge-types.ts +264 -0
- package/templates/dataif/services/web/src/components/base/badges/badges.tsx +415 -0
- package/templates/dataif/services/web/src/components/base/button-group/button-group.tsx +104 -0
- package/templates/dataif/services/web/src/components/base/buttons/button.tsx +267 -0
- package/templates/dataif/services/web/src/components/base/input/hint-text.tsx +31 -0
- package/templates/dataif/services/web/src/components/base/input/input.tsx +269 -0
- package/templates/dataif/services/web/src/components/base/input/label.tsx +48 -0
- package/templates/dataif/services/web/src/components/base/radio-buttons/radio-buttons.tsx +127 -0
- package/templates/dataif/services/web/src/components/base/select/combobox.tsx +150 -0
- package/templates/dataif/services/web/src/components/base/select/multi-select.tsx +361 -0
- package/templates/dataif/services/web/src/components/base/select/popover.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/select/select-item.tsx +95 -0
- package/templates/dataif/services/web/src/components/base/select/select-native.tsx +67 -0
- package/templates/dataif/services/web/src/components/base/select/select.tsx +144 -0
- package/templates/dataif/services/web/src/components/base/tags/base-components/tag-close-x.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/tooltip/tooltip.tsx +107 -0
- package/templates/dataif/services/web/src/components/foundations/dot-icon.tsx +22 -0
- package/templates/dataif/services/web/src/components/foundations/logo/untitledui-logo-minimal.tsx +170 -0
- package/templates/dataif/services/web/src/components/foundations/logo/untitledui-logo.tsx +58 -0
- package/templates/dataif/services/web/src/hooks/use-breakpoint.ts +34 -0
- package/templates/dataif/services/web/src/hooks/use-resize-observer.ts +67 -0
- package/templates/dataif/services/web/src/main.jsx +14 -0
- package/templates/dataif/services/web/src/providers/theme-provider.jsx +62 -0
- package/templates/dataif/services/web/src/styles/globals.css +60 -0
- package/templates/dataif/services/web/src/styles/theme.css +1326 -0
- package/templates/dataif/services/web/src/styles/typography.css +430 -0
- package/templates/dataif/services/web/src/styles.css +1287 -0
- package/templates/dataif/services/web/src/utils/cx.ts +24 -0
- package/templates/dataif/services/web/src/utils/is-react-component.ts +33 -0
- package/templates/dataif/services/web/vite.config.js +14 -0
- package/templates/dataif/sql/ddl/001_schemas.sql +6 -0
- package/templates/dataif/sql/ddl/003_pnp_raw_staging_curated.sql +699 -0
- package/templates/dataif/sql/migrations/001_pnp_phase1_backfill.sql +3 -0
- package/templates/dataif/sql/migrations/002_pnp_phase2_admin_config_backfill.sql +184 -0
- package/templates/dataif/sql/migrations/003_pnp_phase3_raw_tabular_backfill.sql +3 -0
- package/templates/dataif/sql/migrations/004_pnp_phase3_raw_backfill_support_index.sql +3 -0
- package/templates/dataif/sql/migrations/005_pnp_phase7_staging_support_indexes.sql +2 -0
- package/templates/dataif/sql/migrations/006_pnp_phase7_staging_autovacuum_tuning.sql +2 -0
- package/templates/dataif/sql/migrations/007_pnp_phase7b_run_packages.sql +20 -0
- package/templates/dataif/sql/migrations/008_pnp_phase7a_pipeline_endpoints.sql +169 -0
- package/templates/dataif/sql/migrations/009_pnp_phase8_curated.sql +35 -0
- package/templates/dataif/sql/migrations/010_pnp_phase10_staging_incremental_upsert.sql +3 -0
- package/templates/dataif/sql/migrations/010_pnp_pipeline_uuid.sql +51 -0
- package/templates/dataif/sql/migrations/011_app_settings.sql +7 -0
- package/templates/dataif/sql/staging/020_pnp_matriculas.sql +112 -0
- package/templates/dataif/sql/staging/030_pnp_eficiencia_academica.sql +83 -0
- package/templates/dataif/sql/staging/040_pnp_servidores.sql +90 -0
- package/templates/dataif/sql/staging/050_pnp_financeiro.sql +72 -0
- package/templates/dataif/sql/views_curated/003_vw_pnp_microdados_admin.sql +160 -0
- package/templates/dataif/sql/views_curated/004_mv_pnp_dashboard_fast.sql +204 -0
- package/templates/dataif/sql/views_curated/010_vw_pnp_admin_ingestao.sql +51 -0
- package/templates/dataif/sql/views_curated/020_vw_pnp_qualidade_dados.sql +114 -0
- package/templates/dataif/sql/views_curated/030_vw_pnp_matriculas.sql +67 -0
- package/templates/dataif/sql/views_curated/040_vw_pnp_eficiencia.sql +33 -0
- package/templates/dataif/sql/views_curated/050_vw_pnp_servidores.sql +30 -0
- package/templates/dataif/sql/views_curated/060_vw_pnp_financeiro.sql +22 -0
- package/templates/dataif/sql/views_curated/070_vw_pnp_vanna.sql +115 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { isValidElement, useContext } from "react";
|
|
2
|
+
import { Check } from "@untitledui/icons";
|
|
3
|
+
import type { ListBoxItemProps as AriaListBoxItemProps } from "react-aria-components";
|
|
4
|
+
import { ListBoxItem as AriaListBoxItem, Text as AriaText } from "react-aria-components";
|
|
5
|
+
import { Avatar } from "@/components/base/avatar/avatar";
|
|
6
|
+
import { cx } from "@/utils/cx";
|
|
7
|
+
import { isReactComponent } from "@/utils/is-react-component";
|
|
8
|
+
import type { SelectItemType } from "./select";
|
|
9
|
+
import { SelectContext } from "./select";
|
|
10
|
+
|
|
11
|
+
const sizes = {
|
|
12
|
+
sm: "p-2 pr-2.5",
|
|
13
|
+
md: "p-2.5 pl-2",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
interface SelectItemProps extends Omit<AriaListBoxItemProps<SelectItemType>, "id">, SelectItemType {}
|
|
17
|
+
|
|
18
|
+
export const SelectItem = ({ label, id, value, avatarUrl, supportingText, isDisabled, icon: Icon, className, children, ...props }: SelectItemProps) => {
|
|
19
|
+
const { size } = useContext(SelectContext);
|
|
20
|
+
|
|
21
|
+
const labelOrChildren = label || (typeof children === "string" ? children : "");
|
|
22
|
+
const textValue = supportingText ? labelOrChildren + " " + supportingText : labelOrChildren;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<AriaListBoxItem
|
|
26
|
+
id={id}
|
|
27
|
+
value={
|
|
28
|
+
value ?? {
|
|
29
|
+
id,
|
|
30
|
+
label: labelOrChildren,
|
|
31
|
+
avatarUrl,
|
|
32
|
+
supportingText,
|
|
33
|
+
isDisabled,
|
|
34
|
+
icon: Icon,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
textValue={textValue}
|
|
38
|
+
isDisabled={isDisabled}
|
|
39
|
+
{...props}
|
|
40
|
+
className={(state) => cx("w-full px-1.5 py-px outline-hidden", typeof className === "function" ? className(state) : className)}
|
|
41
|
+
>
|
|
42
|
+
{(state) => (
|
|
43
|
+
<div
|
|
44
|
+
className={cx(
|
|
45
|
+
"flex cursor-pointer items-center gap-2 rounded-md outline-hidden select-none",
|
|
46
|
+
state.isSelected && "bg-active",
|
|
47
|
+
state.isDisabled && "cursor-not-allowed",
|
|
48
|
+
state.isFocused && "bg-primary_hover",
|
|
49
|
+
state.isFocusVisible && "ring-2 ring-focus-ring ring-inset",
|
|
50
|
+
|
|
51
|
+
// Icon styles
|
|
52
|
+
"*:data-icon:size-5 *:data-icon:shrink-0 *:data-icon:text-fg-quaternary",
|
|
53
|
+
state.isDisabled && "*:data-icon:text-fg-disabled",
|
|
54
|
+
|
|
55
|
+
sizes[size],
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{avatarUrl ? (
|
|
59
|
+
<Avatar aria-hidden="true" size="xs" src={avatarUrl} alt={label} />
|
|
60
|
+
) : isReactComponent(Icon) ? (
|
|
61
|
+
<Icon data-icon aria-hidden="true" />
|
|
62
|
+
) : isValidElement(Icon) ? (
|
|
63
|
+
Icon
|
|
64
|
+
) : null}
|
|
65
|
+
|
|
66
|
+
<div className="flex w-full min-w-0 flex-1 flex-wrap gap-x-2">
|
|
67
|
+
<AriaText
|
|
68
|
+
slot="label"
|
|
69
|
+
className={cx("truncate text-md font-medium whitespace-nowrap text-primary", state.isDisabled && "text-disabled")}
|
|
70
|
+
>
|
|
71
|
+
{label || (typeof children === "function" ? children(state) : children)}
|
|
72
|
+
</AriaText>
|
|
73
|
+
|
|
74
|
+
{supportingText && (
|
|
75
|
+
<AriaText slot="description" className={cx("text-md whitespace-nowrap text-tertiary", state.isDisabled && "text-disabled")}>
|
|
76
|
+
{supportingText}
|
|
77
|
+
</AriaText>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
{state.isSelected && (
|
|
82
|
+
<Check
|
|
83
|
+
aria-hidden="true"
|
|
84
|
+
className={cx(
|
|
85
|
+
"ml-auto text-fg-brand-primary",
|
|
86
|
+
size === "sm" ? "size-4 stroke-[2.5px]" : "size-5",
|
|
87
|
+
state.isDisabled && "text-fg-disabled",
|
|
88
|
+
)}
|
|
89
|
+
/>
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
</AriaListBoxItem>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type SelectHTMLAttributes, useId } from "react";
|
|
2
|
+
import { ChevronDown } from "@untitledui/icons";
|
|
3
|
+
import { HintText } from "@/components/base/input/hint-text";
|
|
4
|
+
import { Label } from "@/components/base/input/label";
|
|
5
|
+
import { cx } from "@/utils/cx";
|
|
6
|
+
|
|
7
|
+
interface NativeSelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|
8
|
+
label?: string;
|
|
9
|
+
hint?: string;
|
|
10
|
+
selectClassName?: string;
|
|
11
|
+
options: { label: string; value: string; disabled?: boolean }[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const NativeSelect = ({ label, hint, options, className, selectClassName, ...props }: NativeSelectProps) => {
|
|
15
|
+
const id = useId();
|
|
16
|
+
const selectId = `select-native-${id}`;
|
|
17
|
+
const hintId = `select-native-hint-${id}`;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className={cx("w-full in-data-input-wrapper:w-max", className)}>
|
|
21
|
+
{label && (
|
|
22
|
+
<Label htmlFor={selectId} id={selectId} className="mb-1.5">
|
|
23
|
+
{label}
|
|
24
|
+
</Label>
|
|
25
|
+
)}
|
|
26
|
+
|
|
27
|
+
<div className="relative grid w-full items-center">
|
|
28
|
+
<select
|
|
29
|
+
{...props}
|
|
30
|
+
id={selectId}
|
|
31
|
+
aria-describedby={hintId}
|
|
32
|
+
aria-labelledby={selectId}
|
|
33
|
+
className={cx(
|
|
34
|
+
"appearance-none rounded-lg bg-primary px-3.5 py-2.5 text-md font-medium text-primary shadow-xs ring-1 ring-primary outline-hidden transition duration-100 ease-linear ring-inset placeholder:text-fg-quaternary focus-visible:ring-2 focus-visible:ring-brand disabled:cursor-not-allowed disabled:bg-disabled_subtle disabled:text-disabled",
|
|
35
|
+
// Styles when the select is within an `InputGroup`
|
|
36
|
+
"in-data-input-wrapper:flex in-data-input-wrapper:h-full in-data-input-wrapper:gap-1 in-data-input-wrapper:bg-inherit in-data-input-wrapper:px-3 in-data-input-wrapper:py-2 in-data-input-wrapper:font-normal in-data-input-wrapper:text-tertiary in-data-input-wrapper:shadow-none in-data-input-wrapper:ring-transparent",
|
|
37
|
+
// Styles for the select when `TextField` is disabled
|
|
38
|
+
"in-data-input-wrapper:group-disabled:pointer-events-none in-data-input-wrapper:group-disabled:cursor-not-allowed in-data-input-wrapper:group-disabled:bg-transparent in-data-input-wrapper:group-disabled:text-disabled",
|
|
39
|
+
// Common styles for sizes and border radius within `InputGroup`
|
|
40
|
+
"in-data-input-wrapper:in-data-leading:rounded-r-none in-data-input-wrapper:in-data-trailing:rounded-l-none in-data-input-wrapper:in-data-[input-size=md]:py-2.5 in-data-input-wrapper:in-data-leading:in-data-[input-size=md]:pl-3.5 in-data-input-wrapper:in-data-[input-size=sm]:py-2 in-data-input-wrapper:in-data-[input-size=sm]:pl-3",
|
|
41
|
+
// For "leading" dropdown within `InputGroup`
|
|
42
|
+
"in-data-input-wrapper:in-data-leading:in-data-[input-size=md]:pr-4.5 in-data-input-wrapper:in-data-leading:in-data-[input-size=sm]:pr-4.5",
|
|
43
|
+
// For "trailing" dropdown within `InputGroup`
|
|
44
|
+
"in-data-input-wrapper:in-data-trailing:in-data-[input-size=md]:pr-8 in-data-input-wrapper:in-data-trailing:in-data-[input-size=sm]:pr-7.5",
|
|
45
|
+
selectClassName,
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
{options.map((opt) => (
|
|
49
|
+
<option key={opt.value} value={opt.value}>
|
|
50
|
+
{opt.label}
|
|
51
|
+
</option>
|
|
52
|
+
))}
|
|
53
|
+
</select>
|
|
54
|
+
<ChevronDown
|
|
55
|
+
aria-hidden="true"
|
|
56
|
+
className="pointer-events-none absolute right-3.5 size-5 text-fg-quaternary in-data-input-wrapper:right-0 in-data-input-wrapper:size-4 in-data-input-wrapper:stroke-[2.625px] in-data-input-wrapper:in-data-trailing:in-data-[input-size=sm]:right-3"
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{hint && (
|
|
61
|
+
<HintText className="mt-2" id={hintId}>
|
|
62
|
+
{hint}
|
|
63
|
+
</HintText>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { FC, ReactNode, Ref, RefAttributes } from "react";
|
|
2
|
+
import { createContext, isValidElement } from "react";
|
|
3
|
+
import { ChevronDown } from "@untitledui/icons";
|
|
4
|
+
import type { SelectProps as AriaSelectProps } from "react-aria-components";
|
|
5
|
+
import { Button as AriaButton, ListBox as AriaListBox, Select as AriaSelect, SelectValue as AriaSelectValue } from "react-aria-components";
|
|
6
|
+
import { Avatar } from "@/components/base/avatar/avatar";
|
|
7
|
+
import { HintText } from "@/components/base/input/hint-text";
|
|
8
|
+
import { Label } from "@/components/base/input/label";
|
|
9
|
+
import { cx } from "@/utils/cx";
|
|
10
|
+
import { isReactComponent } from "@/utils/is-react-component";
|
|
11
|
+
import { ComboBox } from "./combobox";
|
|
12
|
+
import { Popover } from "./popover";
|
|
13
|
+
import { SelectItem } from "./select-item";
|
|
14
|
+
|
|
15
|
+
export type SelectItemType = {
|
|
16
|
+
id: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
avatarUrl?: string;
|
|
19
|
+
isDisabled?: boolean;
|
|
20
|
+
supportingText?: string;
|
|
21
|
+
icon?: FC | ReactNode;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface CommonProps {
|
|
25
|
+
hint?: string;
|
|
26
|
+
label?: string;
|
|
27
|
+
tooltip?: string;
|
|
28
|
+
size?: "sm" | "md";
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface SelectProps extends Omit<AriaSelectProps<SelectItemType>, "children" | "items">, RefAttributes<HTMLDivElement>, CommonProps {
|
|
33
|
+
items?: SelectItemType[];
|
|
34
|
+
popoverClassName?: string;
|
|
35
|
+
placeholderIcon?: FC | ReactNode;
|
|
36
|
+
children: ReactNode | ((item: SelectItemType) => ReactNode);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface SelectValueProps {
|
|
40
|
+
isOpen: boolean;
|
|
41
|
+
size: "sm" | "md";
|
|
42
|
+
isFocused: boolean;
|
|
43
|
+
isDisabled: boolean;
|
|
44
|
+
placeholder?: string;
|
|
45
|
+
ref?: Ref<HTMLButtonElement>;
|
|
46
|
+
placeholderIcon?: FC | ReactNode;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const sizes = {
|
|
50
|
+
sm: { root: "py-2 px-3", shortcut: "pr-2.5" },
|
|
51
|
+
md: { root: "py-2.5 px-3.5", shortcut: "pr-3" },
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const SelectValue = ({ isOpen, isFocused, isDisabled, size, placeholder, placeholderIcon, ref }: SelectValueProps) => {
|
|
55
|
+
return (
|
|
56
|
+
<AriaButton
|
|
57
|
+
ref={ref}
|
|
58
|
+
className={cx(
|
|
59
|
+
"relative flex w-full cursor-pointer items-center rounded-lg bg-primary shadow-xs ring-1 ring-primary outline-hidden transition duration-100 ease-linear ring-inset",
|
|
60
|
+
(isFocused || isOpen) && "ring-2 ring-brand",
|
|
61
|
+
isDisabled && "cursor-not-allowed bg-disabled_subtle text-disabled",
|
|
62
|
+
)}
|
|
63
|
+
>
|
|
64
|
+
<AriaSelectValue<SelectItemType>
|
|
65
|
+
className={cx(
|
|
66
|
+
"flex h-max w-full items-center justify-start gap-2 truncate text-left align-middle",
|
|
67
|
+
|
|
68
|
+
// Icon styles
|
|
69
|
+
"*:data-icon:size-5 *:data-icon:shrink-0 *:data-icon:text-fg-quaternary in-disabled:*:data-icon:text-fg-disabled",
|
|
70
|
+
|
|
71
|
+
sizes[size].root,
|
|
72
|
+
)}
|
|
73
|
+
>
|
|
74
|
+
{(state) => {
|
|
75
|
+
const Icon = state.selectedItem?.icon || placeholderIcon;
|
|
76
|
+
return (
|
|
77
|
+
<>
|
|
78
|
+
{state.selectedItem?.avatarUrl ? (
|
|
79
|
+
<Avatar size="xs" src={state.selectedItem.avatarUrl} alt={state.selectedItem.label} />
|
|
80
|
+
) : isReactComponent(Icon) ? (
|
|
81
|
+
<Icon data-icon aria-hidden="true" />
|
|
82
|
+
) : isValidElement(Icon) ? (
|
|
83
|
+
Icon
|
|
84
|
+
) : null}
|
|
85
|
+
|
|
86
|
+
{state.selectedItem ? (
|
|
87
|
+
<section className="flex w-full gap-2 truncate">
|
|
88
|
+
<p className="truncate text-md font-medium text-primary">{state.selectedItem?.label}</p>
|
|
89
|
+
{state.selectedItem?.supportingText && <p className="text-md text-tertiary">{state.selectedItem?.supportingText}</p>}
|
|
90
|
+
</section>
|
|
91
|
+
) : (
|
|
92
|
+
<p className={cx("text-md text-placeholder", isDisabled && "text-disabled")}>{placeholder}</p>
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
<ChevronDown
|
|
96
|
+
aria-hidden="true"
|
|
97
|
+
className={cx("ml-auto shrink-0 text-fg-quaternary", size === "sm" ? "size-4 stroke-[2.5px]" : "size-5")}
|
|
98
|
+
/>
|
|
99
|
+
</>
|
|
100
|
+
);
|
|
101
|
+
}}
|
|
102
|
+
</AriaSelectValue>
|
|
103
|
+
</AriaButton>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const SelectContext = createContext<{ size: "sm" | "md" }>({ size: "sm" });
|
|
108
|
+
|
|
109
|
+
const Select = ({ placeholder = "Select", placeholderIcon, size = "sm", children, items, label, hint, tooltip, className, ...rest }: SelectProps) => {
|
|
110
|
+
return (
|
|
111
|
+
<SelectContext.Provider value={{ size }}>
|
|
112
|
+
<AriaSelect {...rest} className={(state) => cx("flex flex-col gap-1.5", typeof className === "function" ? className(state) : className)}>
|
|
113
|
+
{(state) => (
|
|
114
|
+
<>
|
|
115
|
+
{label && (
|
|
116
|
+
<Label isRequired={state.isRequired} tooltip={tooltip}>
|
|
117
|
+
{label}
|
|
118
|
+
</Label>
|
|
119
|
+
)}
|
|
120
|
+
|
|
121
|
+
<SelectValue {...state} {...{ size, placeholder }} placeholderIcon={placeholderIcon} />
|
|
122
|
+
|
|
123
|
+
<Popover size={size} className={rest.popoverClassName}>
|
|
124
|
+
<AriaListBox items={items} className="size-full outline-hidden">
|
|
125
|
+
{children}
|
|
126
|
+
</AriaListBox>
|
|
127
|
+
</Popover>
|
|
128
|
+
|
|
129
|
+
{hint && <HintText isInvalid={state.isInvalid}>{hint}</HintText>}
|
|
130
|
+
</>
|
|
131
|
+
)}
|
|
132
|
+
</AriaSelect>
|
|
133
|
+
</SelectContext.Provider>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const _Select = Select as typeof Select & {
|
|
138
|
+
ComboBox: typeof ComboBox;
|
|
139
|
+
Item: typeof SelectItem;
|
|
140
|
+
};
|
|
141
|
+
_Select.ComboBox = ComboBox;
|
|
142
|
+
_Select.Item = SelectItem;
|
|
143
|
+
|
|
144
|
+
export { _Select as Select };
|
package/templates/dataif/services/web/src/components/base/tags/base-components/tag-close-x.tsx
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { RefAttributes } from "react";
|
|
2
|
+
import { XClose } from "@untitledui/icons";
|
|
3
|
+
import { Button as AriaButton, type ButtonProps as AriaButtonProps } from "react-aria-components";
|
|
4
|
+
import { cx } from "@/utils/cx";
|
|
5
|
+
|
|
6
|
+
interface TagCloseXProps extends AriaButtonProps, RefAttributes<HTMLButtonElement> {
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const styles = {
|
|
12
|
+
sm: { root: "p-0.5", icon: "size-2.5" },
|
|
13
|
+
md: { root: "p-0.5", icon: "size-3" },
|
|
14
|
+
lg: { root: "p-0.75", icon: "size-3.5" },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const TagCloseX = ({ size = "md", className, ...otherProps }: TagCloseXProps) => {
|
|
18
|
+
return (
|
|
19
|
+
<AriaButton
|
|
20
|
+
slot="remove"
|
|
21
|
+
aria-label="Remove this tag"
|
|
22
|
+
className={cx(
|
|
23
|
+
"flex cursor-pointer rounded-[3px] text-fg-quaternary outline-transparent transition duration-100 ease-linear hover:bg-primary_hover hover:text-fg-quaternary_hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring disabled:cursor-not-allowed",
|
|
24
|
+
styles[size].root,
|
|
25
|
+
className,
|
|
26
|
+
)}
|
|
27
|
+
{...otherProps}
|
|
28
|
+
>
|
|
29
|
+
<XClose className={cx("transition-inherit-all", styles[size].icon)} strokeWidth="3" />
|
|
30
|
+
</AriaButton>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type {
|
|
3
|
+
ButtonProps as AriaButtonProps,
|
|
4
|
+
TooltipProps as AriaTooltipProps,
|
|
5
|
+
TooltipTriggerComponentProps as AriaTooltipTriggerComponentProps,
|
|
6
|
+
} from "react-aria-components";
|
|
7
|
+
import { Button as AriaButton, OverlayArrow as AriaOverlayArrow, Tooltip as AriaTooltip, TooltipTrigger as AriaTooltipTrigger } from "react-aria-components";
|
|
8
|
+
import { cx } from "@/utils/cx";
|
|
9
|
+
|
|
10
|
+
interface TooltipProps extends AriaTooltipTriggerComponentProps, Omit<AriaTooltipProps, "children"> {
|
|
11
|
+
/**
|
|
12
|
+
* The title of the tooltip.
|
|
13
|
+
*/
|
|
14
|
+
title: ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* The description of the tooltip.
|
|
17
|
+
*/
|
|
18
|
+
description?: ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Whether to show the arrow on the tooltip.
|
|
21
|
+
*
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
arrow?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Delay in milliseconds before the tooltip is shown.
|
|
27
|
+
*
|
|
28
|
+
* @default 300
|
|
29
|
+
*/
|
|
30
|
+
delay?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const Tooltip = ({
|
|
34
|
+
title,
|
|
35
|
+
description,
|
|
36
|
+
children,
|
|
37
|
+
arrow = false,
|
|
38
|
+
delay = 300,
|
|
39
|
+
closeDelay = 0,
|
|
40
|
+
trigger,
|
|
41
|
+
isDisabled,
|
|
42
|
+
isOpen,
|
|
43
|
+
defaultOpen,
|
|
44
|
+
offset = 6,
|
|
45
|
+
crossOffset,
|
|
46
|
+
placement = "top",
|
|
47
|
+
onOpenChange,
|
|
48
|
+
...tooltipProps
|
|
49
|
+
}: TooltipProps) => {
|
|
50
|
+
const isTopOrBottomLeft = ["top left", "top end", "bottom left", "bottom end"].includes(placement);
|
|
51
|
+
const isTopOrBottomRight = ["top right", "top start", "bottom right", "bottom start"].includes(placement);
|
|
52
|
+
// Set negative cross offset for left and right placement to visually balance the tooltip.
|
|
53
|
+
const calculatedCrossOffset = isTopOrBottomLeft ? -12 : isTopOrBottomRight ? 12 : 0;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<AriaTooltipTrigger {...{ trigger, delay, closeDelay, isDisabled, isOpen, defaultOpen, onOpenChange }}>
|
|
57
|
+
{children}
|
|
58
|
+
|
|
59
|
+
<AriaTooltip
|
|
60
|
+
{...tooltipProps}
|
|
61
|
+
offset={offset}
|
|
62
|
+
placement={placement}
|
|
63
|
+
crossOffset={crossOffset ?? calculatedCrossOffset}
|
|
64
|
+
className={({ isEntering, isExiting }) => cx(isEntering && "ease-out animate-in", isExiting && "ease-in animate-out")}
|
|
65
|
+
>
|
|
66
|
+
{({ isEntering, isExiting }) => (
|
|
67
|
+
<div
|
|
68
|
+
className={cx(
|
|
69
|
+
"z-50 flex max-w-xs origin-(--trigger-anchor-point) flex-col items-start gap-1 rounded-lg bg-primary-solid px-3 shadow-lg will-change-transform",
|
|
70
|
+
description ? "py-3" : "py-2",
|
|
71
|
+
|
|
72
|
+
isEntering &&
|
|
73
|
+
"ease-out animate-in fade-in zoom-in-95 in-placement-left:slide-in-from-right-0.5 in-placement-right:slide-in-from-left-0.5 in-placement-top:slide-in-from-bottom-0.5 in-placement-bottom:slide-in-from-top-0.5",
|
|
74
|
+
isExiting &&
|
|
75
|
+
"ease-in animate-out fade-out zoom-out-95 in-placement-left:slide-out-to-right-0.5 in-placement-right:slide-out-to-left-0.5 in-placement-top:slide-out-to-bottom-0.5 in-placement-bottom:slide-out-to-top-0.5",
|
|
76
|
+
)}
|
|
77
|
+
>
|
|
78
|
+
<span className="text-xs font-semibold text-white">{title}</span>
|
|
79
|
+
|
|
80
|
+
{description && <span className="text-xs font-medium text-tooltip-supporting-text">{description}</span>}
|
|
81
|
+
|
|
82
|
+
{arrow && (
|
|
83
|
+
<AriaOverlayArrow>
|
|
84
|
+
<svg
|
|
85
|
+
viewBox="0 0 100 100"
|
|
86
|
+
className="size-2.5 fill-bg-primary-solid in-placement-left:-rotate-90 in-placement-right:rotate-90 in-placement-top:rotate-0 in-placement-bottom:rotate-180"
|
|
87
|
+
>
|
|
88
|
+
<path d="M0,0 L35.858,35.858 Q50,50 64.142,35.858 L100,0 Z" />
|
|
89
|
+
</svg>
|
|
90
|
+
</AriaOverlayArrow>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
</AriaTooltip>
|
|
95
|
+
</AriaTooltipTrigger>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
interface TooltipTriggerProps extends AriaButtonProps {}
|
|
100
|
+
|
|
101
|
+
export const TooltipTrigger = ({ children, className, ...buttonProps }: TooltipTriggerProps) => {
|
|
102
|
+
return (
|
|
103
|
+
<AriaButton {...buttonProps} className={(values) => cx("h-max w-max outline-hidden", typeof className === "function" ? className(values) : className)}>
|
|
104
|
+
{children}
|
|
105
|
+
</AriaButton>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react";
|
|
2
|
+
|
|
3
|
+
const sizes = {
|
|
4
|
+
sm: {
|
|
5
|
+
wh: 8,
|
|
6
|
+
c: 4,
|
|
7
|
+
r: 2.5,
|
|
8
|
+
},
|
|
9
|
+
md: {
|
|
10
|
+
wh: 10,
|
|
11
|
+
c: 5,
|
|
12
|
+
r: 4,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const Dot = ({ size = "md", ...props }: HTMLAttributes<HTMLOrSVGElement> & { size?: "sm" | "md" }) => {
|
|
17
|
+
return (
|
|
18
|
+
<svg width={sizes[size].wh} height={sizes[size].wh} viewBox={`0 0 ${sizes[size].wh} ${sizes[size].wh}`} fill="none" {...props}>
|
|
19
|
+
<circle cx={sizes[size].c} cy={sizes[size].c} r={sizes[size].r} fill="currentColor" stroke="currentColor" />
|
|
20
|
+
</svg>
|
|
21
|
+
);
|
|
22
|
+
};
|