Skip to content

Commit

Permalink
feat: Implement Data frame editor. WF-66
Browse files Browse the repository at this point in the history
  • Loading branch information
madeindjs committed Oct 3, 2024
1 parent 522c405 commit f433f64
Show file tree
Hide file tree
Showing 19 changed files with 1,228 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{"id": "d1e01ce1-fab1-4a6e-91a1-1f45f9e57aa5", "type": "column", "content": {"width": "1", "isCollapsible": "", "title": "", "isSticky": "yes"}, "isCodeManaged": false, "position": 0, "parentId": "92a2c0c8-7ab4-4865-b7eb-ed437408c8f5"}
{"id": "0569937e-c72c-4fb9-820e-2ae56e17bcc0", "type": "column", "content": {"width": "1.61"}, "isCodeManaged": false, "position": 1, "parentId": "92a2c0c8-7ab4-4865-b7eb-ed437408c8f5"}
{"id": "9c30af6d-4ee5-4782-9169-0f361d67fa76", "type": "section", "content": {"title": ""}, "isCodeManaged": false, "position": 0, "parentId": "d1e01ce1-fab1-4a6e-91a1-1f45f9e57aa5"}
{"id": "7fdd1d02-53de-4466-bd3c-4822cbc2694f", "type": "image", "content": {"src": "static/pigeon1.jpg?8", "caption": ""}, "isCodeManaged": false, "position": 0, "parentId": "9c30af6d-4ee5-4782-9169-0f361d67fa76"}
{"id": "7fdd1d02-53de-4466-bd3c-4822cbc2694f", "type": "image", "content": {"src": "static/pigeon1.jpg?8", "caption": ""}, "isCodeManaged": false, "position": 0, "parentId": "9c30af6d-4ee5-4782-9169-0f361d67fa76", "visible": {"expression": true, "binding": "", "reversed": false}}
{"id": "ee919cd6-8153-4f34-8c6a-bfc1153df360", "type": "tabs", "content": {}, "isCodeManaged": false, "position": 0, "parentId": "0569937e-c72c-4fb9-820e-2ae56e17bcc0"}
{"id": "c6392876-7cfd-4680-8725-b04f43ff294f", "type": "tab", "content": {"name": "Data and Charts"}, "isCodeManaged": false, "position": 0, "parentId": "ee919cd6-8153-4f34-8c6a-bfc1153df360"}
{"id": "da00a61f-0ee2-434e-acd6-228d32eae5c6", "type": "tab", "content": {"name": "Repeater"}, "isCodeManaged": false, "position": 2, "parentId": "ee919cd6-8153-4f34-8c6a-bfc1153df360"}
Expand Down Expand Up @@ -74,3 +74,8 @@
{"id": "zfp1koasiuleygmz", "type": "pagination", "content": {"page": "@{paginated_members_page}", "pageSize": "@{paginated_members_page_size}", "totalItems": "@{paginated_members_total_items}", "pageSizeOptions": "1,2,5", "pageSizeShowAll": "no", "jumpTo": "no", "urlParam": "no"}, "isCodeManaged": false, "position": 1, "parentId": "e1ax8ctt8lrao0e4", "handlers": {"wf-change-page": "handle_paginated_members_page_change", "wf-change-page-size": "handle_paginated_members_page_size_change"}, "visible": {"expression": true, "binding": "", "reversed": false}}
{"id": "b27lw9ex8ig3x17p", "type": "tags", "content": {"tags": "{\n \"fiction\": \"fiction\",\n \"inspirational\": \"inspirational\",\n \"ai-generated\": \"ai-generated\"\n}", "seed": ""}, "isCodeManaged": false, "position": 2, "parentId": "9c30af6d-4ee5-4782-9169-0f361d67fa76", "visible": {"expression": true, "binding": "", "reversed": false}}
{"id": "804e15bf-11a7-463d-8082-f46ea3acac1b", "type": "separator", "content": {}, "isCodeManaged": false, "position": 3, "parentId": "9c30af6d-4ee5-4782-9169-0f361d67fa76"}
{"id": "1sajvc1p1c293w26", "type": "dataframe", "content": {"dataframe": "@{editable_df}", "enableEdit": "yes", "enableRecordAdd": "yes", "enableRecordUpdate": "yes", "enableSearch": "yes", "showIndex": "yes", "actions": "{\n \"remove\": \"Remove\",\n \"open\": \"Open\",\n \"notify\": \"Notify\"\n}", "enableDownload": "yes"}, "isCodeManaged": false, "position": 1, "parentId": "axdad4l298047mxv", "handlers": {"wf-dataframe-add": "on_editable_df_record_add", "wf-dataframe-update": "on_editable_df_record_change", "wf-dataframe-action": "on_editable_df_record_action"}}
{"id": "xmbpsuxhu7om9165", "type": "text", "content": {"text": "Opened row text: @{editable_df_open_text}\n(use row action from the dataframe to open a row)"}, "isCodeManaged": false, "position": 2, "parentId": "axdad4l298047mxv", "handlers": {}}
{"id": "cqmyovmwxnxp7ffz", "type": "tabs", "content": {}, "isCodeManaged": false, "position": 1, "parentId": "0569937e-c72c-4fb9-820e-2ae56e17bcc0", "handlers": {}}
{"id": "axdad4l298047mxv", "type": "tab", "content": {"name": "Dataframe"}, "isCodeManaged": false, "position": 5, "parentId": "ee919cd6-8153-4f34-8c6a-bfc1153df360", "handlers": {}}
{"id": "3nluyquva0mt1a41", "type": "text", "content": {"text": "You can use Dataframe to visualize and edit Python Panda's data"}, "isCodeManaged": false, "position": 0, "parentId": "axdad4l298047mxv", "handlers": {}}
2 changes: 1 addition & 1 deletion apps/hello/.wf/metadata.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"writer_version": "0.7.0rc2"
"writer_version": "0.7.5"
}
39 changes: 38 additions & 1 deletion apps/hello/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import statistics

