Skip to content

Commit

Permalink
fix: icon type auto-generation and normalization (#696)
Browse files Browse the repository at this point in the history
Closes #601 
Closes #784 


Updated deephaven/ui README with instructions on how to run the script.
Wasn't certain if it should run everytime the docker container is built?
  • Loading branch information
ethanalvizo authored Sep 6, 2024
1 parent 1807862 commit ef4bb29
Show file tree
Hide file tree
Showing 6 changed files with 2,570 additions and 6 deletions.
13 changes: 12 additions & 1 deletion plugins/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,15 @@ python make_docs.py
```

The files will be built into `docs/build/markdown`.
Note that these built files should not be committed to the repository.
Note that these built files should not be committed to the repository.

## Update Icon Types
Available IconTypes can be generated automatically using icon TypeScript definitions in node_modules.

Writes to `icon_types.py`.

```shell
npm install
cd plugins/ui
python make_icon_types.py
```
67 changes: 67 additions & 0 deletions plugins/ui/make_icon_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import re
from typing import Dict, Any


def camel_to_snake(name: str) -> str:
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()


relative_path = "./src/js/node_modules/@deephaven/icons/dist/index.d.ts"
icon_pattern = r"^export const (\w+): IconDefinition;$"

icons = {}
snakeCase = {}
noPrefix = {}
snakeCaseNoPrefix = {}

with open(relative_path, "r") as file:
for line in file:
match = re.match(icon_pattern, line)
if match:
icon = line.split(" ")[2].strip()[:-1]
if icon != "IconDefinition":
icons[icon] = icon
snakeCase[camel_to_snake(icon)] = icon

isVsIcon = icon.startswith("vs")
noPrefixIcon = icon[2:]
snakeCaseNoPrefixIcon = camel_to_snake(noPrefixIcon)

## DH Icons are always prefixed with "dh"
if isVsIcon:
noPrefix[noPrefixIcon] = icon
snakeCaseNoPrefix[snakeCaseNoPrefixIcon] = icon

output_file_path = "./src/deephaven/ui/components/types/icon_types.py"

with open(output_file_path, "w") as output_file:
output_file.truncate(0)

output_file.write(
"from __future__ import annotations\n" + "from typing import Literal\n\n"
)

## IconTypes
output_file.write("IconTypes = Literal[\n")
for key, value in snakeCaseNoPrefix.items():
output_file.write(' "' + key + '",' + "\n")
for key, value in snakeCase.items():
if key.startswith("dh"):
output_file.write(' "' + key + '",' + "\n")
output_file.write("]" + "\n")

## IconMapping
output_file.write("\n")
output_file.write("IconMapping = {" + "\n")
for dict in [icons, noPrefix, snakeCase, snakeCaseNoPrefix]:
for key, value in dict.items():
output_file.write(' "' + key + '": "' + value + '",' + "\n")
output_file.write("}" + "\n")


print(f"Generated file: {output_file_path}")
print(f"Total number of icon types: {len(snakeCaseNoPrefix)}")
print(
f"Total number of icon mappings: {len(icons) + len(noPrefix) + len(snakeCase) + len(snakeCaseNoPrefix)}"
)
7 changes: 5 additions & 2 deletions plugins/ui/src/deephaven/ui/components/icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
Position,
IconSize,
IconColor,
IconTypes,
IconMapping,
)
from .._internal.utils import create_props


def icon(
name: str,
name: IconTypes,
size: IconSize | None = None,
color: IconColor | None = None,
flex: LayoutFlex | None = None,
Expand Down Expand Up @@ -114,4 +116,5 @@ def icon(
"""

children, props = create_props(locals())
return BaseElement(f"deephaven.ui.icons.{name}", *children, **props)
normalized_name = IconMapping[name]
return BaseElement(f"deephaven.ui.icons.{normalized_name}", *children, **props)
6 changes: 3 additions & 3 deletions plugins/ui/src/deephaven/ui/components/text_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@
NecessityIndicator,
)

from ..types import Icon
from .types import IconTypes
from .basic import component_element
from ..elements import Element

from .icon import icon as icon_component


def text_area(
icon: Element | Icon | None = None,
icon: Element | IconTypes | None = None,
is_quiet: bool | None = None,
is_disabled: bool | None = None,
is_read_only: bool | None = None,
Expand Down Expand Up @@ -193,7 +193,7 @@ def text_area(

return component_element(
"TextArea",
icon=icon_component(icon) if type(icon) == str else icon,
icon=icon_component(name=icon) if type(icon) == str else icon,
is_quiet=is_quiet,
is_disabled=is_disabled,
is_read_only=is_read_only,
Expand Down
1 change: 1 addition & 0 deletions plugins/ui/src/deephaven/ui/components/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
from .events import *
from .layout import *
from .validate import *
from .icon_types import *
Loading

0 comments on commit ef4bb29

Please sign in to comment.