mirror of
https://github.com/viewflow/viewflow.git
synced 2026-03-13 10:32:34 +08:00
Changes sync
This commit is contained in:
15
package-lock.json
generated
15
package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"@hotwired/turbo": "^7.3.0",
|
||||
"@material/textfield": "^14.0.0",
|
||||
"@material/web": "^0.1.0-alpha.2",
|
||||
"@open-wc/lit-helpers": "^0.6.0",
|
||||
"material-components-web": "^14.0.0",
|
||||
"material-icons": "^1.13.3",
|
||||
"perfect-scrollbar": "^1.5.5",
|
||||
@@ -1667,6 +1668,14 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@open-wc/lit-helpers": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@open-wc/lit-helpers/-/lit-helpers-0.6.0.tgz",
|
||||
"integrity": "sha512-9F0Rw18Lupp8hehF299yYozN4cFMTnHeCVNtz0k18/eUkcUUb6DCWerL/ASJ9lZ4bLA/YUPmrkdgZz/xe9cKeg==",
|
||||
"peerDependencies": {
|
||||
"lit": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||
@@ -8355,6 +8364,12 @@
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@open-wc/lit-helpers": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@open-wc/lit-helpers/-/lit-helpers-0.6.0.tgz",
|
||||
"integrity": "sha512-9F0Rw18Lupp8hehF299yYozN4cFMTnHeCVNtz0k18/eUkcUUb6DCWerL/ASJ9lZ4bLA/YUPmrkdgZz/xe9cKeg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@popperjs/core": {
|
||||
"version": "2.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"@hotwired/turbo": "^7.3.0",
|
||||
"@material/textfield": "^14.0.0",
|
||||
"@material/web": "^0.1.0-alpha.2",
|
||||
"@open-wc/lit-helpers": "^0.6.0",
|
||||
"material-components-web": "^14.0.0",
|
||||
"material-icons": "^1.13.3",
|
||||
"perfect-scrollbar": "^1.5.5",
|
||||
|
||||
3
tox.ini
3
tox.ini
@@ -97,8 +97,9 @@ envdir=.venv310
|
||||
basepython=python3.8
|
||||
envdir = {toxworkdir}/docs
|
||||
deps =
|
||||
Sphinx
|
||||
Sphinx==6.2.1
|
||||
sphinxcontrib-fulltoc
|
||||
docutils==0.19
|
||||
https://github.com/guzzle/guzzle_sphinx_theme/archive/master.zip
|
||||
https://github.com/joh/when-changed/archive/master.zip
|
||||
{[testenv]deps}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"module": "ES6",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"declaration": false,
|
||||
"outDir": "./types",
|
||||
@@ -18,7 +18,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"useDefineForClassFields": false,
|
||||
"skipLibCheck": true
|
||||
"skipLibCheck": true,
|
||||
"target": "ES5",
|
||||
},
|
||||
"include": ["viewflow/js/**/*.ts"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
|
||||
@@ -72,7 +72,8 @@ export default class VFDateUtils {
|
||||
result += value.getFullYear();
|
||||
break;
|
||||
case 'I':
|
||||
result += ('0' + (value.getHours()%12)).slice(-2);
|
||||
const twelveHour = value.getHours() % 12 || 12;
|
||||
result += ('0' + twelveHour).slice(-2);
|
||||
break;
|
||||
case 'H':
|
||||
result += ('0' + value.getHours()).slice(-2);
|
||||
@@ -84,7 +85,7 @@ export default class VFDateUtils {
|
||||
result += ('0' + value.getSeconds()).slice(-2);
|
||||
break;
|
||||
case 'p':
|
||||
result += value.getHours() > 12 ? 'pm': 'am';
|
||||
result += value.getHours() >= 12 ? 'pm': 'am';
|
||||
}
|
||||
i++;
|
||||
} else {
|
||||
@@ -111,6 +112,12 @@ export default class VFDateUtils {
|
||||
case '%Y':
|
||||
year = date[i];
|
||||
break;
|
||||
case '%b':
|
||||
month = VFDateUtils.monthsOfYearAbbr.indexOf(date[i]);
|
||||
if (month === -1) {
|
||||
throw new Error(`Invalid month abbreviation: ${date[i]}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new Date(Date.UTC(year, month, day));
|
||||
|
||||
@@ -61,7 +61,9 @@ const VFileInputField = customElement('vf-field-file', defaultProps, (props, {el
|
||||
<input
|
||||
class="vf-field__file-input"
|
||||
multiple={props.multiple}
|
||||
type="file" {...props}
|
||||
type="file"
|
||||
{...props}
|
||||
value=""
|
||||
name={props.name}
|
||||
id={props.id}
|
||||
required={props.required}
|
||||
|
||||
@@ -7,7 +7,7 @@ import cc from 'classcat';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const defaultProps = {
|
||||
export const defaultProps = {
|
||||
'autofocus': undefined,
|
||||
'disabled': false,
|
||||
'error': undefined,
|
||||
@@ -24,6 +24,7 @@ const defaultProps = {
|
||||
'trailingIcon': undefined,
|
||||
'type': 'text',
|
||||
'value': '',
|
||||
'readonly': undefined
|
||||
};
|
||||
|
||||
const HelpText = (props) => {
|
||||
@@ -100,6 +101,7 @@ const Input = (props) => {
|
||||
type={ props.type }
|
||||
tabindex={ props.tabIndex }
|
||||
value={ props.value }
|
||||
readonly= {props.readonly }
|
||||
aria-labelledby={ props.id + '_label' }
|
||||
oninput={props.onInput}
|
||||
onChange={props.onChange}
|
||||
|
||||
102
viewflow/static/viewflow/js/viewflow.min.js
vendored
102
viewflow/static/viewflow/js/viewflow.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -7,7 +7,25 @@ from . import views, mixins, utils
|
||||
|
||||
class Start(mixins.NodeDetailMixin, mixins.NodeUndoMixin, nodes.Start):
|
||||
"""
|
||||
The Start node in a flow.
|
||||
The ``Start`` node in a flow.
|
||||
|
||||
This node is used as the initial step in a flow by a user.
|
||||
|
||||
`Live Demo <https://demo.viewflow.io/workflow/flows/helloworld/start/>`_ /
|
||||
`Cookbook sample <https://github.com/viewflow/cookbook/blob/main/workflow101/helloworld/flows.py>`_
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyFlow(flow.Flow):
|
||||
start = (
|
||||
flow.Start(views.CreateProcessView.as_view(fields=["text"]))
|
||||
.Annotation(title=_("New message"))
|
||||
.Permission(auto_create=True)
|
||||
.Next(this.approve)
|
||||
)
|
||||
|
||||
...
|
||||
|
||||
"""
|
||||
|
||||
index_view_class = views.IndexTaskView
|
||||
@@ -28,6 +46,26 @@ class Start(mixins.NodeDetailMixin, mixins.NodeUndoMixin, nodes.Start):
|
||||
|
||||
|
||||
class StartHandle(mixins.NodeDetailMixin, mixins.NodeUndoMixin, nodes.StartHandle):
|
||||
"""
|
||||
The ``Start`` handle node in a flow.
|
||||
|
||||
This node is used as the initial step in a flow from code
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyFlow(flow.Flow):
|
||||
start = flow.StartHandle(this.on_start_process).Next(this.approve)
|
||||
|
||||
def start_process(self, activation, sample=False):
|
||||
activation.process.sample = sample
|
||||
return activation.process
|
||||
|
||||
...
|
||||
|
||||
process = MyFlow.start.run(sample=True)
|
||||
|
||||
"""
|
||||
|
||||
index_view_class = views.IndexTaskView
|
||||
detail_view_class = views.DetailTaskView
|
||||
undo_view_class = views.UndoTaskView
|
||||
@@ -36,6 +74,21 @@ class StartHandle(mixins.NodeDetailMixin, mixins.NodeUndoMixin, nodes.StartHandl
|
||||
class End(
|
||||
mixins.NodeDetailMixin, mixins.NodeUndoMixin, mixins.NodeReviveMixin, nodes.End
|
||||
):
|
||||
"""
|
||||
The ``End`` node in a flow.
|
||||
|
||||
This node serves as the terminal point of a flow
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyFlow(flow.Flow):
|
||||
...
|
||||
|
||||
approved = this.End()
|
||||
rejected = this.End()
|
||||
|
||||
"""
|
||||
|
||||
index_view_class = views.IndexTaskView
|
||||
detail_view_class = views.DetailTaskView
|
||||
undo_view_class = views.UndoTaskView
|
||||
@@ -49,6 +102,31 @@ class View(
|
||||
mixins.NodeReviveMixin,
|
||||
nodes.View,
|
||||
):
|
||||
"""
|
||||
Represents a user-interaction node within a flow
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyFlow(flow.Flow):
|
||||
...
|
||||
|
||||
approve = (
|
||||
flow.View(views.UpdateProcessView.as_view(fields=["approved"]))
|
||||
.Annotation(
|
||||
title=_("Approve"),
|
||||
description=_("Supervisor approvement"),
|
||||
summary_template=_("Message review required"),
|
||||
result_template=_(
|
||||
"Message was {{ process.approved|yesno:'Approved,Rejected' }}"
|
||||
),
|
||||
)
|
||||
.Permission(auto_create=True)
|
||||
.Next(this.check_approve)
|
||||
)
|
||||
|
||||
...
|
||||
"""
|
||||
|
||||
index_view_class = views.UserIndexTaskView
|
||||
detail_view_class = views.DetailTaskView
|
||||
cancel_view_class = views.CancelTaskView
|
||||
@@ -119,6 +197,24 @@ class View(
|
||||
class If(
|
||||
mixins.NodeDetailMixin, mixins.NodeUndoMixin, mixins.NodeReviveMixin, nodes.If
|
||||
):
|
||||
"""
|
||||
The If-gate
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyFlow(flow.Flow):
|
||||
...
|
||||
|
||||
check_approve = (
|
||||
flow.If(lambda activation: activation.process.approved)
|
||||
.Annotation(title=_("Approvement check"))
|
||||
.Then(this.send)
|
||||
.Else(this.end)
|
||||
)
|
||||
|
||||
. ...
|
||||
"""
|
||||
|
||||
index_view_class = views.IndexTaskView
|
||||
detail_view_class = views.DetailTaskView
|
||||
undo_view_class = views.UndoTaskView
|
||||
@@ -138,6 +234,22 @@ class Handle(
|
||||
mixins.NodeReviveMixin,
|
||||
nodes.Handle,
|
||||
):
|
||||
"""
|
||||
Represents a task executed from the other parts of code
|
||||
|
||||
Usage:
|
||||
To define a handle in a flow and run it:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyFlow(flow.Flow):
|
||||
...
|
||||
my_handle = flow.Handle().Next(this.join_gate)
|
||||
|
||||
task = task=process.task_set.get(flow_task=MyFlow.my_handle, status=STATUS.NEW),
|
||||
MyFlow.my_handle.run(task)
|
||||
"""
|
||||
|
||||
index_view_class = views.IndexTaskView
|
||||
detail_view_class = views.DetailTaskView
|
||||
cancel_view_class = views.CancelTaskView
|
||||
|
||||
@@ -222,7 +222,16 @@ class BulkActionsViewsMixin(metaclass=ViewsetMeta):
|
||||
|
||||
|
||||
class FlowViewset(BaseFlowViewsMixin, AppMenuMixin, Viewset):
|
||||
"""Basic flow viewset."""
|
||||
"""
|
||||
Basic flow viewset to include single flow into url patterns
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
urlpatterns = [path("my_flow/", FlowViewset(MyFlow).urls)]
|
||||
|
||||
"""
|
||||
|
||||
def _get_urls(self):
|
||||
own_patterns = super()._get_urls()
|
||||
@@ -248,7 +257,28 @@ class FlowViewset(BaseFlowViewsMixin, AppMenuMixin, Viewset):
|
||||
|
||||
|
||||
class FlowAppViewset(BaseFlowViewsMixin, BulkActionsViewsMixin, Application):
|
||||
"""Viewset includes flow as an separate App into Site."""
|
||||
"""
|
||||
Viewset includes flow as an separate App into Site.
|
||||
|
||||
`Cookbook sample <https://github.com/viewflow/cookbook/blob/main/workflow101/config/urls.py>`_
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
site = Site(
|
||||
viewsets=[
|
||||
FlowAppViewset(
|
||||
ShipmentFlow,
|
||||
icon="local_shipping",
|
||||
viewsets=[
|
||||
ShipmentCRUDViewset()
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
urlpatterns = [path("", site.urls)]
|
||||
"""
|
||||
|
||||
menu_template_name = "viewflow/workflow/flow_menu.html"
|
||||
base_template_name = "viewflow/workflow/base_page.html"
|
||||
@@ -385,7 +415,30 @@ class NestedFlowsApp(AppMenuMixin, Application):
|
||||
|
||||
|
||||
class WorkflowAppViewset(BulkActionsViewsMixin, Application):
|
||||
""" """
|
||||
"""
|
||||
Viewset includes multiples flow with common Inbox/Queue/Archive views as an
|
||||
separate App into Site.
|
||||
|
||||
`Life demo
|
||||
<https://demo.viewflow.io/workflow/inbox/>`_
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
site = Site(
|
||||
viewsets=[
|
||||
WorkflowAppViewset(
|
||||
flow_viewsets=[
|
||||
FlowViewset(HelloWorldFlow, icon="assignment"),
|
||||
FlowViewset(ShipmentFlow, icon="local_shipping"),
|
||||
FlowViewset(DynamicSplitFlow, icon="tune"),
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
urlpatterns = [path("", site.urls)]
|
||||
"""
|
||||
|
||||
app_name = "workflow"
|
||||
icon = "assignment"
|
||||
|
||||
Reference in New Issue
Block a user