mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Merge pull request #3281 from makeplane/preview
release: preview to master
This commit is contained in:
commit
28a9c53202
25
.github/workflows/build-branch.yml
vendored
25
.github/workflows/build-branch.yml
vendored
@ -9,6 +9,7 @@ on:
|
|||||||
- preview
|
- preview
|
||||||
- qa
|
- qa
|
||||||
- develop
|
- develop
|
||||||
|
- release-*
|
||||||
release:
|
release:
|
||||||
types: [released, prereleased]
|
types: [released, prereleased]
|
||||||
|
|
||||||
@ -62,14 +63,14 @@ jobs:
|
|||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs: [branch_build_setup]
|
needs: [branch_build_setup]
|
||||||
env:
|
env:
|
||||||
FRONTEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:${{ needs.branch_build_setup.outputs.gh_branch_name }}
|
FRONTEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ needs.branch_build_setup.outputs.gh_branch_name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Set Frontend Docker Tag
|
- name: Set Frontend Docker Tag
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then
|
if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then
|
||||||
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:${{ github.event.release.tag_name }}
|
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ github.event.release.tag_name }}
|
||||||
elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then
|
elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then
|
||||||
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:stable
|
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:stable
|
||||||
else
|
else
|
||||||
TAG=${{ env.FRONTEND_TAG }}
|
TAG=${{ env.FRONTEND_TAG }}
|
||||||
fi
|
fi
|
||||||
@ -104,14 +105,14 @@ jobs:
|
|||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs: [branch_build_setup]
|
needs: [branch_build_setup]
|
||||||
env:
|
env:
|
||||||
SPACE_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-space:${{ needs.branch_build_setup.outputs.gh_branch_name }}
|
SPACE_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-space${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ needs.branch_build_setup.outputs.gh_branch_name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Set Space Docker Tag
|
- name: Set Space Docker Tag
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then
|
if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then
|
||||||
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-space:${{ github.event.release.tag_name }}
|
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space${{ secrets.DOCKER_REPO_SUFFIX || '' }}:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-space${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ github.event.release.tag_name }}
|
||||||
elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then
|
elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then
|
||||||
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space:stable
|
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space${{ secrets.DOCKER_REPO_SUFFIX || '' }}:stable
|
||||||
else
|
else
|
||||||
TAG=${{ env.SPACE_TAG }}
|
TAG=${{ env.SPACE_TAG }}
|
||||||
fi
|
fi
|
||||||
@ -146,14 +147,14 @@ jobs:
|
|||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs: [branch_build_setup]
|
needs: [branch_build_setup]
|
||||||
env:
|
env:
|
||||||
BACKEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:${{ needs.branch_build_setup.outputs.gh_branch_name }}
|
BACKEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-backend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ needs.branch_build_setup.outputs.gh_branch_name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Set Backend Docker Tag
|
- name: Set Backend Docker Tag
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then
|
if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then
|
||||||
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:${{ github.event.release.tag_name }}
|
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-backend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ github.event.release.tag_name }}
|
||||||
elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then
|
elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then
|
||||||
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:stable
|
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:stable
|
||||||
else
|
else
|
||||||
TAG=${{ env.BACKEND_TAG }}
|
TAG=${{ env.BACKEND_TAG }}
|
||||||
fi
|
fi
|
||||||
@ -188,14 +189,14 @@ jobs:
|
|||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs: [branch_build_setup]
|
needs: [branch_build_setup]
|
||||||
env:
|
env:
|
||||||
PROXY_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:${{ needs.branch_build_setup.outputs.gh_branch_name }}
|
PROXY_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ needs.branch_build_setup.outputs.gh_branch_name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Set Proxy Docker Tag
|
- name: Set Proxy Docker Tag
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then
|
if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then
|
||||||
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:${{ github.event.release.tag_name }}
|
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy${{ secrets.DOCKER_REPO_SUFFIX || '' }}:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ github.event.release.tag_name }}
|
||||||
elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then
|
elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then
|
||||||
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:stable
|
TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy${{ secrets.DOCKER_REPO_SUFFIX || '' }}:stable
|
||||||
else
|
else
|
||||||
TAG=${{ env.PROXY_TAG }}
|
TAG=${{ env.PROXY_TAG }}
|
||||||
fi
|
fi
|
||||||
|
@ -14,12 +14,14 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository to Actions
|
- name: Checkout Repository to Actions
|
||||||
uses: actions/checkout@v3.3.0
|
uses: actions/checkout@v3.3.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Setup Node.js 18.x
|
- name: Setup Node.js 18.x
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 18.x
|
node-version: 18.x
|
||||||
cache: 'yarn'
|
cache: "yarn"
|
||||||
|
|
||||||
- name: Get changed files
|
- name: Get changed files
|
||||||
id: changed-files
|
id: changed-files
|
||||||
@ -44,5 +46,3 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
yarn
|
yarn
|
||||||
yarn build --filter=space
|
yarn build --filter=space
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ class IssueSerializer(BaseSerializer):
|
|||||||
if (
|
if (
|
||||||
data.get("state")
|
data.get("state")
|
||||||
and not State.objects.filter(
|
and not State.objects.filter(
|
||||||
project_id=self.context.get("project_id"), pk=data.get("state")
|
project_id=self.context.get("project_id"), pk=data.get("state").id
|
||||||
).exists()
|
).exists()
|
||||||
):
|
):
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
@ -107,7 +107,7 @@ class IssueSerializer(BaseSerializer):
|
|||||||
if (
|
if (
|
||||||
data.get("parent")
|
data.get("parent")
|
||||||
and not Issue.objects.filter(
|
and not Issue.objects.filter(
|
||||||
workspace_id=self.context.get("workspace_id"), pk=data.get("parent")
|
workspace_id=self.context.get("workspace_id"), pk=data.get("parent").id
|
||||||
).exists()
|
).exists()
|
||||||
):
|
):
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
|
@ -65,18 +65,18 @@ class ModuleSerializer(BaseSerializer):
|
|||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
members = validated_data.pop("members", None)
|
members = validated_data.pop("members", None)
|
||||||
|
|
||||||
project = self.context["project"]
|
project_id = self.context["project_id"]
|
||||||
|
workspace_id = self.context["workspace_id"]
|
||||||
module = Module.objects.create(**validated_data, project=project)
|
|
||||||
|
|
||||||
|
module = Module.objects.create(**validated_data, project_id=project_id)
|
||||||
if members is not None:
|
if members is not None:
|
||||||
ModuleMember.objects.bulk_create(
|
ModuleMember.objects.bulk_create(
|
||||||
[
|
[
|
||||||
ModuleMember(
|
ModuleMember(
|
||||||
module=module,
|
module=module,
|
||||||
member=member,
|
member_id=str(member),
|
||||||
project=project,
|
project_id=project_id,
|
||||||
workspace=project.workspace,
|
workspace_id=workspace_id,
|
||||||
created_by=module.created_by,
|
created_by=module.created_by,
|
||||||
updated_by=module.updated_by,
|
updated_by=module.updated_by,
|
||||||
)
|
)
|
||||||
@ -97,7 +97,7 @@ class ModuleSerializer(BaseSerializer):
|
|||||||
[
|
[
|
||||||
ModuleMember(
|
ModuleMember(
|
||||||
module=instance,
|
module=instance,
|
||||||
member=member,
|
member_id=str(member),
|
||||||
project=instance.project,
|
project=instance.project,
|
||||||
workspace=instance.project.workspace,
|
workspace=instance.project.workspace,
|
||||||
created_by=instance.created_by,
|
created_by=instance.created_by,
|
||||||
|
@ -221,11 +221,20 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
|||||||
|
|
||||||
def patch(self, request, slug, project_id, pk=None):
|
def patch(self, request, slug, project_id, pk=None):
|
||||||
issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk)
|
issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk)
|
||||||
|
project = Project.objects.get(pk=project_id)
|
||||||
current_instance = json.dumps(
|
current_instance = json.dumps(
|
||||||
IssueSerializer(issue).data, cls=DjangoJSONEncoder
|
IssueSerializer(issue).data, cls=DjangoJSONEncoder
|
||||||
)
|
)
|
||||||
requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder)
|
requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder)
|
||||||
serializer = IssueSerializer(issue, data=request.data, partial=True)
|
serializer = IssueSerializer(
|
||||||
|
issue,
|
||||||
|
data=request.data,
|
||||||
|
context={
|
||||||
|
"project_id": project_id,
|
||||||
|
"workspace_id": project.workspace_id,
|
||||||
|
},
|
||||||
|
partial=True,
|
||||||
|
)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
issue_activity.delay(
|
issue_activity.delay(
|
||||||
|
@ -121,8 +121,8 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def post(self, request, slug, project_id):
|
def post(self, request, slug, project_id):
|
||||||
project = Project.objects.get(workspace__slug=slug, pk=project_id)
|
project = Project.objects.get(pk=project_id, workspace__slug=slug)
|
||||||
serializer = ModuleSerializer(data=request.data, context={"project": project})
|
serializer = ModuleSerializer(data=request.data, context={"project_id": project_id, "workspace_id": project.workspace_id})
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
module = Module.objects.get(pk=serializer.data["id"])
|
module = Module.objects.get(pk=serializer.data["id"])
|
||||||
@ -132,7 +132,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView):
|
|||||||
|
|
||||||
def patch(self, request, slug, project_id, pk):
|
def patch(self, request, slug, project_id, pk):
|
||||||
module = Module.objects.get(pk=pk, project_id=project_id, workspace__slug=slug)
|
module = Module.objects.get(pk=pk, project_id=project_id, workspace__slug=slug)
|
||||||
serializer = ModuleSerializer(module, data=request.data)
|
serializer = ModuleSerializer(module, data=request.data, context={"project_id": project_id}, partial=True)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
@ -40,6 +40,7 @@ class CycleSerializer(BaseSerializer):
|
|||||||
started_estimates = serializers.IntegerField(read_only=True)
|
started_estimates = serializers.IntegerField(read_only=True)
|
||||||
workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace")
|
workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace")
|
||||||
project_detail = ProjectLiteSerializer(read_only=True, source="project")
|
project_detail = ProjectLiteSerializer(read_only=True, source="project")
|
||||||
|
status = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
if (
|
if (
|
||||||
|
@ -4,6 +4,7 @@ from .base import BaseSerializer
|
|||||||
from plane.db.models import Estimate, EstimatePoint
|
from plane.db.models import Estimate, EstimatePoint
|
||||||
from plane.app.serializers import WorkspaceLiteSerializer, ProjectLiteSerializer
|
from plane.app.serializers import WorkspaceLiteSerializer, ProjectLiteSerializer
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
class EstimateSerializer(BaseSerializer):
|
class EstimateSerializer(BaseSerializer):
|
||||||
workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace")
|
workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace")
|
||||||
@ -19,6 +20,15 @@ class EstimateSerializer(BaseSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class EstimatePointSerializer(BaseSerializer):
|
class EstimatePointSerializer(BaseSerializer):
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
if not data:
|
||||||
|
raise serializers.ValidationError("Estimate points are required")
|
||||||
|
value = data.get("value")
|
||||||
|
if value and len(value) > 20:
|
||||||
|
raise serializers.ValidationError("Value can't be more than 20 characters")
|
||||||
|
return data
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = EstimatePoint
|
model = EstimatePoint
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
@ -11,6 +11,10 @@ from django.db.models import (
|
|||||||
Count,
|
Count,
|
||||||
Prefetch,
|
Prefetch,
|
||||||
Sum,
|
Sum,
|
||||||
|
Case,
|
||||||
|
When,
|
||||||
|
Value,
|
||||||
|
CharField
|
||||||
)
|
)
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -157,6 +161,28 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.annotate(
|
||||||
|
status=Case(
|
||||||
|
When(
|
||||||
|
Q(start_date__lte=timezone.now()) & Q(end_date__gte=timezone.now()),
|
||||||
|
then=Value("CURRENT")
|
||||||
|
),
|
||||||
|
When(
|
||||||
|
start_date__gt=timezone.now(),
|
||||||
|
then=Value("UPCOMING")
|
||||||
|
),
|
||||||
|
When(
|
||||||
|
end_date__lt=timezone.now(),
|
||||||
|
then=Value("COMPLETED")
|
||||||
|
),
|
||||||
|
When(
|
||||||
|
Q(start_date__isnull=True) & Q(end_date__isnull=True),
|
||||||
|
then=Value("DRAFT")
|
||||||
|
),
|
||||||
|
default=Value("DRAFT"),
|
||||||
|
output_field=CharField(),
|
||||||
|
)
|
||||||
|
)
|
||||||
.prefetch_related(
|
.prefetch_related(
|
||||||
Prefetch(
|
Prefetch(
|
||||||
"issue_cycle__issue__assignees",
|
"issue_cycle__issue__assignees",
|
||||||
@ -177,7 +203,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
|
|||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
cycle_view = request.GET.get("cycle_view", "all")
|
cycle_view = request.GET.get("cycle_view", "all")
|
||||||
|
|
||||||
queryset = queryset.order_by("-is_favorite","-created_at")
|
queryset = queryset.order_by("-is_favorite", "-created_at")
|
||||||
|
|
||||||
# Current Cycle
|
# Current Cycle
|
||||||
if cycle_view == "current":
|
if cycle_view == "current":
|
||||||
@ -575,7 +601,9 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
issues = IssueStateSerializer(issues, many=True, fields=fields if fields else None).data
|
issues = IssueStateSerializer(
|
||||||
|
issues, many=True, fields=fields if fields else None
|
||||||
|
).data
|
||||||
issue_dict = {str(issue["id"]): issue for issue in issues}
|
issue_dict = {str(issue["id"]): issue for issue in issues}
|
||||||
return Response(issue_dict, status=status.HTTP_200_OK)
|
return Response(issue_dict, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
@ -54,10 +54,10 @@ class BulkEstimatePointEndpoint(BaseViewSet):
|
|||||||
|
|
||||||
estimate_points = request.data.get("estimate_points", [])
|
estimate_points = request.data.get("estimate_points", [])
|
||||||
|
|
||||||
if not len(estimate_points) or len(estimate_points) > 8:
|
serializer = EstimatePointSerializer(data=request.data.get("estimate_points"), many=True)
|
||||||
|
if not serializer.is_valid():
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Estimate points are required"},
|
serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
estimate_serializer = EstimateSerializer(data=request.data.get("estimate"))
|
estimate_serializer = EstimateSerializer(data=request.data.get("estimate"))
|
||||||
|
@ -50,7 +50,8 @@ class GlobalSearchEndpoint(BaseAPIView):
|
|||||||
q = Q()
|
q = Q()
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if field == "sequence_id":
|
if field == "sequence_id":
|
||||||
sequences = re.findall(r"\d+\.\d+|\d+", query)
|
# Match whole integers only (exclude decimal numbers)
|
||||||
|
sequences = re.findall(r"\b\d+\b", query)
|
||||||
for sequence_id in sequences:
|
for sequence_id in sequences:
|
||||||
q |= Q(**{"sequence_id": sequence_id})
|
q |= Q(**{"sequence_id": sequence_id})
|
||||||
else:
|
else:
|
||||||
|
@ -112,8 +112,8 @@ def track_parent(
|
|||||||
epoch,
|
epoch,
|
||||||
):
|
):
|
||||||
if current_instance.get("parent") != requested_data.get("parent"):
|
if current_instance.get("parent") != requested_data.get("parent"):
|
||||||
old_parent = Issue.objects.filter(pk=current_instance.get("parent")).first()
|
old_parent = Issue.objects.filter(pk=current_instance.get("parent")).first() if current_instance.get("parent") is not None else None
|
||||||
new_parent = Issue.objects.filter(pk=requested_data.get("parent")).first()
|
new_parent = Issue.objects.filter(pk=requested_data.get("parent")).first() if requested_data.get("parent") is not None else None
|
||||||
|
|
||||||
issue_activities.append(
|
issue_activities.append(
|
||||||
IssueActivity(
|
IssueActivity(
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2023-12-29 10:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('db', '0050_user_use_case_alter_workspace_organization_size'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='cycle',
|
||||||
|
name='external_id',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='cycle',
|
||||||
|
name='external_source',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='inboxissue',
|
||||||
|
name='external_id',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='inboxissue',
|
||||||
|
name='external_source',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='issue',
|
||||||
|
name='external_id',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='issue',
|
||||||
|
name='external_source',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='issuecomment',
|
||||||
|
name='external_id',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='issuecomment',
|
||||||
|
name='external_source',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='label',
|
||||||
|
name='external_id',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='label',
|
||||||
|
name='external_source',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='module',
|
||||||
|
name='external_id',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='module',
|
||||||
|
name='external_source',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='state',
|
||||||
|
name='external_id',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='state',
|
||||||
|
name='external_source',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -18,6 +18,8 @@ class Cycle(ProjectBaseModel):
|
|||||||
)
|
)
|
||||||
view_props = models.JSONField(default=dict)
|
view_props = models.JSONField(default=dict)
|
||||||
sort_order = models.FloatField(default=65535)
|
sort_order = models.FloatField(default=65535)
|
||||||
|
external_source = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
external_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Cycle"
|
verbose_name = "Cycle"
|
||||||
|
@ -39,6 +39,8 @@ class InboxIssue(ProjectBaseModel):
|
|||||||
"db.Issue", related_name="inbox_duplicate", on_delete=models.SET_NULL, null=True
|
"db.Issue", related_name="inbox_duplicate", on_delete=models.SET_NULL, null=True
|
||||||
)
|
)
|
||||||
source = models.TextField(blank=True, null=True)
|
source = models.TextField(blank=True, null=True)
|
||||||
|
external_source = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
external_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "InboxIssue"
|
verbose_name = "InboxIssue"
|
||||||
|
@ -102,6 +102,8 @@ class Issue(ProjectBaseModel):
|
|||||||
completed_at = models.DateTimeField(null=True)
|
completed_at = models.DateTimeField(null=True)
|
||||||
archived_at = models.DateField(null=True)
|
archived_at = models.DateField(null=True)
|
||||||
is_draft = models.BooleanField(default=False)
|
is_draft = models.BooleanField(default=False)
|
||||||
|
external_source = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
external_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
issue_objects = IssueManager()
|
issue_objects = IssueManager()
|
||||||
@ -366,6 +368,8 @@ class IssueComment(ProjectBaseModel):
|
|||||||
default="INTERNAL",
|
default="INTERNAL",
|
||||||
max_length=100,
|
max_length=100,
|
||||||
)
|
)
|
||||||
|
external_source = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
external_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.comment_stripped = (
|
self.comment_stripped = (
|
||||||
@ -416,6 +420,8 @@ class Label(ProjectBaseModel):
|
|||||||
description = models.TextField(blank=True)
|
description = models.TextField(blank=True)
|
||||||
color = models.CharField(max_length=255, blank=True)
|
color = models.CharField(max_length=255, blank=True)
|
||||||
sort_order = models.FloatField(default=65535)
|
sort_order = models.FloatField(default=65535)
|
||||||
|
external_source = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
external_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ["name", "project"]
|
unique_together = ["name", "project"]
|
||||||
|
@ -41,6 +41,8 @@ class Module(ProjectBaseModel):
|
|||||||
)
|
)
|
||||||
view_props = models.JSONField(default=dict)
|
view_props = models.JSONField(default=dict)
|
||||||
sort_order = models.FloatField(default=65535)
|
sort_order = models.FloatField(default=65535)
|
||||||
|
external_source = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
external_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ["name", "project"]
|
unique_together = ["name", "project"]
|
||||||
|
@ -24,6 +24,8 @@ class State(ProjectBaseModel):
|
|||||||
max_length=20,
|
max_length=20,
|
||||||
)
|
)
|
||||||
default = models.BooleanField(default=False)
|
default = models.BooleanField(default=False)
|
||||||
|
external_source = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
external_id = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return name of the state"""
|
"""Return name of the state"""
|
||||||
|
@ -291,7 +291,9 @@ CELERY_IMPORTS = (
|
|||||||
|
|
||||||
# Sentry Settings
|
# Sentry Settings
|
||||||
# Enable Sentry Settings
|
# Enable Sentry Settings
|
||||||
if bool(os.environ.get("SENTRY_DSN", False)) and os.environ.get("SENTRY_DSN").startswith("https://"):
|
if bool(os.environ.get("SENTRY_DSN", False)) and os.environ.get(
|
||||||
|
"SENTRY_DSN"
|
||||||
|
).startswith("https://"):
|
||||||
sentry_sdk.init(
|
sentry_sdk.init(
|
||||||
dsn=os.environ.get("SENTRY_DSN", ""),
|
dsn=os.environ.get("SENTRY_DSN", ""),
|
||||||
integrations=[
|
integrations=[
|
||||||
@ -334,3 +336,5 @@ INSTANCE_KEY = os.environ.get(
|
|||||||
|
|
||||||
# Skip environment variable configuration
|
# Skip environment variable configuration
|
||||||
SKIP_ENV_VAR = os.environ.get("SKIP_ENV_VAR", "1") == "1"
|
SKIP_ENV_VAR = os.environ.get("SKIP_ENV_VAR", "1") == "1"
|
||||||
|
|
||||||
|
DATA_UPLOAD_MAX_MEMORY_SIZE = int(os.environ.get("FILE_SIZE_LIMIT", 5242880))
|
||||||
|
@ -18,6 +18,9 @@ http {
|
|||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://web:3000/;
|
proxy_pass http://web:3000/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
}
|
}
|
||||||
|
|
||||||
location /api/ {
|
location /api/ {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"prettier": "latest",
|
"prettier": "latest",
|
||||||
"prettier-plugin-tailwindcss": "^0.5.4",
|
"prettier-plugin-tailwindcss": "^0.5.4",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
"turbo": "^1.11.1"
|
"turbo": "^1.11.2"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "18.2.42"
|
"@types/react": "18.2.42"
|
||||||
|
@ -28,28 +28,22 @@
|
|||||||
"react-dom": "18.2.0"
|
"react-dom": "18.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@plane/editor-types": "*",
|
"@tiptap/core": "^2.1.13",
|
||||||
"@tiptap/core": "^2.1.7",
|
|
||||||
"@tiptap/extension-blockquote": "^2.1.13",
|
"@tiptap/extension-blockquote": "^2.1.13",
|
||||||
"@tiptap/extension-code-block-lowlight": "^2.1.12",
|
"@tiptap/extension-code-block-lowlight": "^2.1.13",
|
||||||
"@tiptap/extension-color": "^2.1.11",
|
"@tiptap/extension-color": "^2.1.13",
|
||||||
"@tiptap/extension-image": "^2.1.7",
|
"@tiptap/extension-image": "^2.1.13",
|
||||||
"@tiptap/extension-link": "^2.1.7",
|
"@tiptap/extension-link": "^2.1.13",
|
||||||
"@tiptap/extension-list-item": "^2.1.12",
|
"@tiptap/extension-list-item": "^2.1.13",
|
||||||
"@tiptap/extension-mention": "^2.1.12",
|
"@tiptap/extension-mention": "^2.1.13",
|
||||||
"@tiptap/extension-table": "^2.1.6",
|
"@tiptap/extension-task-item": "^2.1.13",
|
||||||
"@tiptap/extension-table-cell": "^2.1.6",
|
"@tiptap/extension-task-list": "^2.1.13",
|
||||||
"@tiptap/extension-table-header": "^2.1.6",
|
"@tiptap/extension-text-style": "^2.1.13",
|
||||||
"@tiptap/extension-table-row": "^2.1.6",
|
"@tiptap/extension-underline": "^2.1.13",
|
||||||
"@tiptap/extension-task-item": "^2.1.7",
|
"@tiptap/pm": "^2.1.13",
|
||||||
"@tiptap/extension-task-list": "^2.1.7",
|
"@tiptap/react": "^2.1.13",
|
||||||
"@tiptap/extension-text-style": "^2.1.11",
|
"@tiptap/starter-kit": "^2.1.13",
|
||||||
"@tiptap/extension-underline": "^2.1.7",
|
"@tiptap/suggestion": "^2.0.13",
|
||||||
"@tiptap/pm": "^2.1.7",
|
|
||||||
"@tiptap/prosemirror-tables": "^1.1.4",
|
|
||||||
"@tiptap/react": "^2.1.7",
|
|
||||||
"@tiptap/starter-kit": "^2.1.10",
|
|
||||||
"@tiptap/suggestion": "^2.0.4",
|
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"highlight.js": "^11.8.0",
|
"highlight.js": "^11.8.0",
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { useEditor as useCustomEditor, Editor } from "@tiptap/react";
|
import { useEditor as useCustomEditor, Editor } from "@tiptap/react";
|
||||||
import { useImperativeHandle, useRef, MutableRefObject } from "react";
|
import { useImperativeHandle, useRef, MutableRefObject } from "react";
|
||||||
import { CoreEditorProps } from "../props";
|
import { CoreEditorProps } from "src/ui/props";
|
||||||
import { CoreEditorExtensions } from "../extensions";
|
import { CoreEditorExtensions } from "src/ui/extensions";
|
||||||
import { EditorProps } from "@tiptap/pm/view";
|
import { EditorProps } from "@tiptap/pm/view";
|
||||||
import { getTrimmedHTML } from "../../lib/utils";
|
import { getTrimmedHTML } from "src/lib/utils";
|
||||||
import { DeleteImage, IMentionSuggestion, RestoreImage, UploadImage } from "@plane/editor-types";
|
import { DeleteImage } from "src/types/delete-image";
|
||||||
|
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
||||||
|
import { RestoreImage } from "src/types/restore-image";
|
||||||
|
import { UploadImage } from "src/types/upload-image";
|
||||||
|
|
||||||
interface CustomEditorProps {
|
interface CustomEditorProps {
|
||||||
uploadFile: UploadImage;
|
uploadFile: UploadImage;
|
@ -1,9 +1,9 @@
|
|||||||
import { useEditor as useCustomEditor, Editor } from "@tiptap/react";
|
import { useEditor as useCustomEditor, Editor } from "@tiptap/react";
|
||||||
import { useImperativeHandle, useRef, MutableRefObject } from "react";
|
import { useImperativeHandle, useRef, MutableRefObject } from "react";
|
||||||
import { CoreReadOnlyEditorExtensions } from "../read-only/extensions";
|
import { CoreReadOnlyEditorExtensions } from "src/ui/read-only/extensions";
|
||||||
import { CoreReadOnlyEditorProps } from "../read-only/props";
|
import { CoreReadOnlyEditorProps } from "src/ui/read-only/props";
|
||||||
import { EditorProps } from "@tiptap/pm/view";
|
import { EditorProps } from "@tiptap/pm/view";
|
||||||
import { IMentionSuggestion } from "@plane/editor-types";
|
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
||||||
|
|
||||||
interface CustomReadOnlyEditorProps {
|
interface CustomReadOnlyEditorProps {
|
||||||
value: string;
|
value: string;
|
@ -1,23 +1,32 @@
|
|||||||
// styles
|
// styles
|
||||||
// import "./styles/tailwind.css";
|
// import "./styles/tailwind.css";
|
||||||
// import "./styles/editor.css";
|
import "src/styles/editor.css";
|
||||||
import "./styles/github-dark.css";
|
import "src/styles/table.css";
|
||||||
|
import "src/styles/github-dark.css";
|
||||||
|
|
||||||
export { isCellSelection } from "./ui/extensions/table/table/utilities/is-cell-selection";
|
export { isCellSelection } from "src/ui/extensions/table/table/utilities/is-cell-selection";
|
||||||
|
|
||||||
// utils
|
// utils
|
||||||
export * from "./lib/utils";
|
export * from "src/lib/utils";
|
||||||
export * from "./ui/extensions/table/table";
|
export * from "src/ui/extensions/table/table";
|
||||||
export { startImageUpload } from "./ui/plugins/upload-image";
|
export { startImageUpload } from "src/ui/plugins/upload-image";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
export { EditorContainer } from "./ui/components/editor-container";
|
export { EditorContainer } from "src/ui/components/editor-container";
|
||||||
export { EditorContentWrapper } from "./ui/components/editor-content";
|
export { EditorContentWrapper } from "src/ui/components/editor-content";
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
export { useEditor } from "./ui/hooks/use-editor";
|
export { useEditor } from "src/hooks/use-editor";
|
||||||
export { useReadOnlyEditor } from "./ui/hooks/use-read-only-editor";
|
export { useReadOnlyEditor } from "src/hooks/use-read-only-editor";
|
||||||
|
|
||||||
// helper items
|
// helper items
|
||||||
export * from "./ui/menus/menu-items";
|
export * from "src/ui/menus/menu-items";
|
||||||
export * from "./lib/editor-commands";
|
export * from "src/lib/editor-commands";
|
||||||
|
|
||||||
|
// types
|
||||||
|
export type { DeleteImage } from "src/types/delete-image";
|
||||||
|
export type { UploadImage } from "src/types/upload-image";
|
||||||
|
export type { RestoreImage } from "src/types/restore-image";
|
||||||
|
export type { IMentionHighlight, IMentionSuggestion } from "src/types/mention-suggestion";
|
||||||
|
export type { ISlashCommandItem, CommandProps } from "src/types/slash-commands-suggestion";
|
||||||
|
export type { LucideIconType } from "src/types/lucide-icon";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { UploadImage } from "@plane/editor-types";
|
|
||||||
import { Editor, Range } from "@tiptap/core";
|
import { Editor, Range } from "@tiptap/core";
|
||||||
import { startImageUpload } from "../ui/plugins/upload-image";
|
import { startImageUpload } from "src/ui/plugins/upload-image";
|
||||||
import { findTableAncestor } from "./utils";
|
import { findTableAncestor } from "src/lib/utils";
|
||||||
|
import { UploadImage } from "src/types/upload-image";
|
||||||
|
|
||||||
export const toggleHeadingOne = (editor: Editor, range?: Range) => {
|
export const toggleHeadingOne = (editor: Editor, range?: Range) => {
|
||||||
if (range) editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run();
|
if (range) editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run();
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* block quotes */
|
||||||
|
.ProseMirror blockquote p::before,
|
||||||
|
.ProseMirror blockquote p::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.ProseMirror .is-empty::before {
|
.ProseMirror .is-empty::before {
|
||||||
content: attr(data-placeholder);
|
content: attr(data-placeholder);
|
||||||
float: left;
|
float: left;
|
||||||
@ -15,9 +21,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Custom image styles */
|
/* Custom image styles */
|
||||||
|
|
||||||
.ProseMirror img {
|
.ProseMirror img {
|
||||||
transition: filter 0.1s ease-in-out;
|
transition: filter 0.1s ease-in-out;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -53,11 +60,12 @@ ul[data-type="taskList"] li > label input[type="checkbox"] {
|
|||||||
background-color: rgb(var(--color-background-100));
|
background-color: rgb(var(--color-background-100));
|
||||||
margin: 0;
|
margin: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 1.2rem;
|
width: 0.8rem;
|
||||||
height: 1.2rem;
|
height: 0.8rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 2px solid rgb(var(--color-text-100));
|
border: 1.5px solid rgb(var(--color-text-100));
|
||||||
margin-right: 0.3rem;
|
margin-right: 0.2rem;
|
||||||
|
margin-top: 0.15rem;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-content: center;
|
place-content: center;
|
||||||
|
|
||||||
@ -71,8 +79,8 @@ ul[data-type="taskList"] li > label input[type="checkbox"] {
|
|||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
width: 0.65em;
|
width: 0.5em;
|
||||||
height: 0.65em;
|
height: 0.5em;
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
transition: 120ms transform ease-in-out;
|
transition: 120ms transform ease-in-out;
|
||||||
box-shadow: inset 1em 1em;
|
box-shadow: inset 1em 1em;
|
||||||
@ -133,6 +141,8 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
|||||||
.img-placeholder {
|
.img-placeholder {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 35%;
|
width: 35%;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
@ -159,7 +169,8 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
|||||||
table {
|
table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
margin: 0;
|
margin: 0.5em 0 0.5em 0;
|
||||||
|
|
||||||
border: 1px solid rgb(var(--color-border-200));
|
border: 1px solid rgb(var(--color-border-200));
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@ -229,3 +240,34 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
|||||||
.ProseMirror table * .is-empty::before {
|
.ProseMirror table * .is-empty::before {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ProseMirror pre {
|
||||||
|
background: rgba(var(--color-background-80));
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
color: rgba(var(--color-text-100));
|
||||||
|
font-family: "JetBrainsMono", monospace;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ProseMirror pre code {
|
||||||
|
background: none;
|
||||||
|
color: inherit;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[data-type="horizontalRule"] {
|
||||||
|
line-height: 0;
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
border-bottom: 1px solid rgb(var(--color-text-100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* image resizer */
|
||||||
|
.moveable-control-box {
|
||||||
|
z-index: 10 !important;
|
||||||
|
}
|
||||||
|
3
packages/editor/core/src/types/lucide-icon.ts
Normal file
3
packages/editor/core/src/types/lucide-icon.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Smile } from "lucide-react";
|
||||||
|
|
||||||
|
export type LucideIconType = typeof Smile;
|
@ -1,6 +1,6 @@
|
|||||||
import { Editor, EditorContent } from "@tiptap/react";
|
import { Editor, EditorContent } from "@tiptap/react";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { ImageResizer } from "../extensions/image/image-resize";
|
import { ImageResizer } from "src/ui/extensions/image/image-resize";
|
||||||
|
|
||||||
interface EditorContentProps {
|
interface EditorContentProps {
|
||||||
editor: Editor | null;
|
editor: Editor | null;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { getNodeAtPosition } from "@tiptap/core";
|
import { getNodeAtPosition } from "@tiptap/core";
|
||||||
import { EditorState } from "@tiptap/pm/state";
|
import { EditorState } from "@tiptap/pm/state";
|
||||||
|
|
||||||
import { findListItemPos } from "./find-list-item-pos";
|
import { findListItemPos } from "src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos";
|
||||||
|
|
||||||
export const getNextListDepth = (typeOrName: string, state: EditorState) => {
|
export const getNextListDepth = (typeOrName: string, state: EditorState) => {
|
||||||
const listItemPos = findListItemPos(typeOrName, state);
|
const listItemPos = findListItemPos(typeOrName, state);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Editor, isAtStartOfNode, isNodeActive } from "@tiptap/core";
|
import { Editor, isAtStartOfNode, isNodeActive } from "@tiptap/core";
|
||||||
import { Node } from "@tiptap/pm/model";
|
import { Node } from "@tiptap/pm/model";
|
||||||
|
|
||||||
import { findListItemPos } from "./find-list-item-pos";
|
import { findListItemPos } from "src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos";
|
||||||
import { hasListBefore } from "./has-list-before";
|
import { hasListBefore } from "src/ui/extensions/custom-list-keymap/list-helpers/has-list-before";
|
||||||
|
|
||||||
export const handleBackspace = (editor: Editor, name: string, parentListTypes: string[]) => {
|
export const handleBackspace = (editor: Editor, name: string, parentListTypes: string[]) => {
|
||||||
// this is required to still handle the undo handling
|
// this is required to still handle the undo handling
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Editor, isAtEndOfNode, isNodeActive } from "@tiptap/core";
|
import { Editor, isAtEndOfNode, isNodeActive } from "@tiptap/core";
|
||||||
|
|
||||||
import { nextListIsDeeper } from "./next-list-is-deeper";
|
import { nextListIsDeeper } from "src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper";
|
||||||
import { nextListIsHigher } from "./next-list-is-higher";
|
import { nextListIsHigher } from "src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher";
|
||||||
|
|
||||||
export const handleDelete = (editor: Editor, name: string) => {
|
export const handleDelete = (editor: Editor, name: string) => {
|
||||||
// if the cursor is not inside the current node type
|
// if the cursor is not inside the current node type
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { EditorState } from "@tiptap/pm/state";
|
import { EditorState } from "@tiptap/pm/state";
|
||||||
|
|
||||||
import { findListItemPos } from "./find-list-item-pos";
|
import { findListItemPos } from "src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos";
|
||||||
import { getNextListDepth } from "./get-next-list-depth";
|
import { getNextListDepth } from "src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth";
|
||||||
|
|
||||||
export const nextListIsDeeper = (typeOrName: string, state: EditorState) => {
|
export const nextListIsDeeper = (typeOrName: string, state: EditorState) => {
|
||||||
const listDepth = getNextListDepth(typeOrName, state);
|
const listDepth = getNextListDepth(typeOrName, state);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { EditorState } from "@tiptap/pm/state";
|
import { EditorState } from "@tiptap/pm/state";
|
||||||
|
|
||||||
import { findListItemPos } from "./find-list-item-pos";
|
import { findListItemPos } from "src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos";
|
||||||
import { getNextListDepth } from "./get-next-list-depth";
|
import { getNextListDepth } from "src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth";
|
||||||
|
|
||||||
export const nextListIsHigher = (typeOrName: string, state: EditorState) => {
|
export const nextListIsHigher = (typeOrName: string, state: EditorState) => {
|
||||||
const listDepth = getNextListDepth(typeOrName, state);
|
const listDepth = getNextListDepth(typeOrName, state);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Extension } from "@tiptap/core";
|
import { Extension } from "@tiptap/core";
|
||||||
|
|
||||||
import { handleBackspace, handleDelete } from "./list-helpers";
|
import { handleBackspace, handleDelete } from "src/ui/extensions/custom-list-keymap/list-helpers";
|
||||||
|
|
||||||
export type ListKeymapOptions = {
|
export type ListKeymapOptions = {
|
||||||
listTypes: Array<{
|
listTypes: Array<{
|
||||||
|
@ -22,7 +22,7 @@ declare module "@tiptap/core" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Node.create<HorizontalRuleOptions>({
|
export const HorizontalRule = Node.create<HorizontalRuleOptions>({
|
||||||
name: "horizontalRule",
|
name: "horizontalRule",
|
||||||
|
|
||||||
addOptions() {
|
addOptions() {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state";
|
import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state";
|
||||||
import { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
import { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
||||||
import UploadImagesPlugin from "../../plugins/upload-image";
|
import { UploadImagesPlugin } from "src/ui/plugins/upload-image";
|
||||||
import ImageExt from "@tiptap/extension-image";
|
import ImageExt from "@tiptap/extension-image";
|
||||||
import { onNodeDeleted, onNodeRestored } from "../../plugins/delete-image";
|
import { onNodeDeleted, onNodeRestored } from "src/ui/plugins/delete-image";
|
||||||
import { DeleteImage, RestoreImage } from "@plane/editor-types";
|
import { DeleteImage } from "src/types/delete-image";
|
||||||
|
import { RestoreImage } from "src/types/restore-image";
|
||||||
|
|
||||||
interface ImageNode extends ProseMirrorNode {
|
interface ImageNode extends ProseMirrorNode {
|
||||||
attrs: {
|
attrs: {
|
||||||
@ -15,7 +16,7 @@ interface ImageNode extends ProseMirrorNode {
|
|||||||
const deleteKey = new PluginKey("delete-image");
|
const deleteKey = new PluginKey("delete-image");
|
||||||
const IMAGE_NODE_TYPE = "image";
|
const IMAGE_NODE_TYPE = "image";
|
||||||
|
|
||||||
const ImageExtension = (deleteImage: DeleteImage, restoreFile: RestoreImage, cancelUploadImage?: () => any) =>
|
export const ImageExtension = (deleteImage: DeleteImage, restoreFile: RestoreImage, cancelUploadImage?: () => any) =>
|
||||||
ImageExt.extend({
|
ImageExt.extend({
|
||||||
addProseMirrorPlugins() {
|
addProseMirrorPlugins() {
|
||||||
return [
|
return [
|
||||||
@ -130,5 +131,3 @@ const ImageExtension = (deleteImage: DeleteImage, restoreFile: RestoreImage, can
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default ImageExtension;
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Image from "@tiptap/extension-image";
|
import Image from "@tiptap/extension-image";
|
||||||
|
|
||||||
const ReadOnlyImageExtension = Image.extend({
|
export const ReadOnlyImageExtension = Image.extend({
|
||||||
addAttributes() {
|
addAttributes() {
|
||||||
return {
|
return {
|
||||||
...this.parent?.(),
|
...this.parent?.(),
|
||||||
@ -13,5 +13,3 @@ const ReadOnlyImageExtension = Image.extend({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default ReadOnlyImageExtension;
|
|
||||||
|
@ -7,22 +7,25 @@ import TaskItem from "@tiptap/extension-task-item";
|
|||||||
import TaskList from "@tiptap/extension-task-list";
|
import TaskList from "@tiptap/extension-task-list";
|
||||||
import { Markdown } from "tiptap-markdown";
|
import { Markdown } from "tiptap-markdown";
|
||||||
|
|
||||||
import TableHeader from "./table/table-header/table-header";
|
import { TableHeader } from "src/ui/extensions/table/table-header/table-header";
|
||||||
import Table from "./table/table";
|
import { Table } from "src/ui/extensions/table/table";
|
||||||
import TableCell from "./table/table-cell/table-cell";
|
import { TableCell } from "src/ui/extensions/table/table-cell/table-cell";
|
||||||
import TableRow from "./table/table-row/table-row";
|
import { TableRow } from "src/ui/extensions/table/table-row/table-row";
|
||||||
import HorizontalRule from "./horizontal-rule";
|
import { HorizontalRule } from "src/ui/extensions/horizontal-rule";
|
||||||
|
|
||||||
import ImageExtension from "./image";
|
import { ImageExtension } from "src/ui/extensions/image";
|
||||||
|
|
||||||
import { isValidHttpUrl } from "../../lib/utils";
|
import { isValidHttpUrl } from "src/lib/utils";
|
||||||
import { Mentions } from "../mentions";
|
import { Mentions } from "src/ui/mentions";
|
||||||
|
|
||||||
import { CustomKeymap } from "./keymap";
|
import { CustomKeymap } from "src/ui/extensions/keymap";
|
||||||
import { CustomCodeBlock } from "./code";
|
import { CustomCodeBlock } from "src/ui/extensions/code";
|
||||||
import { CustomQuoteExtension } from "./quote";
|
import { CustomQuoteExtension } from "src/ui/extensions/quote";
|
||||||
import { ListKeymap } from "./custom-list-keymap";
|
import { ListKeymap } from "src/ui/extensions/custom-list-keymap";
|
||||||
import { IMentionSuggestion, DeleteImage, RestoreImage } from "@plane/editor-types";
|
|
||||||
|
import { DeleteImage } from "src/types/delete-image";
|
||||||
|
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
||||||
|
import { RestoreImage } from "src/types/restore-image";
|
||||||
|
|
||||||
export const CoreEditorExtensions = (
|
export const CoreEditorExtensions = (
|
||||||
mentionConfig: {
|
mentionConfig: {
|
||||||
@ -49,12 +52,12 @@ export const CoreEditorExtensions = (
|
|||||||
class: "leading-normal -mb-2",
|
class: "leading-normal -mb-2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// blockquote: {
|
code: {
|
||||||
// HTMLAttributes: {
|
HTMLAttributes: {
|
||||||
// class: "border-l-4 border-custom-border-300",
|
class: "rounded-md bg-custom-primary-30 mx-1 px-1 py-1 font-mono font-medium text-custom-text-1000",
|
||||||
// },
|
spellcheck: "false",
|
||||||
// },
|
},
|
||||||
code: false,
|
},
|
||||||
codeBlock: false,
|
codeBlock: false,
|
||||||
horizontalRule: false,
|
horizontalRule: false,
|
||||||
dropcursor: {
|
dropcursor: {
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default as default } from "./table-cell";
|
export { TableCell } from "./table-cell";
|
||||||
|
@ -4,7 +4,7 @@ export interface TableCellOptions {
|
|||||||
HTMLAttributes: Record<string, any>;
|
HTMLAttributes: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Node.create<TableCellOptions>({
|
export const TableCell = Node.create<TableCellOptions>({
|
||||||
name: "tableCell",
|
name: "tableCell",
|
||||||
|
|
||||||
addOptions() {
|
addOptions() {
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default as default } from "./table-header";
|
export { TableHeader } from "./table-header";
|
||||||
|
@ -3,7 +3,8 @@ import { mergeAttributes, Node } from "@tiptap/core";
|
|||||||
export interface TableHeaderOptions {
|
export interface TableHeaderOptions {
|
||||||
HTMLAttributes: Record<string, any>;
|
HTMLAttributes: Record<string, any>;
|
||||||
}
|
}
|
||||||
export default Node.create<TableHeaderOptions>({
|
|
||||||
|
export const TableHeader = Node.create<TableHeaderOptions>({
|
||||||
name: "tableHeader",
|
name: "tableHeader",
|
||||||
|
|
||||||
addOptions() {
|
addOptions() {
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default as default } from "./table-row";
|
export { TableRow } from "./table-row";
|
||||||
|
@ -4,7 +4,7 @@ export interface TableRowOptions {
|
|||||||
HTMLAttributes: Record<string, any>;
|
HTMLAttributes: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Node.create<TableRowOptions>({
|
export const TableRow = Node.create<TableRowOptions>({
|
||||||
name: "tableRow",
|
name: "tableRow",
|
||||||
|
|
||||||
addOptions() {
|
addOptions() {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const icons = {
|
export const icons = {
|
||||||
colorPicker: `<svg xmlns="http://www.w3.org/2000/svg" length="24" viewBox="0 0 24 24" style="transform: ;msFilter:;"><path fill="rgb(var(--color-text-300))" d="M20 14c-.092.064-2 2.083-2 3.5 0 1.494.949 2.448 2 2.5.906.044 2-.891 2-2.5 0-1.5-1.908-3.436-2-3.5zM9.586 20c.378.378.88.586 1.414.586s1.036-.208 1.414-.586l7-7-.707-.707L11 4.586 8.707 2.293 7.293 3.707 9.586 6 4 11.586c-.378.378-.586.88-.586 1.414s.208 1.036.586 1.414L9.586 20zM11 7.414 16.586 13H5.414L11 7.414z"></path></svg>`,
|
colorPicker: `<svg xmlns="http://www.w3.org/2000/svg" length="24" viewBox="0 0 24 24" style="transform: ;msFilter:;"><path fill="rgb(var(--color-text-300))" d="M20 14c-.092.064-2 2.083-2 3.5 0 1.494.949 2.448 2 2.5.906.044 2-.891 2-2.5 0-1.5-1.908-3.436-2-3.5zM9.586 20c.378.378.88.586 1.414.586s1.036-.208 1.414-.586l7-7-.707-.707L11 4.586 8.707 2.293 7.293 3.707 9.586 6 4 11.586c-.378.378-.586.88-.586 1.414s.208 1.036.586 1.414L9.586 20zM11 7.414 16.586 13H5.414L11 7.414z"></path></svg>`,
|
||||||
deleteColumn: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" length="24"><path fill="#e53e3e" d="M0 0H24V24H0z"/><path d="M12 3c.552 0 1 .448 1 1v8c.835-.628 1.874-1 3-1 2.761 0 5 2.239 5 5s-2.239 5-5 5c-1.032 0-1.99-.313-2.787-.848L13 20c0 .552-.448 1-1 1H6c-.552 0-1-.448-1-1V4c0-.552.448-1 1-1h6zm-1 2H7v14h4V5zm8 10h-6v2h6v-2z"/></svg>`,
|
deleteColumn: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" length="24"><path fill="#e53e3e" d="M0 0H24V24H0z"/><path d="M12 3c.552 0 1 .448 1 1v8c.835-.628 1.874-1 3-1 2.761 0 5 2.239 5 5s-2.239 5-5 5c-1.032 0-1.99-.313-2.787-.848L13 20c0 .552-.448 1-1 1H6c-.552 0-1-.448-1-1V4c0-.552.448-1 1-1h6zm-1 2H7v14h4V5zm8 10h-6v2h6v-2z"/></svg>`,
|
||||||
deleteRow: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" length="24"><path fill="#e53e3e" d="M0 0H24V24H0z"/><path d="M20 5c.552 0 1 .448 1 1v6c0 .552-.448 1-1 1 .628.835 1 1.874 1 3 0 2.761-2.239 5-5 5s-5-2.239-5-5c0-1.126.372-2.165 1-3H4c-.552 0-1-.448-1-1V6c0-.552.448-1 1-1h16zm-7 10v2h6v-2h-6zm6-8H5v4h14V7z"/></svg>`,
|
deleteRow: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" length="24"><path fill="#e53e3e" d="M0 0H24V24H0z"/><path d="M20 5c.552 0 1 .448 1 1v6c0 .552-.448 1-1 1 .628.835 1 1.874 1 3 0 2.761-2.239 5-5 5s-5-2.239-5-5c0-1.126.372-2.165 1-3H4c-.552 0-1-.448-1-1V6c0-.552.448-1 1-1h16zm-7 10v2h6v-2h-6zm6-8H5v4h14V7z"/></svg>`,
|
||||||
@ -47,5 +47,3 @@ const icons = {
|
|||||||
</svg>
|
</svg>
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default icons;
|
|
||||||
|
@ -1 +1 @@
|
|||||||
export { default as default } from "./table";
|
export { Table } from "./table";
|
||||||
|
@ -4,9 +4,9 @@ import { Decoration, NodeView } from "@tiptap/pm/view";
|
|||||||
import tippy, { Instance, Props } from "tippy.js";
|
import tippy, { Instance, Props } from "tippy.js";
|
||||||
|
|
||||||
import { Editor } from "@tiptap/core";
|
import { Editor } from "@tiptap/core";
|
||||||
import { CellSelection, TableMap, updateColumnsOnResize } from "@tiptap/prosemirror-tables";
|
import { CellSelection, TableMap, updateColumnsOnResize } from "@tiptap/pm/tables";
|
||||||
|
|
||||||
import icons from "./icons";
|
import { icons } from "src/ui/extensions/table/table/icons";
|
||||||
|
|
||||||
export function updateColumns(
|
export function updateColumns(
|
||||||
node: ProseMirrorNode,
|
node: ProseMirrorNode,
|
||||||
|
@ -19,12 +19,12 @@ import {
|
|||||||
tableEditing,
|
tableEditing,
|
||||||
toggleHeader,
|
toggleHeader,
|
||||||
toggleHeaderCell,
|
toggleHeaderCell,
|
||||||
} from "@tiptap/prosemirror-tables";
|
} from "@tiptap/pm/tables";
|
||||||
|
|
||||||
import { tableControls } from "./table-controls";
|
import { tableControls } from "src/ui/extensions/table/table/table-controls";
|
||||||
import { TableView } from "./table-view";
|
import { TableView } from "src/ui/extensions/table/table/table-view";
|
||||||
import { createTable } from "./utilities/create-table";
|
import { createTable } from "src/ui/extensions/table/table/utilities/create-table";
|
||||||
import { deleteTableWhenAllCellsSelected } from "./utilities/delete-table-when-all-cells-selected";
|
import { deleteTableWhenAllCellsSelected } from "src/ui/extensions/table/table/utilities/delete-table-when-all-cells-selected";
|
||||||
|
|
||||||
export interface TableOptions {
|
export interface TableOptions {
|
||||||
HTMLAttributes: Record<string, any>;
|
HTMLAttributes: Record<string, any>;
|
||||||
@ -72,7 +72,7 @@ declare module "@tiptap/core" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Node.create({
|
export const Table = Node.create({
|
||||||
name: "table",
|
name: "table",
|
||||||
|
|
||||||
addOptions() {
|
addOptions() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Fragment, Node as ProsemirrorNode, Schema } from "@tiptap/pm/model";
|
import { Fragment, Node as ProsemirrorNode, Schema } from "@tiptap/pm/model";
|
||||||
|
|
||||||
import { createCell } from "./create-cell";
|
import { createCell } from "src/ui/extensions/table/table/utilities/create-cell";
|
||||||
import { getTableNodeTypes } from "./get-table-node-types";
|
import { getTableNodeTypes } from "src/ui/extensions/table/table/utilities/get-table-node-types";
|
||||||
|
|
||||||
export function createTable(
|
export function createTable(
|
||||||
schema: Schema,
|
schema: Schema,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { findParentNodeClosestToPos, KeyboardShortcutCommand } from "@tiptap/core";
|
import { findParentNodeClosestToPos, KeyboardShortcutCommand } from "@tiptap/core";
|
||||||
|
|
||||||
import { isCellSelection } from "./is-cell-selection";
|
import { isCellSelection } from "src/ui/extensions/table/table/utilities/is-cell-selection";
|
||||||
|
|
||||||
export const deleteTableWhenAllCellsSelected: KeyboardShortcutCommand = ({ editor }) => {
|
export const deleteTableWhenAllCellsSelected: KeyboardShortcutCommand = ({ editor }) => {
|
||||||
const { selection } = editor.state;
|
const { selection } = editor.state;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { CellSelection } from "@tiptap/prosemirror-tables";
|
import { CellSelection } from "@tiptap/pm/tables";
|
||||||
|
|
||||||
export function isCellSelection(value: unknown): value is CellSelection {
|
export function isCellSelection(value: unknown): value is CellSelection {
|
||||||
return value instanceof CellSelection;
|
return value instanceof CellSelection;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Mention, MentionOptions } from "@tiptap/extension-mention";
|
import { Mention, MentionOptions } from "@tiptap/extension-mention";
|
||||||
import { mergeAttributes } from "@tiptap/core";
|
import { mergeAttributes } from "@tiptap/core";
|
||||||
import { ReactNodeViewRenderer } from "@tiptap/react";
|
import { ReactNodeViewRenderer } from "@tiptap/react";
|
||||||
import mentionNodeView from "./mentionNodeView";
|
import { MentionNodeView } from "src/ui/mentions/mention-node-view";
|
||||||
import { IMentionHighlight } from "@plane/editor-types";
|
import { IMentionHighlight } from "src/types/mention-suggestion";
|
||||||
|
|
||||||
export interface CustomMentionOptions extends MentionOptions {
|
export interface CustomMentionOptions extends MentionOptions {
|
||||||
mentionHighlights: IMentionHighlight[];
|
mentionHighlights: IMentionHighlight[];
|
||||||
@ -31,7 +31,7 @@ export const CustomMention = Mention.extend<CustomMentionOptions>({
|
|||||||
},
|
},
|
||||||
|
|
||||||
addNodeView() {
|
addNodeView() {
|
||||||
return ReactNodeViewRenderer(mentionNodeView);
|
return ReactNodeViewRenderer(MentionNodeView);
|
||||||
},
|
},
|
||||||
|
|
||||||
parseHTML() {
|
parseHTML() {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import suggestion from "./suggestion";
|
import { Suggestion } from "src/ui/mentions/suggestion";
|
||||||
import { CustomMention } from "./custom";
|
import { CustomMention } from "src/ui/mentions/custom";
|
||||||
import { IMentionHighlight, IMentionSuggestion } from "@plane/editor-types";
|
import { IMentionHighlight } from "src/types/mention-suggestion";
|
||||||
|
|
||||||
export const Mentions = (mentionSuggestions: IMentionSuggestion[], mentionHighlights: IMentionHighlight[], readonly) =>
|
export const Mentions = (mentionSuggestions: IMentionSuggestion[], mentionHighlights: IMentionHighlight[], readonly) =>
|
||||||
CustomMention.configure({
|
CustomMention.configure({
|
||||||
@ -11,5 +11,5 @@ export const Mentions = (mentionSuggestions: IMentionSuggestion[], mentionHighli
|
|||||||
},
|
},
|
||||||
readonly: readonly,
|
readonly: readonly,
|
||||||
mentionHighlights: mentionHighlights,
|
mentionHighlights: mentionHighlights,
|
||||||
suggestion: suggestion(mentionSuggestions),
|
suggestion: Suggestion(mentionSuggestions),
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { IMentionSuggestion } from "@plane/editor-types";
|
|
||||||
import { Editor } from "@tiptap/react";
|
import { Editor } from "@tiptap/react";
|
||||||
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
|
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
||||||
|
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
||||||
|
|
||||||
interface MentionListProps {
|
interface MentionListProps {
|
||||||
items: IMentionSuggestion[];
|
items: IMentionSuggestion[];
|
||||||
@ -9,7 +9,7 @@ interface MentionListProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
const MentionList = forwardRef((props: MentionListProps, ref) => {
|
export const MentionList = forwardRef((props: MentionListProps, ref) => {
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||||
|
|
||||||
const selectItem = (index: number) => {
|
const selectItem = (index: number) => {
|
||||||
@ -98,5 +98,3 @@ const MentionList = forwardRef((props: MentionListProps, ref) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
MentionList.displayName = "MentionList";
|
MentionList.displayName = "MentionList";
|
||||||
|
|
||||||
export default MentionList;
|
|
@ -1,12 +1,12 @@
|
|||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { NodeViewWrapper } from "@tiptap/react";
|
import { NodeViewWrapper } from "@tiptap/react";
|
||||||
import { cn } from "../../lib/utils";
|
import { cn } from "src/lib/utils";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { IMentionHighlight } from "@plane/editor-types";
|
import { IMentionHighlight } from "src/types/mention-suggestion";
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-anonymous-default-export
|
// eslint-disable-next-line import/no-anonymous-default-export
|
||||||
export default (props) => {
|
export const MentionNodeView = (props) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const highlights = props.extension.options.mentionHighlights as IMentionHighlight[];
|
const highlights = props.extension.options.mentionHighlights as IMentionHighlight[];
|
||||||
|
|
@ -2,10 +2,10 @@ import { ReactRenderer } from "@tiptap/react";
|
|||||||
import { Editor } from "@tiptap/core";
|
import { Editor } from "@tiptap/core";
|
||||||
import tippy from "tippy.js";
|
import tippy from "tippy.js";
|
||||||
|
|
||||||
import MentionList from "./MentionList";
|
import { MentionList } from "src/ui/mentions/mention-list";
|
||||||
import { IMentionSuggestion } from "@plane/editor-types";
|
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
||||||
|
|
||||||
const Suggestion = (suggestions: IMentionSuggestion[]) => ({
|
export const Suggestion = (suggestions: IMentionSuggestion[]) => ({
|
||||||
items: ({ query }: { query: string }) =>
|
items: ({ query }: { query: string }) =>
|
||||||
suggestions.filter((suggestion) => suggestion.title.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5),
|
suggestions.filter((suggestion) => suggestion.title.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5),
|
||||||
render: () => {
|
render: () => {
|
||||||
@ -55,5 +55,3 @@ const Suggestion = (suggestions: IMentionSuggestion[]) => ({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Suggestion;
|
|
||||||
|
@ -30,14 +30,15 @@ import {
|
|||||||
toggleStrike,
|
toggleStrike,
|
||||||
toggleTaskList,
|
toggleTaskList,
|
||||||
toggleUnderline,
|
toggleUnderline,
|
||||||
} from "../../../lib/editor-commands";
|
} from "src/lib/editor-commands";
|
||||||
import { UploadImage } from "@plane/editor-types";
|
import { LucideIconType } from "src/types/lucide-icon";
|
||||||
|
import { UploadImage } from "src/types/upload-image";
|
||||||
|
|
||||||
export interface EditorMenuItem {
|
export interface EditorMenuItem {
|
||||||
name: string;
|
name: string;
|
||||||
isActive: () => boolean;
|
isActive: () => boolean;
|
||||||
command: () => void;
|
command: () => void;
|
||||||
icon: typeof BoldIcon;
|
icon: LucideIconType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HeadingOneItem = (editor: Editor): EditorMenuItem => ({
|
export const HeadingOneItem = (editor: Editor): EditorMenuItem => ({
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state";
|
import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state";
|
||||||
import { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
import { Node as ProseMirrorNode } from "@tiptap/pm/model";
|
||||||
import { DeleteImage, RestoreImage } from "@plane/editor-types";
|
import { DeleteImage } from "src/types/delete-image";
|
||||||
|
import { RestoreImage } from "src/types/restore-image";
|
||||||
|
|
||||||
const deleteKey = new PluginKey("delete-image");
|
const deleteKey = new PluginKey("delete-image");
|
||||||
const IMAGE_NODE_TYPE = "image";
|
const IMAGE_NODE_TYPE = "image";
|
||||||
@ -12,7 +13,7 @@ interface ImageNode extends ProseMirrorNode {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin =>
|
export const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin =>
|
||||||
new Plugin({
|
new Plugin({
|
||||||
key: deleteKey,
|
key: deleteKey,
|
||||||
appendTransaction: (transactions: readonly Transaction[], oldState: EditorState, newState: EditorState) => {
|
appendTransaction: (transactions: readonly Transaction[], oldState: EditorState, newState: EditorState) => {
|
||||||
@ -53,8 +54,6 @@ const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin =>
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default TrackImageDeletionPlugin;
|
|
||||||
|
|
||||||
export async function onNodeDeleted(src: string, deleteImage: DeleteImage): Promise<void> {
|
export async function onNodeDeleted(src: string, deleteImage: DeleteImage): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const assetUrlWithWorkspaceId = new URL(src).pathname.substring(1);
|
const assetUrlWithWorkspaceId = new URL(src).pathname.substring(1);
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { UploadImage } from "@plane/editor-types";
|
|
||||||
import { EditorState, Plugin, PluginKey } from "@tiptap/pm/state";
|
import { EditorState, Plugin, PluginKey } from "@tiptap/pm/state";
|
||||||
import { Decoration, DecorationSet, EditorView } from "@tiptap/pm/view";
|
import { Decoration, DecorationSet, EditorView } from "@tiptap/pm/view";
|
||||||
|
import { UploadImage } from "src/types/upload-image";
|
||||||
|
|
||||||
const uploadKey = new PluginKey("upload-image");
|
const uploadKey = new PluginKey("upload-image");
|
||||||
|
|
||||||
const UploadImagesPlugin = (cancelUploadImage?: () => any) =>
|
export const UploadImagesPlugin = (cancelUploadImage?: () => any) =>
|
||||||
new Plugin({
|
new Plugin({
|
||||||
key: uploadKey,
|
key: uploadKey,
|
||||||
state: {
|
state: {
|
||||||
@ -43,7 +43,7 @@ const UploadImagesPlugin = (cancelUploadImage?: () => any) =>
|
|||||||
|
|
||||||
cancelButton.appendChild(svgElement);
|
cancelButton.appendChild(svgElement);
|
||||||
placeholder.appendChild(cancelButton);
|
placeholder.appendChild(cancelButton);
|
||||||
const deco = Decoration.widget(pos + 1, placeholder, {
|
const deco = Decoration.widget(pos, placeholder, {
|
||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
set = set.add(tr.doc, [deco]);
|
set = set.add(tr.doc, [deco]);
|
||||||
@ -60,8 +60,6 @@ const UploadImagesPlugin = (cancelUploadImage?: () => any) =>
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default UploadImagesPlugin;
|
|
||||||
|
|
||||||
function findPlaceholder(state: EditorState, id: {}) {
|
function findPlaceholder(state: EditorState, id: {}) {
|
||||||
const decos = uploadKey.getState(state);
|
const decos = uploadKey.getState(state);
|
||||||
const found = decos.find(undefined, undefined, (spec: { id: number | undefined }) => spec.id == id);
|
const found = decos.find(undefined, undefined, (spec: { id: number | undefined }) => spec.id == id);
|
||||||
@ -133,7 +131,8 @@ export async function startImageUpload(
|
|||||||
const imageSrc = typeof src === "object" ? reader.result : src;
|
const imageSrc = typeof src === "object" ? reader.result : src;
|
||||||
|
|
||||||
const node = schema.nodes.image.create({ src: imageSrc });
|
const node = schema.nodes.image.create({ src: imageSrc });
|
||||||
const transaction = view.state.tr.replaceWith(pos, pos, node).setMeta(uploadKey, { remove: { id } });
|
const transaction = view.state.tr.insert(pos - 1, node).setMeta(uploadKey, { remove: { id } });
|
||||||
|
|
||||||
view.dispatch(transaction);
|
view.dispatch(transaction);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Upload error: ", error);
|
console.error("Upload error: ", error);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { UploadImage } from "@plane/editor-types";
|
|
||||||
import { EditorProps } from "@tiptap/pm/view";
|
import { EditorProps } from "@tiptap/pm/view";
|
||||||
import { findTableAncestor } from "../lib/utils";
|
import { findTableAncestor } from "src/lib/utils";
|
||||||
import { startImageUpload } from "./plugins/upload-image";
|
import { UploadImage } from "src/types/upload-image";
|
||||||
|
import { startImageUpload } from "src/ui/plugins/upload-image";
|
||||||
|
|
||||||
export function CoreEditorProps(
|
export function CoreEditorProps(
|
||||||
uploadFile: UploadImage,
|
uploadFile: UploadImage,
|
||||||
|
@ -8,15 +8,16 @@ import TaskList from "@tiptap/extension-task-list";
|
|||||||
import { Markdown } from "tiptap-markdown";
|
import { Markdown } from "tiptap-markdown";
|
||||||
import Gapcursor from "@tiptap/extension-gapcursor";
|
import Gapcursor from "@tiptap/extension-gapcursor";
|
||||||
|
|
||||||
import TableHeader from "../extensions/table/table-header/table-header";
|
import { TableHeader } from "src/ui/extensions/table/table-header/table-header";
|
||||||
import Table from "../extensions/table/table";
|
import { Table } from "src/ui/extensions/table/table";
|
||||||
import TableCell from "../extensions/table/table-cell/table-cell";
|
import { TableCell } from "src/ui/extensions/table/table-cell/table-cell";
|
||||||
import TableRow from "../extensions/table/table-row/table-row";
|
import { TableRow } from "src/ui/extensions/table/table-row/table-row";
|
||||||
|
import { HorizontalRule } from "src/ui/extensions/horizontal-rule";
|
||||||
|
|
||||||
import ReadOnlyImageExtension from "../extensions/image/read-only-image";
|
import { ReadOnlyImageExtension } from "src/ui/extensions/image/read-only-image";
|
||||||
import { isValidHttpUrl } from "../../lib/utils";
|
import { isValidHttpUrl } from "src/lib/utils";
|
||||||
import { Mentions } from "../mentions";
|
import { Mentions } from "src/ui/mentions";
|
||||||
import { IMentionSuggestion } from "@plane/editor-types";
|
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
||||||
|
|
||||||
export const CoreReadOnlyEditorExtensions = (mentionConfig: {
|
export const CoreReadOnlyEditorExtensions = (mentionConfig: {
|
||||||
mentionSuggestions: IMentionSuggestion[];
|
mentionSuggestions: IMentionSuggestion[];
|
||||||
@ -71,6 +72,7 @@ export const CoreReadOnlyEditorExtensions = (mentionConfig: {
|
|||||||
class: "rounded-lg border border-custom-border-300",
|
class: "rounded-lg border border-custom-border-300",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
HorizontalRule,
|
||||||
TiptapUnderline,
|
TiptapUnderline,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
Color,
|
Color,
|
||||||
|
@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"extends": "tsconfig/react-library.json",
|
"extends": "tsconfig/react-library.json",
|
||||||
"include": ["src/**/*", "index.d.ts"],
|
"include": [
|
||||||
"exclude": ["dist", "build", "node_modules"]
|
"src/**/*",
|
||||||
|
"index.d.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"build",
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,13 +30,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@plane/editor-core": "*",
|
"@plane/editor-core": "*",
|
||||||
"@plane/editor-extensions": "*",
|
"@plane/editor-extensions": "*",
|
||||||
"@plane/editor-types": "*",
|
|
||||||
"@plane/ui": "*",
|
"@plane/ui": "*",
|
||||||
"@tiptap/core": "^2.1.7",
|
"@tiptap/core": "^2.1.13",
|
||||||
"@tiptap/extension-placeholder": "^2.1.11",
|
"@tiptap/extension-placeholder": "^2.1.13",
|
||||||
"@tiptap/pm": "^2.1.12",
|
"@tiptap/pm": "^2.1.13",
|
||||||
"@tiptap/suggestion": "^2.1.12",
|
"@tiptap/suggestion": "^2.1.13",
|
||||||
"eslint": "8.36.0",
|
|
||||||
"eslint-config-next": "13.2.4",
|
"eslint-config-next": "13.2.4",
|
||||||
"react-popper": "^2.3.0",
|
"react-popper": "^2.3.0",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
@ -46,7 +44,7 @@
|
|||||||
"@types/node": "18.15.3",
|
"@types/node": "18.15.3",
|
||||||
"@types/react": "^18.2.42",
|
"@types/react": "^18.2.42",
|
||||||
"@types/react-dom": "^18.2.17",
|
"@types/react-dom": "^18.2.17",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "8.36.0",
|
||||||
"postcss": "^8.4.29",
|
"postcss": "^8.4.29",
|
||||||
"tailwind-config-custom": "*",
|
"tailwind-config-custom": "*",
|
||||||
"tsconfig": "*",
|
"tsconfig": "*",
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Editor } from "@tiptap/react";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { IMarking } from "..";
|
import { IMarking } from "src/types/editor-types";
|
||||||
|
|
||||||
export const useEditorMarkings = () => {
|
export const useEditorMarkings = () => {
|
||||||
const [markings, setMarkings] = useState<IMarking[]>([]);
|
const [markings, setMarkings] = useState<IMarking[]>([]);
|
@ -1,3 +1,3 @@
|
|||||||
export { DocumentEditor, DocumentEditorWithRef } from "./ui";
|
export { DocumentEditor, DocumentEditorWithRef } from "src/ui";
|
||||||
export { DocumentReadOnlyEditor, DocumentReadOnlyEditorWithRef } from "./ui/readonly";
|
export { DocumentReadOnlyEditor, DocumentReadOnlyEditorWithRef } from "src/ui/readonly";
|
||||||
export { FixedMenu } from "./ui/menu/fixed-menu";
|
export { FixedMenu } from "src/ui/menu/fixed-menu";
|
||||||
|
@ -5,3 +5,9 @@ export interface DocumentDetails {
|
|||||||
last_updated_by: string;
|
last_updated_by: string;
|
||||||
last_updated_at: Date;
|
last_updated_at: Date;
|
||||||
}
|
}
|
||||||
|
export interface IMarking {
|
||||||
|
type: "heading";
|
||||||
|
level: number;
|
||||||
|
text: string;
|
||||||
|
sequence: number;
|
||||||
|
}
|
0
packages/editor/document-editor/src/types/mark.ts
Normal file
0
packages/editor/document-editor/src/types/mark.ts
Normal file
@ -1,12 +1,11 @@
|
|||||||
import { Icon } from "lucide-react";
|
import { LucideIconType } from "@plane/editor-core";
|
||||||
|
|
||||||
interface IAlertLabelProps {
|
interface IAlertLabelProps {
|
||||||
Icon?: Icon;
|
Icon?: LucideIconType;
|
||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
textColor?: string;
|
textColor?: string;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AlertLabel = (props: IAlertLabelProps) => {
|
export const AlertLabel = (props: IAlertLabelProps) => {
|
||||||
const { Icon, backgroundColor, textColor, label } = props;
|
const { Icon, backgroundColor, textColor, label } = props;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { HeadingComp, HeadingThreeComp, SubheadingComp } from "./heading-component";
|
import { HeadingComp, HeadingThreeComp, SubheadingComp } from "src/ui/components/heading-component";
|
||||||
import { IMarking } from "..";
|
import { IMarking } from "src/types/editor-types";
|
||||||
import { Editor } from "@tiptap/react";
|
import { Editor } from "@tiptap/react";
|
||||||
import { scrollSummary } from "../utils/editor-summary-utils";
|
import { scrollSummary } from "src/utils/editor-summary-utils";
|
||||||
|
|
||||||
interface ContentBrowserProps {
|
interface ContentBrowserProps {
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import { Editor } from "@tiptap/react";
|
import { Editor } from "@tiptap/react";
|
||||||
import { Archive, RefreshCw, Lock } from "lucide-react";
|
import { Archive, RefreshCw, Lock } from "lucide-react";
|
||||||
import { IMarking } from "..";
|
import { IMarking, DocumentDetails } from "src/types/editor-types";
|
||||||
import { FixedMenu } from "../menu";
|
import { FixedMenu } from "src/ui/menu";
|
||||||
import { UploadImage } from "@plane/editor-types";
|
import { UploadImage } from "@plane/editor-core";
|
||||||
import { DocumentDetails } from "../types/editor-types";
|
import { AlertLabel } from "src/ui/components/alert-label";
|
||||||
import { AlertLabel } from "./alert-label";
|
import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "src/ui/components/vertical-dropdown-menu";
|
||||||
import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "./vertical-dropdown-menu";
|
import { SummaryPopover } from "src/ui/components/summary-popover";
|
||||||
import { SummaryPopover } from "./summary-popover";
|
import { InfoPopover } from "src/ui/components/info-popover";
|
||||||
import { InfoPopover } from "./info-popover";
|
|
||||||
|
|
||||||
interface IEditorHeader {
|
interface IEditorHeader {
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
|
@ -2,7 +2,7 @@ import { useState } from "react";
|
|||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
import { Calendar, History, Info } from "lucide-react";
|
import { Calendar, History, Info } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { DocumentDetails } from "../types/editor-types";
|
import { DocumentDetails } from "src/types/editor-types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
documentDetails: DocumentDetails;
|
documentDetails: DocumentDetails;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { EditorContainer, EditorContentWrapper } from "@plane/editor-core";
|
import { EditorContainer, EditorContentWrapper } from "@plane/editor-core";
|
||||||
import { Editor } from "@tiptap/react";
|
import { Editor } from "@tiptap/react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { DocumentDetails } from "../types/editor-types";
|
import { DocumentDetails } from "src/types/editor-types";
|
||||||
|
|
||||||
type IPageRenderer = {
|
type IPageRenderer = {
|
||||||
documentDetails: DocumentDetails;
|
documentDetails: DocumentDetails;
|
||||||
|
@ -3,9 +3,9 @@ import { Editor } from "@tiptap/react";
|
|||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
import { List } from "lucide-react";
|
import { List } from "lucide-react";
|
||||||
// components
|
// components
|
||||||
import { ContentBrowser } from "./content-browser";
|
import { ContentBrowser } from "src/ui/components/content-browser";
|
||||||
// types
|
// types
|
||||||
import { IMarking } from "..";
|
import { IMarking } from "src/types/editor-types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Editor } from "@tiptap/react";
|
import { Editor } from "@tiptap/react";
|
||||||
import { IMarking } from "..";
|
import { IMarking } from "src/types/editor-types";
|
||||||
import { ContentBrowser } from "./content-browser";
|
import { ContentBrowser } from "src/ui/components/content-browser";
|
||||||
|
|
||||||
interface ISummarySideBarProps {
|
interface ISummarySideBarProps {
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
@ -8,8 +8,7 @@ interface ISummarySideBarProps {
|
|||||||
sidePeekVisible: boolean;
|
sidePeekVisible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SummarySideBar = ({ editor, markings, sidePeekVisible }: ISummarySideBarProps) => {
|
export const SummarySideBar = ({ editor, markings, sidePeekVisible }: ISummarySideBarProps) => (
|
||||||
return (
|
|
||||||
<div
|
<div
|
||||||
className={`h-full transform overflow-hidden p-5 transition-all duration-200 ${
|
className={`h-full transform overflow-hidden p-5 transition-all duration-200 ${
|
||||||
sidePeekVisible ? "translate-x-0" : "-translate-x-full"
|
sidePeekVisible ? "translate-x-0" : "-translate-x-full"
|
||||||
@ -17,5 +16,4 @@ export const SummarySideBar = ({ editor, markings, sidePeekVisible }: ISummarySi
|
|||||||
>
|
>
|
||||||
<ContentBrowser editor={editor} markings={markings} />
|
<ContentBrowser editor={editor} markings={markings} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Button, CustomMenu } from "@plane/ui";
|
import { LucideIconType } from "@plane/editor-core";
|
||||||
import { ChevronUp, Icon, MoreVertical } from "lucide-react";
|
import { CustomMenu } from "@plane/ui";
|
||||||
|
import { MoreVertical } from "lucide-react";
|
||||||
|
|
||||||
type TMenuItems =
|
type TMenuItems =
|
||||||
| "archive_page"
|
| "archive_page"
|
||||||
@ -14,7 +15,7 @@ type TMenuItems =
|
|||||||
export interface IVerticalDropdownItemProps {
|
export interface IVerticalDropdownItemProps {
|
||||||
key: number;
|
key: number;
|
||||||
type: TMenuItems;
|
type: TMenuItems;
|
||||||
Icon: Icon;
|
Icon: LucideIconType;
|
||||||
label: string;
|
label: string;
|
||||||
action: () => Promise<void> | void;
|
action: () => Promise<void> | void;
|
||||||
}
|
}
|
||||||
@ -23,17 +24,14 @@ export interface IVerticalDropdownMenuProps {
|
|||||||
items: IVerticalDropdownItemProps[];
|
items: IVerticalDropdownItemProps[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const VerticalDropdownItem = ({ Icon, label, action }: IVerticalDropdownItemProps) => {
|
const VerticalDropdownItem = ({ Icon, label, action }: IVerticalDropdownItemProps) => (
|
||||||
return (
|
|
||||||
<CustomMenu.MenuItem onClick={action} className="flex items-center gap-2">
|
<CustomMenu.MenuItem onClick={action} className="flex items-center gap-2">
|
||||||
<Icon className="h-3 w-3" />
|
<Icon className="h-3 w-3" />
|
||||||
<div className="text-custom-text-300">{label}</div>
|
<div className="text-custom-text-300">{label}</div>
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
export const VerticalDropdownMenu = ({ items }: IVerticalDropdownMenuProps) => {
|
export const VerticalDropdownMenu = ({ items }: IVerticalDropdownMenuProps) => (
|
||||||
return (
|
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
maxHeight={"md"}
|
maxHeight={"md"}
|
||||||
className={"h-4.5 mt-1"}
|
className={"h-4.5 mt-1"}
|
||||||
@ -41,9 +39,8 @@ export const VerticalDropdownMenu = ({ items }: IVerticalDropdownMenuProps) => {
|
|||||||
optionsClassName={"border-custom-border border-r border-solid transition-all duration-200 ease-in-out "}
|
optionsClassName={"border-custom-border border-r border-solid transition-all duration-200 ease-in-out "}
|
||||||
customButton={<MoreVertical size={14} />}
|
customButton={<MoreVertical size={14} />}
|
||||||
>
|
>
|
||||||
{items.map((item, index) => (
|
{items.map((item) => (
|
||||||
<VerticalDropdownItem key={index} type={item.type} Icon={item.Icon} label={item.label} action={item.action} />
|
<VerticalDropdownItem key={item.key} type={item.type} Icon={item.Icon} label={item.label} action={item.action} />
|
||||||
))}
|
))}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import Placeholder from "@tiptap/extension-placeholder";
|
import Placeholder from "@tiptap/extension-placeholder";
|
||||||
import { IssueWidgetExtension } from "./widgets/IssueEmbedWidget";
|
import { IssueWidgetExtension } from "src/ui/extensions/widgets/issue-embed-widget";
|
||||||
|
|
||||||
import { IIssueEmbedConfig } from "./widgets/IssueEmbedWidget/types";
|
import { IIssueEmbedConfig } from "src/ui/extensions/widgets/issue-embed-widget/types";
|
||||||
|
|
||||||
import { SlashCommand, DragAndDrop } from "@plane/editor-extensions";
|
import { SlashCommand, DragAndDrop } from "@plane/editor-extensions";
|
||||||
import { ISlashCommandItem, UploadImage } from "@plane/editor-types";
|
import { ISlashCommandItem, UploadImage } from "@plane/editor-core";
|
||||||
import { IssueSuggestions } from "./widgets/IssueEmbedSuggestionList";
|
import { IssueSuggestions } from "src/ui/extensions/widgets/issue-embed-suggestion-list";
|
||||||
import { LayersIcon } from "@plane/ui";
|
import { LayersIcon } from "@plane/ui";
|
||||||
|
|
||||||
export const DocumentEditorExtensions = (
|
export const DocumentEditorExtensions = (
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import { IIssueListSuggestion } from ".";
|
|
||||||
|
|
||||||
export const getIssueSuggestionItems = (issueSuggestions: Array<IIssueListSuggestion>) => {
|
|
||||||
return ({ query }: { query: string }) => {
|
|
||||||
const search = query.toLowerCase();
|
|
||||||
const filteredSuggestions = issueSuggestions.filter((item) => {
|
|
||||||
return (
|
|
||||||
item.title.toLowerCase().includes(search) ||
|
|
||||||
item.identifier.toLowerCase().includes(search) ||
|
|
||||||
item.priority.toLowerCase().includes(search)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return filteredSuggestions;
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,7 +1,7 @@
|
|||||||
import { Editor, Range } from "@tiptap/react";
|
import { Editor, Range } from "@tiptap/react";
|
||||||
import { IssueEmbedSuggestions } from "./issue-suggestion-extension";
|
import { IssueEmbedSuggestions } from "src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-extension";
|
||||||
import { getIssueSuggestionItems } from "./issue-suggestion-items";
|
import { getIssueSuggestionItems } from "src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-items";
|
||||||
import { IssueListRenderer } from "./issue-suggestion-renderer";
|
import { IssueListRenderer } from "src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-renderer";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
export type CommandProps = {
|
export type CommandProps = {
|
||||||
@ -19,7 +19,7 @@ export interface IIssueListSuggestion {
|
|||||||
|
|
||||||
export const IssueSuggestions = (suggestions: any[]) => {
|
export const IssueSuggestions = (suggestions: any[]) => {
|
||||||
const mappedSuggestions: IIssueListSuggestion[] = suggestions.map((suggestion): IIssueListSuggestion => {
|
const mappedSuggestions: IIssueListSuggestion[] = suggestions.map((suggestion): IIssueListSuggestion => {
|
||||||
let transactionId = uuidv4();
|
const transactionId = uuidv4();
|
||||||
return {
|
return {
|
||||||
title: suggestion.name,
|
title: suggestion.name,
|
||||||
priority: suggestion.priority.toString(),
|
priority: suggestion.priority.toString(),
|
@ -0,0 +1,15 @@
|
|||||||
|
import { IIssueListSuggestion } from "src/ui/extensions/widgets/issue-embed-suggestion-list";
|
||||||
|
|
||||||
|
export const getIssueSuggestionItems =
|
||||||
|
(issueSuggestions: Array<IIssueListSuggestion>) =>
|
||||||
|
({ query }: { query: string }) => {
|
||||||
|
const search = query.toLowerCase();
|
||||||
|
const filteredSuggestions = issueSuggestions.filter(
|
||||||
|
(item) =>
|
||||||
|
item.title.toLowerCase().includes(search) ||
|
||||||
|
item.identifier.toLowerCase().includes(search) ||
|
||||||
|
item.priority.toLowerCase().includes(search)
|
||||||
|
);
|
||||||
|
|
||||||
|
return filteredSuggestions;
|
||||||
|
};
|
@ -171,7 +171,7 @@ const IssueSuggestionList = ({
|
|||||||
section === currentSection && index === selectedIndex,
|
section === currentSection && index === selectedIndex,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
key={index}
|
key={item.identifier}
|
||||||
onClick={() => selectItem(index)}
|
onClick={() => selectItem(index)}
|
||||||
>
|
>
|
||||||
<h5 className="whitespace-nowrap text-xs text-custom-text-300">{item.identifier}</h5>
|
<h5 className="whitespace-nowrap text-xs text-custom-text-300">{item.identifier}</h5>
|
@ -1,5 +1,5 @@
|
|||||||
import { IssueWidget } from "./issue-widget-node";
|
import { IssueWidget } from "src/ui/extensions/widgets/issue-embed-widget/issue-widget-node";
|
||||||
import { IIssueEmbedConfig } from "./types";
|
import { IIssueEmbedConfig } from "src/ui/extensions/widgets/issue-embed-widget/types";
|
||||||
|
|
||||||
interface IssueWidgetExtensionProps {
|
interface IssueWidgetExtensionProps {
|
||||||
issueEmbedConfig?: IIssueEmbedConfig;
|
issueEmbedConfig?: IIssueEmbedConfig;
|
@ -4,7 +4,7 @@ import { NodeViewWrapper } from "@tiptap/react";
|
|||||||
import { Avatar, AvatarGroup, Loader, PriorityIcon } from "@plane/ui";
|
import { Avatar, AvatarGroup, Loader, PriorityIcon } from "@plane/ui";
|
||||||
import { Calendar, AlertTriangle } from "lucide-react";
|
import { Calendar, AlertTriangle } from "lucide-react";
|
||||||
|
|
||||||
const IssueWidgetCard = (props) => {
|
export const IssueWidgetCard = (props) => {
|
||||||
const [loading, setLoading] = useState<number>(1);
|
const [loading, setLoading] = useState<number>(1);
|
||||||
const [issueDetails, setIssueDetails] = useState();
|
const [issueDetails, setIssueDetails] = useState();
|
||||||
|
|
||||||
@ -42,11 +42,9 @@ const IssueWidgetCard = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<AvatarGroup size="sm">
|
<AvatarGroup size="sm">
|
||||||
{issueDetails.assignee_details.map((assignee) => {
|
{issueDetails.assignee_details.map((assignee) => (
|
||||||
return (
|
|
||||||
<Avatar key={assignee.id} name={assignee.display_name} src={assignee.avatar} className={"m-0"} />
|
<Avatar key={assignee.id} name={assignee.display_name} src={assignee.avatar} className={"m-0"} />
|
||||||
);
|
))}
|
||||||
})}
|
|
||||||
</AvatarGroup>
|
</AvatarGroup>
|
||||||
</div>
|
</div>
|
||||||
{issueDetails.target_date && (
|
{issueDetails.target_date && (
|
||||||
@ -76,5 +74,3 @@ const IssueWidgetCard = (props) => {
|
|||||||
</NodeViewWrapper>
|
</NodeViewWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default IssueWidgetCard;
|
|
@ -1,5 +1,5 @@
|
|||||||
import { mergeAttributes, Node } from "@tiptap/core";
|
import { mergeAttributes, Node } from "@tiptap/core";
|
||||||
import IssueWidgetCard from "./issue-widget-card";
|
import { IssueWidgetCard } from "src/ui/extensions/widgets/issue-embed-widget/issue-widget-card";
|
||||||
import { ReactNodeViewRenderer } from "@tiptap/react";
|
import { ReactNodeViewRenderer } from "@tiptap/react";
|
||||||
|
|
||||||
export const IssueWidget = Node.create({
|
export const IssueWidget = Node.create({
|
@ -1,17 +1,16 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { getEditorClassNames, useEditor } from "@plane/editor-core";
|
import { UploadImage, DeleteImage, RestoreImage, getEditorClassNames, useEditor } from "@plane/editor-core";
|
||||||
import { DocumentEditorExtensions } from "./extensions";
|
import { DocumentEditorExtensions } from "src/ui/extensions";
|
||||||
import { IDuplicationConfig, IPageArchiveConfig, IPageLockConfig } from "./types/menu-actions";
|
import { IDuplicationConfig, IPageArchiveConfig, IPageLockConfig } from "src/types/menu-actions";
|
||||||
import { EditorHeader } from "./components/editor-header";
|
import { EditorHeader } from "src/ui/components/editor-header";
|
||||||
import { useEditorMarkings } from "./hooks/use-editor-markings";
|
import { useEditorMarkings } from "src/hooks/use-editor-markings";
|
||||||
import { SummarySideBar } from "./components/summary-side-bar";
|
import { SummarySideBar } from "src/ui/components/summary-side-bar";
|
||||||
import { DocumentDetails } from "./types/editor-types";
|
import { DocumentDetails } from "src/types/editor-types";
|
||||||
import { PageRenderer } from "./components/page-renderer";
|
import { PageRenderer } from "src/ui/components/page-renderer";
|
||||||
import { getMenuOptions } from "./utils/menu-options";
|
import { getMenuOptions } from "src/utils/menu-options";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { IEmbedConfig } from "./extensions/widgets/IssueEmbedWidget/types";
|
import { IEmbedConfig } from "src/ui/extensions/widgets/issue-embed-widget/types";
|
||||||
import { UploadImage, DeleteImage, RestoreImage } from "@plane/editor-types";
|
|
||||||
|
|
||||||
interface IDocumentEditor {
|
interface IDocumentEditor {
|
||||||
// document info
|
// document info
|
||||||
@ -59,13 +58,6 @@ interface EditorHandle {
|
|||||||
setEditorValue: (content: string) => void;
|
setEditorValue: (content: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMarking {
|
|
||||||
type: "heading";
|
|
||||||
level: number;
|
|
||||||
text: string;
|
|
||||||
sequence: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DocumentEditor = ({
|
const DocumentEditor = ({
|
||||||
documentDetails,
|
documentDetails,
|
||||||
onChange,
|
onChange,
|
||||||
|
@ -17,8 +17,8 @@ import {
|
|||||||
HeadingThreeItem,
|
HeadingThreeItem,
|
||||||
findTableAncestor,
|
findTableAncestor,
|
||||||
EditorMenuItem,
|
EditorMenuItem,
|
||||||
|
UploadImage,
|
||||||
} from "@plane/editor-core";
|
} from "@plane/editor-core";
|
||||||
import { UploadImage } from "@plane/editor-types";
|
|
||||||
|
|
||||||
export type BubbleMenuItem = EditorMenuItem;
|
export type BubbleMenuItem = EditorMenuItem;
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { getEditorClassNames, useReadOnlyEditor } from "@plane/editor-core";
|
import { getEditorClassNames, useReadOnlyEditor } from "@plane/editor-core";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useState, forwardRef, useEffect } from "react";
|
import { useState, forwardRef, useEffect } from "react";
|
||||||
import { EditorHeader } from "../components/editor-header";
|
import { EditorHeader } from "src/ui/components/editor-header";
|
||||||
import { PageRenderer } from "../components/page-renderer";
|
import { PageRenderer } from "src/ui/components/page-renderer";
|
||||||
import { SummarySideBar } from "../components/summary-side-bar";
|
import { SummarySideBar } from "src/ui/components/summary-side-bar";
|
||||||
import { IssueWidgetExtension } from "../extensions/widgets/IssueEmbedWidget";
|
import { IssueWidgetExtension } from "src/ui/extensions/widgets/issue-embed-widget";
|
||||||
import { IEmbedConfig } from "../extensions/widgets/IssueEmbedWidget/types";
|
import { IEmbedConfig } from "src/ui/extensions/widgets/issue-embed-widget/types";
|
||||||
import { useEditorMarkings } from "../hooks/use-editor-markings";
|
import { useEditorMarkings } from "src/hooks/use-editor-markings";
|
||||||
import { DocumentDetails } from "../types/editor-types";
|
import { DocumentDetails } from "src/types/editor-types";
|
||||||
import { IPageArchiveConfig, IPageLockConfig, IDuplicationConfig } from "../types/menu-actions";
|
import { IPageArchiveConfig, IPageLockConfig, IDuplicationConfig } from "src/types/menu-actions";
|
||||||
import { getMenuOptions } from "../utils/menu-options";
|
import { getMenuOptions } from "src/utils/menu-options";
|
||||||
|
|
||||||
interface IDocumentReadOnlyEditor {
|
interface IDocumentReadOnlyEditor {
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Editor } from "@tiptap/react";
|
import { Editor } from "@tiptap/react";
|
||||||
import { IMarking } from "..";
|
import { IMarking } from "src/types/editor-types";
|
||||||
|
|
||||||
function findNthH1(editor: Editor, n: number, level: number): number {
|
function findNthH1(editor: Editor, n: number, level: number): number {
|
||||||
let count = 0;
|
let count = 0;
|
@ -1,19 +1,9 @@
|
|||||||
import { Editor } from "@tiptap/react";
|
import { Editor } from "@tiptap/react";
|
||||||
import {
|
import { Archive, ArchiveRestoreIcon, ClipboardIcon, Copy, Link, Lock, Unlock } from "lucide-react";
|
||||||
Archive,
|
|
||||||
ArchiveIcon,
|
|
||||||
ArchiveRestoreIcon,
|
|
||||||
ClipboardIcon,
|
|
||||||
Copy,
|
|
||||||
Link,
|
|
||||||
Lock,
|
|
||||||
Unlock,
|
|
||||||
XCircle,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { NextRouter } from "next/router";
|
import { NextRouter } from "next/router";
|
||||||
import { IVerticalDropdownItemProps } from "../components/vertical-dropdown-menu";
|
import { IVerticalDropdownItemProps } from "src/ui/components/vertical-dropdown-menu";
|
||||||
import { IDuplicationConfig, IPageArchiveConfig, IPageLockConfig } from "../types/menu-actions";
|
import { IDuplicationConfig, IPageArchiveConfig, IPageLockConfig } from "src/types/menu-actions";
|
||||||
import { copyMarkdownToClipboard, CopyPageLink } from "./menu-actions";
|
import { copyMarkdownToClipboard, CopyPageLink } from "src/utils/menu-actions";
|
||||||
|
|
||||||
export interface MenuOptionsProps {
|
export interface MenuOptionsProps {
|
||||||
editor: Editor;
|
editor: Editor;
|
@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"extends": "tsconfig/react-library.json",
|
"extends": "tsconfig/react-library.json",
|
||||||
"include": ["src/**/*", "index.d.ts"],
|
"include": [
|
||||||
"exclude": ["dist", "build", "node_modules"]
|
"src/**/*",
|
||||||
|
"index.d.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"build",
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user