import numpy as np
Expand Down Expand Up @@ -65,6 +66,17 @@ def _get_main_df():
main_df = pd.read_csv("assets/main_df.csv")
return main_df


def _get_editable_df():

df = pd.DataFrame({
'number': [1, 2, 3],
'boolean': [True, False, True],
'object': [{"updatedAt": None}, {"updatedAt": None}, {"updatedAt": None}],
'text': ['one', 'two', 'three'],
})
return wf.EditableDataframe(df)

def _get_highlighted_members():
sample_df = _get_main_df().sample(3).set_index("name", drop=False)
sample = sample_df.to_dict("index")
Expand Down Expand Up @@ -126,11 +138,36 @@ def _update_scatter_chart(state):
)
state["scatter_chart"] = fig

# STATE INIT

# UPDATES DATAFRAME

# Subscribe this event handler to the `wf-dataframe-add` event
def on_editable_df_record_add(state, payload):
state['editable_df'].record_add(payload)


# Subscribe this event handler to the `wf-dataframe-update` event
def on_editable_df_record_change(state, payload):
payload['record']['object']['updatedAt'] = str(datetime.datetime.now())
state['editable_df'].record_update(payload)

# Subscribe this event handler to the `wf-dataframe-action` event
def on_editable_df_record_action(state, payload):
record_index = payload['record_index']
if payload['action'] == 'remove':
state['editable_df'].record_remove(payload)
if payload['action'] == 'open':
state['editable_df_open_text'] = str(state['editable_df'].record(record_index)['text'])
if payload['action'] == 'notify':
state.add_notification("info", "Test", "Notify on this row : " + str(state['editable_df'].record(record_index)))


# STATE INIT

initial_state = wf.init_state({
"main_df": _get_main_df(),
"editable_df": _get_editable_df(),
"editable_df_open_text": "<none>",
"highlighted_members": _get_highlighted_members(),
"random_df": _generate_random_df(),
"hue_rotation": 26,
Expand Down
4 changes: 2 additions & 2 deletions docs/framework/dataframe.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ def on_record_action(state, payload):
This event corresponds to a quick action in the drop-down menu to the left of the dataframe.
"""
record_index = payload['record_index']
if payload.action == 'remove':
if payload['action'] == 'remove':
state['mydf'].record_remove(payload)
if payload.action == 'open':
if payload['action'] == 'open':
state['record'] = state['df'].record(record_index) # dict representation of record
```

Expand Down
133 changes: 133 additions & 0 deletions src/ui/src/components/core/base/BaseDropdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<script setup lang="ts">
import { onMounted, onUnmounted, PropType, ref } from "vue";
defineProps({
options: {
type: Object as PropType<Record<string, string>>,
required: true,
},
});
const emits = defineEmits({
selected: (key: string) => typeof key === "string",
});
/**
* @deprecated use `useId` from Vue 3.5 when we upgrade
*/
function useId() {
return Math.random().toString();
}
const trigger = ref<Element>();
const popoverId = useId();
const popover = ref<Element & { hidePopover: () => void }>();
const closePopover = () => popover.value?.hidePopover();
const popoverTop = ref("unset");
const popoverLeft = ref("unset");
function computePopoverPosition() {
// the popover use an overlay with an absolute position, so we have to positionate it manually according to the trigger position
const boundingRect = trigger.value.getBoundingClientRect();
popoverTop.value = `${boundingRect.top + boundingRect.height + 4 + window.pageYOffset}px`;
popoverLeft.value = `${boundingRect.left + window.pageXOffset}px`;
}
function onItemClick(key: string) {
emits("selected", key);
closePopover();
}
onMounted(() => window.addEventListener("scroll", closePopover));
onUnmounted(() => window.removeEventListener("scroll", closePopover));
</script>

<template>
<div class="BaseDropdown">
<button
ref="trigger"
:popovertarget="popoverId"
class="BaseDropdown__trigger"
@click="computePopoverPosition"
>
<i class="material-symbols-outlined">more_horiz</i>
</button>
<div
:id="popoverId"
ref="popover"
class="BaseDropdown__dropdown"
popover
:style="{ top: popoverTop, left: popoverLeft }"
>
<button
v-for="[key, value] of Object.entries(options)"
:key="key"
class="BaseDropdown__dropdown_item"
tabindex="1"
@click.capture="onItemClick(key)"
>
{{ value }}
</button>
</div>
</div>
</template>

<style scoped>
.BaseDropdown {
position: relative;
}
.BaseDropdown__trigger {
cursor: pointer;
background-color: var(--separatorColor);
border: 1px solid var(--emptinessColor);
height: 16px;
width: 16px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
.BaseDropdown__dropdown {
position: absolute;
z-index: 1;
top: 18px;
background-color: white;
flex-direction: column;
min-width: 120px;
margin: 0;
border-radius: 4px;
border: 1px solid var(--separatorColor);
box-shadow: var(--containerShadow);
}
.BaseDropdown__dropdown:popover-open {
display: flex;
}
.BaseDropdown__dropdown_item {
/* reset button */
background-color: transparent;
border: none;
text-align: left;
padding: 8px 16px;
border-bottom: 1px solid var(--separatorColor);
cursor: pointer;
}
.BaseDropdown__dropdown_item:focus {
outline-offset: -2px;
}
.BaseDropdown__dropdown_item:hover {
background-color: var(--separatorColor);
}
.BaseDropdown__dropdown_item:last-child {
border-bottom: unset;
}
</style>
Loading

0 comments on commit f433f64

Please sign in to comment.